page.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. 'use client';
  2. import { useState, useEffect, useCallback } from 'react';
  3. import { Plus, FileText } from 'lucide-react';
  4. import { Button } from '@/components/ui/button';
  5. import { Card, CardContent } from '@/components/ui/card';
  6. import { getImports, deleteImport } from '@/app/actions/imports';
  7. import { CreateImportDialog } from '@/app/components/imports/CreateImportDialog';
  8. import { EditImportDialog } from '@/app/components/imports/EditImportDialog';
  9. import { ImportDetailDialog } from '@/app/components/imports/ImportDetailDialog';
  10. import { ImportsTable } from '@/app/components/imports/ImportsTable';
  11. import { useToast } from '@/hooks/use-toast';
  12. interface Import {
  13. id: number;
  14. name: string;
  15. importDate: string;
  16. layoutId: number;
  17. layout: {
  18. id: number;
  19. name: string;
  20. };
  21. }
  22. interface RawImportData {
  23. id: number;
  24. name: string;
  25. importDate: Date | string;
  26. layoutId: number;
  27. layout: {
  28. id: number;
  29. name: string;
  30. };
  31. }
  32. export default function ImportsPage() {
  33. const [imports, setImports] = useState<Import[]>([]);
  34. const [loading, setLoading] = useState(true);
  35. const [createDialogOpen, setCreateDialogOpen] = useState(false);
  36. const [editDialogOpen, setEditDialogOpen] = useState(false);
  37. const [detailDialogOpen, setDetailDialogOpen] = useState(false);
  38. const [selectedImport, setSelectedImport] = useState<Import | null>(null);
  39. const { toast } = useToast();
  40. const loadImports = useCallback(async () => {
  41. try {
  42. const result = await getImports();
  43. if (result.success && result.data) {
  44. // Transform the data to match our Import interface
  45. const transformedImports = result.data.map((item: RawImportData) => ({
  46. id: item.id,
  47. name: item.name,
  48. importDate: item.importDate instanceof Date
  49. ? item.importDate.toISOString()
  50. : String(item.importDate),
  51. layoutId: item.layoutId,
  52. layout: {
  53. id: item.layout.id,
  54. name: item.layout.name,
  55. },
  56. }));
  57. setImports(transformedImports);
  58. } else {
  59. toast({
  60. title: 'Error',
  61. description: result.error || 'Failed to load imports',
  62. variant: 'destructive',
  63. });
  64. setImports([]);
  65. }
  66. } catch {
  67. toast({
  68. title: 'Error',
  69. description: 'Failed to load imports',
  70. variant: 'destructive',
  71. });
  72. } finally {
  73. setLoading(false);
  74. }
  75. }, [toast]);
  76. useEffect(() => {
  77. loadImports();
  78. }, [loadImports]);
  79. async function handleDeleteImport(id: number) {
  80. if (!confirm('Are you sure you want to delete this import?')) return;
  81. try {
  82. const result = await deleteImport(id);
  83. if (result.success) {
  84. toast({
  85. title: 'Success',
  86. description: 'Import deleted successfully',
  87. });
  88. loadImports();
  89. } else {
  90. toast({
  91. title: 'Error',
  92. description: result.error || 'Failed to delete import',
  93. variant: 'destructive',
  94. });
  95. }
  96. } catch {
  97. toast({
  98. title: 'Error',
  99. description: 'Failed to delete import',
  100. variant: 'destructive',
  101. });
  102. }
  103. }
  104. function handleEditImport(importRecord: Import) {
  105. setSelectedImport(importRecord);
  106. setEditDialogOpen(true);
  107. }
  108. function handleViewImport(importRecord: Import) {
  109. setSelectedImport(importRecord);
  110. setDetailDialogOpen(true);
  111. }
  112. if (loading) {
  113. return (
  114. <div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
  115. <div className="container mx-auto px-4 py-8">
  116. <div className="flex justify-center">
  117. <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
  118. </div>
  119. </div>
  120. </div>
  121. );
  122. }
  123. return (
  124. <div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
  125. <div className="container mx-auto px-4 py-8">
  126. <div className="flex justify-between items-center mb-8">
  127. <div>
  128. <h1 className="text-3xl font-bold text-gray-900 dark:text-white">
  129. Import Management
  130. </h1>
  131. <p className="text-gray-600 dark:text-gray-300">
  132. Manage your data imports and configurations
  133. </p>
  134. </div>
  135. <Button onClick={() => setCreateDialogOpen(true)}>
  136. <Plus className="mr-2 h-4 w-4" />
  137. Create Import
  138. </Button>
  139. </div>
  140. {imports.length === 0 ? (
  141. <Card className="dark:bg-gray-800 dark:border-gray-700">
  142. <CardContent className="flex flex-col items-center justify-center py-12">
  143. <FileText className="h-12 w-12 text-muted-foreground mb-4" />
  144. <h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-white">No imports yet</h3>
  145. <p className="text-muted-foreground mb-4 dark:text-gray-300">
  146. Get started by creating your first import
  147. </p>
  148. <Button onClick={() => setCreateDialogOpen(true)}>
  149. <Plus className="mr-2 h-4 w-4" />
  150. Create Import
  151. </Button>
  152. </CardContent>
  153. </Card>
  154. ) : (
  155. <div className="w-full">
  156. <ImportsTable
  157. data={imports}
  158. onView={handleViewImport}
  159. onEdit={handleEditImport}
  160. onDelete={handleDeleteImport}
  161. />
  162. </div>
  163. )}
  164. <CreateImportDialog
  165. open={createDialogOpen}
  166. onOpenChange={setCreateDialogOpen}
  167. onSuccess={loadImports}
  168. />
  169. <EditImportDialog
  170. open={editDialogOpen}
  171. onOpenChange={setEditDialogOpen}
  172. importRecord={selectedImport}
  173. onSuccess={loadImports}
  174. />
  175. <ImportDetailDialog
  176. open={detailDialogOpen}
  177. onOpenChange={setDetailDialogOpen}
  178. importId={selectedImport?.id || 0}
  179. />
  180. </div>
  181. </div>
  182. );
  183. }