LayoutConfigurationsTable.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. "use client";
  2. import { useEffect, useState } from "react";
  3. import { useRouter } from "next/navigation";
  4. import { getLayoutConfigurations, deleteLayoutConfiguration } from "@/app/actions/layout-configurations";
  5. import { Button } from "@/components/ui/button";
  6. import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
  7. import { Badge } from "@/components/ui/badge";
  8. import { Skeleton } from "@/components/ui/skeleton";
  9. import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
  10. import { toast } from "@/hooks/use-toast";
  11. import { Trash2, Eye, Edit } from "lucide-react";
  12. interface LayoutConfiguration {
  13. id: number;
  14. name: string;
  15. sections: LayoutSection[];
  16. createdAt: string;
  17. updatedAt: string;
  18. }
  19. interface LayoutSection {
  20. id: number;
  21. name: string;
  22. type: string;
  23. sheetName: string;
  24. fields: LayoutSectionField[];
  25. }
  26. interface LayoutSectionField {
  27. id: number;
  28. name: string;
  29. dataType: string;
  30. cellPosition: string;
  31. }
  32. export function LayoutConfigurationsTable() {
  33. const [configurations, setConfigurations] = useState<LayoutConfiguration[]>([]);
  34. const [loading, setLoading] = useState(true);
  35. const router = useRouter();
  36. useEffect(() => {
  37. loadConfigurations();
  38. }, []);
  39. async function loadConfigurations() {
  40. try {
  41. const result = await getLayoutConfigurations();
  42. if (result.success) {
  43. setConfigurations(result.data || []);
  44. } else {
  45. toast({
  46. title: "Error",
  47. description: result.error,
  48. variant: "destructive",
  49. });
  50. }
  51. } catch (error) {
  52. toast({
  53. title: "Error",
  54. description: "Failed to load configurations",
  55. variant: "destructive",
  56. });
  57. } finally {
  58. setLoading(false);
  59. }
  60. }
  61. async function handleDelete(id: number) {
  62. try {
  63. const result = await deleteLayoutConfiguration(id);
  64. if (result.success) {
  65. toast({
  66. title: "Success",
  67. description: "Configuration deleted successfully",
  68. });
  69. loadConfigurations();
  70. } else {
  71. toast({
  72. title: "Error",
  73. description: result.error,
  74. variant: "destructive",
  75. });
  76. }
  77. } catch (error) {
  78. toast({
  79. title: "Error",
  80. description: "Failed to delete configuration",
  81. variant: "destructive",
  82. });
  83. }
  84. }
  85. if (loading) {
  86. return (
  87. <div className="p-4 space-y-4">
  88. {[...Array(3)].map((_, i) => (
  89. <Card key={i}>
  90. <CardHeader>
  91. <Skeleton className="h-6 w-48" />
  92. <Skeleton className="h-4 w-32" />
  93. </CardHeader>
  94. <CardContent>
  95. <Skeleton className="h-4 w-full" />
  96. </CardContent>
  97. </Card>
  98. ))}
  99. </div>
  100. );
  101. }
  102. if (configurations.length === 0) {
  103. return (
  104. <div className="p-8 text-center">
  105. <p className="text-muted-foreground">No layout configurations found</p>
  106. </div>
  107. );
  108. }
  109. return (
  110. <div className="p-4 space-y-4">
  111. {configurations.map((config) => (
  112. <Card key={config.id} className="hover:shadow-md transition-shadow">
  113. <CardHeader>
  114. <div className="flex justify-between items-start">
  115. <div>
  116. <CardTitle className="text-xl">{config.name}</CardTitle>
  117. <CardDescription>
  118. Created: {new Date(config.createdAt).toLocaleDateString()}
  119. </CardDescription>
  120. </div>
  121. <div className="flex gap-2">
  122. <Button
  123. variant="ghost"
  124. size="sm"
  125. onClick={() => router.push(`/layout-configurations/${config.id}`)}
  126. >
  127. <Eye className="h-4 w-4" />
  128. </Button>
  129. <Button
  130. variant="ghost"
  131. size="sm"
  132. onClick={() => router.push(`/layout-configurations/${config.id}/edit`)}
  133. >
  134. <Edit className="h-4 w-4" />
  135. </Button>
  136. <AlertDialog>
  137. <AlertDialogTrigger asChild>
  138. <Button variant="ghost" size="sm" className="text-destructive">
  139. <Trash2 className="h-4 w-4" />
  140. </Button>
  141. </AlertDialogTrigger>
  142. <AlertDialogContent>
  143. <AlertDialogHeader>
  144. <AlertDialogTitle>Delete Configuration</AlertDialogTitle>
  145. <AlertDialogDescription>
  146. Are you sure you want to delete "{config.name}"? This action cannot be undone.
  147. </AlertDialogDescription>
  148. </AlertDialogHeader>
  149. <AlertDialogFooter>
  150. <AlertDialogCancel>Cancel</AlertDialogCancel>
  151. <AlertDialogAction
  152. onClick={() => handleDelete(config.id)}
  153. className="bg-destructive text-destructive-foreground"
  154. >
  155. Delete
  156. </AlertDialogAction>
  157. </AlertDialogFooter>
  158. </AlertDialogContent>
  159. </AlertDialog>
  160. </div>
  161. </div>
  162. </CardHeader>
  163. <CardContent>
  164. <div className="flex items-center gap-4">
  165. <Badge variant="secondary">
  166. {config.sections.length} sections
  167. </Badge>
  168. <Badge variant="outline">
  169. {config.sections.reduce((total, section) => total + section.fields.length, 0)} fields
  170. </Badge>
  171. </div>
  172. </CardContent>
  173. </Card>
  174. ))}
  175. </div>
  176. );
  177. }