LayoutConfigurationsTable.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. const configurations = (result.data || []).map(config => ({
  44. ...config,
  45. createdAt: config.createdAt instanceof Date ? config.createdAt.toISOString() : config.createdAt,
  46. updatedAt: config.updatedAt instanceof Date ? config.updatedAt.toISOString() : config.updatedAt,
  47. }));
  48. setConfigurations(configurations);
  49. } else {
  50. toast({
  51. title: "Error",
  52. description: result.error,
  53. variant: "destructive",
  54. });
  55. }
  56. } catch {
  57. toast({
  58. title: "Error",
  59. description: "Failed to load configurations",
  60. variant: "destructive",
  61. });
  62. } finally {
  63. setLoading(false);
  64. }
  65. }
  66. async function handleDelete(id: number) {
  67. try {
  68. const result = await deleteLayoutConfiguration(id);
  69. if (result.success) {
  70. toast({
  71. title: "Success",
  72. description: "Configuration deleted successfully",
  73. });
  74. loadConfigurations();
  75. } else {
  76. toast({
  77. title: "Error",
  78. description: result.error,
  79. variant: "destructive",
  80. });
  81. }
  82. } catch {
  83. toast({
  84. title: "Error",
  85. description: "Failed to delete configuration",
  86. variant: "destructive",
  87. });
  88. }
  89. }
  90. if (loading) {
  91. return (
  92. <div className="p-4 space-y-4">
  93. {[...Array(3)].map((_, i) => (
  94. <Card key={i}>
  95. <CardHeader>
  96. <Skeleton className="h-6 w-48" />
  97. <Skeleton className="h-4 w-32" />
  98. </CardHeader>
  99. <CardContent>
  100. <Skeleton className="h-4 w-full" />
  101. </CardContent>
  102. </Card>
  103. ))}
  104. </div>
  105. );
  106. }
  107. if (configurations.length === 0) {
  108. return (
  109. <div className="p-8 text-center">
  110. <p className="text-muted-foreground">No layout configurations found</p>
  111. </div>
  112. );
  113. }
  114. return (
  115. <div className="p-4 space-y-4">
  116. {configurations.map((config) => (
  117. <Card key={config.id} className="hover:shadow-md transition-shadow">
  118. <CardHeader>
  119. <div className="flex justify-between items-start">
  120. <div>
  121. <CardTitle className="text-xl">{config.name}</CardTitle>
  122. <CardDescription>
  123. Created: {new Date(config.createdAt).toLocaleDateString()}
  124. </CardDescription>
  125. </div>
  126. <div className="flex gap-2">
  127. <Button
  128. variant="ghost"
  129. size="sm"
  130. onClick={() => router.push(`/layout-configurations/${config.id}`)}
  131. >
  132. <Eye className="h-4 w-4" />
  133. </Button>
  134. <Button
  135. variant="ghost"
  136. size="sm"
  137. onClick={() => router.push(`/layout-configurations/${config.id}/edit`)}
  138. >
  139. <Edit className="h-4 w-4" />
  140. </Button>
  141. <AlertDialog>
  142. <AlertDialogTrigger asChild>
  143. <Button variant="ghost" size="sm" className="text-destructive">
  144. <Trash2 className="h-4 w-4" />
  145. </Button>
  146. </AlertDialogTrigger>
  147. <AlertDialogContent>
  148. <AlertDialogHeader>
  149. <AlertDialogTitle>Delete Configuration</AlertDialogTitle>
  150. <AlertDialogDescription>
  151. Are you sure you want to delete {config.name}? This action cannot be undone.
  152. </AlertDialogDescription>
  153. </AlertDialogHeader>
  154. <AlertDialogFooter>
  155. <AlertDialogCancel>Cancel</AlertDialogCancel>
  156. <AlertDialogAction
  157. onClick={() => handleDelete(config.id)}
  158. className="bg-destructive text-destructive-foreground"
  159. >
  160. Delete
  161. </AlertDialogAction>
  162. </AlertDialogFooter>
  163. </AlertDialogContent>
  164. </AlertDialog>
  165. </div>
  166. </div>
  167. </CardHeader>
  168. <CardContent>
  169. <div className="flex items-center gap-4">
  170. <Badge variant="secondary">
  171. {config.sections.length} sections
  172. </Badge>
  173. <Badge variant="outline">
  174. {config.sections.reduce((total, section) => total + section.fields.length, 0)} fields
  175. </Badge>
  176. </div>
  177. </CardContent>
  178. </Card>
  179. ))}
  180. </div>
  181. );
  182. }