Selaa lähdekoodia

feat(layout-configurations): add CRUD operations for layout section fields

- Add server actions for managing layout section fields (create, read, update, delete)
- Implement AddFieldDialog component for creating new fields
- Implement EditFieldDialog component for updating existing fields
- Implement DeleteFieldDialog component for field removal
- Create new page for managing section fields at /layout-configurations/sections/[id]/fields
- Add navigation from LayoutSectionCard to fields management page
- Include field management UI with cards, badges, and action buttons
vtugulan 6 kuukautta sitten
vanhempi
sitoutus
203fbd0b85

+ 100 - 0
app/actions/layout-configurations.ts

@@ -88,6 +88,106 @@ export async function getLayoutSectionFields(sectionId: number) {
   }
 }
 
+// Get single layout section
+export async function getSection(id: number) {
+  try {
+    const section = await prisma.layoutSection.findUnique({
+      where: { id },
+      include: {
+        fields: {
+          orderBy: {
+            importColumnOrderNumber: "asc",
+          },
+        },
+      },
+    });
+    
+    if (!section) {
+      return { success: false, error: "Section not found" };
+    }
+    
+    return { success: true, data: section };
+  } catch (error) {
+    console.error("Error fetching section:", error);
+    return { success: false, error: "Failed to fetch section" };
+  }
+}
+
+// Create new layout section field
+export async function createLayoutSectionField(
+  sectionId: number,
+  fieldData: {
+    name: string;
+    dataType: string;
+    dataTypeFormat?: string;
+    cellPosition: string;
+    importTableColumnName: string;
+    importColumnOrderNumber: number;
+  }
+) {
+  try {
+    const field = await prisma.layoutSectionField.create({
+      data: {
+        layoutSectionId: sectionId,
+        name: fieldData.name,
+        dataType: fieldData.dataType,
+        dataTypeFormat: fieldData.dataTypeFormat || null,
+        cellPosition: fieldData.cellPosition,
+        importTableColumnName: fieldData.importTableColumnName,
+        importColumnOrderNumber: fieldData.importColumnOrderNumber,
+      },
+    });
+    return { success: true, data: field };
+  } catch (error) {
+    console.error("Error creating layout section field:", error);
+    return { success: false, error: "Failed to create layout section field" };
+  }
+}
+
+// Update layout section field
+export async function updateLayoutSectionField(
+  id: number,
+  fieldData: {
+    name?: string;
+    dataType?: string;
+    dataTypeFormat?: string;
+    cellPosition?: string;
+    importTableColumnName?: string;
+    importColumnOrderNumber?: number;
+  }
+) {
+  try {
+    const field = await prisma.layoutSectionField.update({
+      where: { id },
+      data: {
+        ...(fieldData.name && { name: fieldData.name }),
+        ...(fieldData.dataType && { dataType: fieldData.dataType }),
+        ...(fieldData.dataTypeFormat !== undefined && { dataTypeFormat: fieldData.dataTypeFormat }),
+        ...(fieldData.cellPosition && { cellPosition: fieldData.cellPosition }),
+        ...(fieldData.importTableColumnName && { importTableColumnName: fieldData.importTableColumnName }),
+        ...(fieldData.importColumnOrderNumber !== undefined && { importColumnOrderNumber: fieldData.importColumnOrderNumber }),
+      },
+    });
+    return { success: true, data: field };
+  } catch (error) {
+    console.error("Error updating layout section field:", error);
+    return { success: false, error: "Failed to update layout section field" };
+  }
+}
+
+// Delete layout section field
+export async function deleteLayoutSectionField(id: number) {
+  try {
+    await prisma.layoutSectionField.delete({
+      where: { id },
+    });
+    return { success: true };
+  } catch (error) {
+    console.error("Error deleting layout section field:", error);
+    return { success: false, error: "Failed to delete layout section field" };
+  }
+}
+
 // Create new layout configuration
 export async function createLayoutConfiguration(name: string) {
   try {

+ 170 - 0
app/components/layout-configurations/AddFieldDialog.tsx

@@ -0,0 +1,170 @@
+"use client";
+
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select";
+import { createLayoutSectionField } from "@/app/actions/layout-configurations";
+
+interface AddFieldDialogProps {
+  sectionId: number;
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  onSuccess: () => void;
+}
+
+export function AddFieldDialog({
+  sectionId,
+  open,
+  onOpenChange,
+  onSuccess,
+}: AddFieldDialogProps) {
+  const [loading, setLoading] = useState(false);
+  const [formData, setFormData] = useState({
+    name: "",
+    dataType: "STRING",
+    dataTypeFormat: "",
+    cellPosition: "",
+    importTableColumnName: "",
+    importColumnOrderNumber: 0,
+  });
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    setLoading(true);
+
+    try {
+      const result = await createLayoutSectionField(sectionId, {
+        ...formData,
+        importColumnOrderNumber: parseInt(formData.importColumnOrderNumber.toString()),
+      });
+
+      if (result.success) {
+        onSuccess();
+        setFormData({
+          name: "",
+          dataType: "STRING",
+          dataTypeFormat: "",
+          cellPosition: "",
+          importTableColumnName: "",
+          importColumnOrderNumber: 0,
+        });
+      }
+    } catch (error) {
+      console.error("Error adding field:", error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent className="sm:max-w-[425px]">
+        <DialogHeader>
+          <DialogTitle>Add New Field</DialogTitle>
+          <DialogDescription>
+            Add a new field to this section configuration.
+          </DialogDescription>
+        </DialogHeader>
+        <form onSubmit={handleSubmit}>
+          <div className="grid gap-4 py-4">
+            <div className="grid gap-2">
+              <Label htmlFor="name">Field Name</Label>
+              <Input
+                id="name"
+                value={formData.name}
+                onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+                required
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="dataType">Data Type</Label>
+              <Select
+                value={formData.dataType}
+                onValueChange={(value) => setFormData({ ...formData, dataType: value })}
+              >
+                <SelectTrigger>
+                  <SelectValue />
+                </SelectTrigger>
+                <SelectContent>
+                  <SelectItem value="STRING">String</SelectItem>
+                  <SelectItem value="NUMBER">Number</SelectItem>
+                  <SelectItem value="DATE">Date</SelectItem>
+                  <SelectItem value="BOOLEAN">Boolean</SelectItem>
+                  <SelectItem value="EMAIL">Email</SelectItem>
+                </SelectContent>
+              </Select>
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="dataTypeFormat">Data Type Format</Label>
+              <Input
+                id="dataTypeFormat"
+                value={formData.dataTypeFormat}
+                onChange={(e) => setFormData({ ...formData, dataTypeFormat: e.target.value })}
+                placeholder="e.g., YYYY-MM-DD"
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="cellPosition">Cell Position</Label>
+              <Input
+                id="cellPosition"
+                value={formData.cellPosition}
+                onChange={(e) => setFormData({ ...formData, cellPosition: e.target.value })}
+                required
+                placeholder="e.g., A1"
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="importTableColumnName">Import Column Name</Label>
+              <Input
+                id="importTableColumnName"
+                value={formData.importTableColumnName}
+                onChange={(e) => setFormData({ ...formData, importTableColumnName: e.target.value })}
+                required
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="importColumnOrderNumber">Column Order</Label>
+              <Input
+                id="importColumnOrderNumber"
+                type="number"
+                value={formData.importColumnOrderNumber}
+                onChange={(e) => setFormData({ ...formData, importColumnOrderNumber: parseInt(e.target.value) || 0 })}
+                required
+                min={0}
+              />
+            </div>
+          </div>
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
+              Cancel
+            </Button>
+            <Button type="submit" disabled={loading}>
+              {loading ? "Adding..." : "Add Field"}
+            </Button>
+          </DialogFooter>
+        </form>
+      </DialogContent>
+    </Dialog>
+  );
+}

+ 77 - 0
app/components/layout-configurations/DeleteFieldDialog.tsx

@@ -0,0 +1,77 @@
+"use client";
+
+import { useState } from "react";
+import { Button } from "@/components/ui/button";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import { deleteLayoutSectionField } from "@/app/actions/layout-configurations";
+
+interface LayoutSectionField {
+  id: number;
+  name: string;
+  dataType: string;
+  cellPosition: string;
+}
+
+interface DeleteFieldDialogProps {
+  field: LayoutSectionField | null;
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  onSuccess: () => void;
+}
+
+export function DeleteFieldDialog({
+  field,
+  open,
+  onOpenChange,
+  onSuccess,
+}: DeleteFieldDialogProps) {
+  const [loading, setLoading] = useState(false);
+
+  const handleDelete = async () => {
+    if (!field) return;
+
+    setLoading(true);
+
+    try {
+      const result = await deleteLayoutSectionField(field.id);
+
+      if (result.success) {
+        onSuccess();
+      }
+    } catch (error) {
+      console.error("Error deleting field:", error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  if (!field) return null;
+
+  return (
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent>
+        <DialogHeader>
+          <DialogTitle>Delete Field</DialogTitle>
+          <DialogDescription>
+            Are you sure you want to delete the field &quot;{field.name}&quot;? This action cannot be undone.
+          </DialogDescription>
+        </DialogHeader>
+        <DialogFooter>
+          <Button variant="outline" onClick={() => onOpenChange(false)}>
+            Cancel
+          </Button>
+          <Button variant="destructive" onClick={handleDelete} disabled={loading}>
+            {loading ? "Deleting..." : "Delete Field"}
+          </Button>
+        </DialogFooter>
+      </DialogContent>
+    </Dialog>
+  );
+}

+ 201 - 0
app/components/layout-configurations/EditFieldDialog.tsx

@@ -0,0 +1,201 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Button } from "@/components/ui/button";
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select";
+import { updateLayoutSectionField } from "@/app/actions/layout-configurations";
+
+interface LayoutSectionField {
+  id: number;
+  name: string;
+  dataType: string;
+  dataTypeFormat: string | null;
+  cellPosition: string;
+  importTableColumnName: string;
+  importColumnOrderNumber: number;
+}
+
+interface EditFieldDialogProps {
+  field: LayoutSectionField | null;
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+  onSuccess: () => void;
+}
+
+export function EditFieldDialog({
+  field,
+  open,
+  onOpenChange,
+  onSuccess,
+}: EditFieldDialogProps) {
+  const [loading, setLoading] = useState(false);
+  const [formData, setFormData] = useState({
+    name: "",
+    dataType: "STRING",
+    dataTypeFormat: "",
+    cellPosition: "",
+    importTableColumnName: "",
+    importColumnOrderNumber: 0,
+  });
+
+  useEffect(() => {
+    if (field) {
+      setFormData({
+        name: field.name,
+        dataType: field.dataType,
+        dataTypeFormat: field.dataTypeFormat || "",
+        cellPosition: field.cellPosition,
+        importTableColumnName: field.importTableColumnName,
+        importColumnOrderNumber: field.importColumnOrderNumber,
+      });
+    }
+  }, [field]);
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    if (!field) return;
+
+    setLoading(true);
+
+    try {
+      const result = await updateLayoutSectionField(field.id, {
+        name: formData.name,
+        dataType: formData.dataType,
+        dataTypeFormat: formData.dataTypeFormat || undefined,
+        cellPosition: formData.cellPosition,
+        importTableColumnName: formData.importTableColumnName,
+        importColumnOrderNumber: parseInt(formData.importColumnOrderNumber.toString()),
+      });
+
+      if (result.success) {
+        onSuccess();
+        setFormData({
+          name: "",
+          dataType: "STRING",
+          dataTypeFormat: "",
+          cellPosition: "",
+          importTableColumnName: "",
+          importColumnOrderNumber: 0,
+        });
+      }
+    } catch (error) {
+      console.error("Error updating field:", error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  if (!field) return null;
+
+  return (
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent className="sm:max-w-[425px]">
+        <DialogHeader>
+          <DialogTitle>Edit Field</DialogTitle>
+          <DialogDescription>
+            Update the details for this field.
+          </DialogDescription>
+        </DialogHeader>
+        <form onSubmit={handleSubmit}>
+          <div className="grid gap-4 py-4">
+            <div className="grid gap-2">
+              <Label htmlFor="edit-name">Field Name</Label>
+              <Input
+                id="edit-name"
+                value={formData.name}
+                onChange={(e) => setFormData({ ...formData, name: e.target.value })}
+                required
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="edit-dataType">Data Type</Label>
+              <Select
+                value={formData.dataType}
+                onValueChange={(value) => setFormData({ ...formData, dataType: value })}
+              >
+                <SelectTrigger>
+                  <SelectValue />
+                </SelectTrigger>
+                <SelectContent>
+                  <SelectItem value="STRING">String</SelectItem>
+                  <SelectItem value="NUMBER">Number</SelectItem>
+                  <SelectItem value="DATE">Date</SelectItem>
+                  <SelectItem value="BOOLEAN">Boolean</SelectItem>
+                  <SelectItem value="EMAIL">Email</SelectItem>
+                </SelectContent>
+              </Select>
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="edit-dataTypeFormat">Data Type Format</Label>
+              <Input
+                id="edit-dataTypeFormat"
+                value={formData.dataTypeFormat}
+                onChange={(e) => setFormData({ ...formData, dataTypeFormat: e.target.value })}
+                placeholder="e.g., YYYY-MM-DD"
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="edit-cellPosition">Cell Position</Label>
+              <Input
+                id="edit-cellPosition"
+                value={formData.cellPosition}
+                onChange={(e) => setFormData({ ...formData, cellPosition: e.target.value })}
+                required
+                placeholder="e.g., A1"
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="edit-importTableColumnName">Import Column Name</Label>
+              <Input
+                id="edit-importTableColumnName"
+                value={formData.importTableColumnName}
+                onChange={(e) => setFormData({ ...formData, importTableColumnName: e.target.value })}
+                required
+              />
+            </div>
+
+            <div className="grid gap-2">
+              <Label htmlFor="edit-importColumnOrderNumber">Column Order</Label>
+              <Input
+                id="edit-importColumnOrderNumber"
+                type="number"
+                value={formData.importColumnOrderNumber}
+                onChange={(e) => setFormData({ ...formData, importColumnOrderNumber: parseInt(e.target.value) || 0 })}
+                required
+                min={0}
+              />
+            </div>
+          </div>
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
+              Cancel
+            </Button>
+            <Button type="submit" disabled={loading}>
+              {loading ? "Updating..." : "Update Field"}
+            </Button>
+          </DialogFooter>
+        </form>
+      </DialogContent>
+    </Dialog>
+  );
+}

+ 4 - 1
app/components/layout-configurations/LayoutSectionCard.tsx

@@ -42,7 +42,10 @@ export function LayoutSectionCard({ section }: LayoutSectionCardProps) {
   };
 
   return (
-    <Card>
+    <Card
+      className="cursor-pointer hover:shadow-lg transition-shadow"
+      onClick={() => window.location.href = `/layout-configurations/sections/${section.id}/fields`}
+    >
       <CardHeader>
         <div className="flex justify-between items-start">
           <div>

+ 245 - 0
app/layout-configurations/sections/[id]/fields/page.tsx

@@ -0,0 +1,245 @@
+"use client";
+
+import { useState, useEffect, useCallback } from "react";
+import { useParams, useRouter } from "next/navigation";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Plus, ArrowLeft, Edit, Trash2 } from "lucide-react";
+import { Skeleton } from "@/components/ui/skeleton";
+import { AddFieldDialog } from "@/app/components/layout-configurations/AddFieldDialog";
+import { EditFieldDialog } from "@/app/components/layout-configurations/EditFieldDialog";
+import { DeleteFieldDialog } from "@/app/components/layout-configurations/DeleteFieldDialog";
+import { getLayoutSectionFields, getSection } from "@/app/actions/layout-configurations";
+
+interface LayoutSectionField {
+  id: number;
+  name: string;
+  dataType: string;
+  dataTypeFormat: string | null;
+  cellPosition: string;
+  importTableColumnName: string;
+  importColumnOrderNumber: number;
+}
+
+interface LayoutSection {
+  id: number;
+  name: string;
+  type: string;
+  sheetName: string;
+  tableName: string;
+}
+
+export default function SectionFieldsPage() {
+  const params = useParams();
+  const router = useRouter();
+  const sectionId = parseInt(params.id as string);
+  
+  const [section, setSection] = useState<LayoutSection | null>(null);
+  const [fields, setFields] = useState<LayoutSectionField[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [addDialogOpen, setAddDialogOpen] = useState(false);
+  const [editDialogOpen, setEditDialogOpen] = useState(false);
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+  const [selectedField, setSelectedField] = useState<LayoutSectionField | null>(null);
+
+  const loadSectionData = useCallback(async () => {
+    try {
+      setLoading(true);
+      const [sectionData, fieldsData] = await Promise.all([
+        getSection(sectionId),
+        getLayoutSectionFields(sectionId)
+      ]);
+       
+      if (sectionData.success && sectionData.data) {
+        setSection(sectionData.data);
+      } else {
+        setSection(null);
+      }
+       
+      if (fieldsData.success && fieldsData.data) {
+        setFields(fieldsData.data);
+      } else {
+        setFields([]);
+      }
+    } catch (error) {
+      console.error("Error loading section data:", error);
+    } finally {
+      setLoading(false);
+    }
+  }, [sectionId]);
+
+  useEffect(() => {
+    loadSectionData();
+  }, [sectionId, loadSectionData]);
+
+  const handleFieldAdded = () => {
+    loadSectionData();
+    setAddDialogOpen(false);
+  };
+
+  const handleFieldUpdated = () => {
+    loadSectionData();
+    setEditDialogOpen(false);
+  };
+
+  const handleFieldDeleted = () => {
+    loadSectionData();
+    setDeleteDialogOpen(false);
+  };
+
+  const openEditDialog = (field: LayoutSectionField) => {
+    setSelectedField(field);
+    setEditDialogOpen(true);
+  };
+
+  const openDeleteDialog = (field: LayoutSectionField) => {
+    setSelectedField(field);
+    setDeleteDialogOpen(true);
+  };
+
+  if (loading) {
+    return (
+      <div className="container mx-auto py-6 space-y-6">
+        <Skeleton className="h-8 w-64" />
+        <Skeleton className="h-32 w-full" />
+        <Skeleton className="h-64 w-full" />
+      </div>
+    );
+  }
+
+  if (!section) {
+    return (
+      <div className="container mx-auto py-6">
+        <Card>
+          <CardContent className="pt-6">
+            <p className="text-center text-muted-foreground">Section not found</p>
+          </CardContent>
+        </Card>
+      </div>
+    );
+  }
+
+  return (
+    <div className="container mx-auto py-6 space-y-6">
+      <div className="flex items-center gap-4">
+        <Button
+          variant="ghost"
+          size="sm"
+          onClick={() => router.back()}
+        >
+          <ArrowLeft className="h-4 w-4 mr-2" />
+          Back
+        </Button>
+      </div>
+
+      <Card>
+        <CardHeader>
+          <div className="flex justify-between items-start">
+            <div>
+              <CardTitle>Section: {section.name}</CardTitle>
+              <CardDescription>
+                Manage fields for this section in the layout configuration
+              </CardDescription>
+            </div>
+            <div className="flex gap-2">
+              <Badge variant="secondary">{fields.length} fields</Badge>
+              <Badge variant="outline">{section.type}</Badge>
+            </div>
+          </div>
+        </CardHeader>
+        <CardContent>
+          <div className="grid gap-4">
+            <div>
+              <span className="font-medium">Sheet:</span> {section.sheetName}
+            </div>
+            <div>
+              <span className="font-medium">Table:</span> {section.tableName}
+            </div>
+          </div>
+        </CardContent>
+      </Card>
+
+      <div className="space-y-4">
+        <div className="flex justify-between items-center">
+          <h2 className="text-2xl font-semibold">Section Fields</h2>
+          <Button size="sm" onClick={() => setAddDialogOpen(true)}>
+            <Plus className="h-4 w-4 mr-2" />
+            Add Field
+          </Button>
+        </div>
+
+        {fields.length === 0 ? (
+          <Card>
+            <CardContent className="pt-6">
+              <p className="text-center text-muted-foreground">
+                No fields found for this section
+              </p>
+            </CardContent>
+          </Card>
+        ) : (
+          <div className="grid gap-4">
+            {fields.map((field) => (
+              <Card key={field.id} className="hover:shadow-md transition-shadow">
+                <CardContent className="pt-6">
+                  <div className="flex justify-between items-start">
+                    <div className="space-y-2">
+                      <div>
+                        <span className="font-medium">{field.name}</span>
+                        <Badge variant="outline" className="ml-2 text-xs">
+                          {field.dataType}
+                        </Badge>
+                      </div>
+                      <div className="text-sm text-muted-foreground">
+                        <div>Position: {field.cellPosition}</div>
+                        <div>Column: {field.importTableColumnName}</div>
+                        <div>Order: {field.importColumnOrderNumber}</div>
+                      </div>
+                    </div>
+                    <div className="flex gap-2">
+                      <Button
+                        variant="ghost"
+                        size="sm"
+                        onClick={() => openEditDialog(field)}
+                      >
+                        <Edit className="h-4 w-4" />
+                      </Button>
+                      <Button
+                        variant="ghost"
+                        size="sm"
+                        onClick={() => openDeleteDialog(field)}
+                      >
+                        <Trash2 className="h-4 w-4" />
+                      </Button>
+                    </div>
+                  </div>
+                </CardContent>
+              </Card>
+            ))}
+          </div>
+        )}
+      </div>
+
+      <AddFieldDialog
+        sectionId={sectionId}
+        open={addDialogOpen}
+        onOpenChange={setAddDialogOpen}
+        onSuccess={handleFieldAdded}
+      />
+
+      <EditFieldDialog
+        field={selectedField}
+        open={editDialogOpen}
+        onOpenChange={setEditDialogOpen}
+        onSuccess={handleFieldUpdated}
+      />
+
+      <DeleteFieldDialog
+        field={selectedField}
+        open={deleteDialogOpen}
+        onOpenChange={setDeleteDialogOpen}
+        onSuccess={handleFieldDeleted}
+      />
+    </div>
+  );
+}