refactor: redesign table module

This commit is contained in:
2026-03-18 01:03:58 +05:30
parent caf527f584
commit 344f37719e
24 changed files with 991 additions and 815 deletions

View File

@@ -12,6 +12,8 @@ import { PageHeader } from "@/components/page-header"
import { createProject, fetchProjects, updateProject, deleteProject, type ProjectCreate, type Project, type ProjectUpdate } from "@/lib/api"
import { ColumnDef } from "@tanstack/react-table"
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { PoweredBy } from "@/components/powered-by"
export default function ProjectPage() {
@@ -91,7 +93,9 @@ export default function ProjectPage() {
end_lng: endLng ? parseFloat(endLng) : null,
}
await updateProject(currentProject.id, data)
toast.success("Project updated successfully!")
toast.success("Project Updated", {
description: `${name} has been updated successfully at ${new Date().toLocaleTimeString()}`,
})
} else {
const data: ProjectCreate = {
name: name.trim(),
@@ -103,7 +107,9 @@ export default function ProjectPage() {
end_lng: endLng ? parseFloat(endLng) : null,
}
await createProject(data)
toast.success("Project created successfully!")
toast.success("Project Created", {
description: `${name} has been established successfully at ${new Date().toLocaleTimeString()}`,
})
}
// Refresh projects list
@@ -113,7 +119,11 @@ export default function ProjectPage() {
setIsModalOpen(false)
resetForm()
} catch (err) {
setError(err instanceof Error ? err.message : `Failed to ${isEditing ? 'update' : 'create'} project`)
const message = err instanceof Error ? err.message : `Failed to ${isEditing ? 'update' : 'create'} project`
setError(message)
toast.error("Operation Failed", {
description: message,
})
} finally {
setIsSubmitting(false)
}
@@ -138,10 +148,15 @@ export default function ProjectPage() {
try {
setIsLoading(true)
await deleteProject(project.id)
toast.success("Project deleted successfully!")
toast.success("Project Deleted", {
description: `${project.name} has been removed from the system.`,
})
await loadProjects()
} catch (err) {
setError("Failed to delete project")
toast.error("Deletion Failed", {
description: "The project could not be removed. Please try again or check your permissions.",
})
} finally {
setIsLoading(false)
}
@@ -152,7 +167,7 @@ export default function ProjectPage() {
accessorKey: "name",
header: "Project Name",
cell: ({ row }) => (
<div className="font-semibold text-gray-900 dark:text-gray-100">{row.original.name}</div>
<div className="font-semibold">{row.original.name}</div>
)
},
{
@@ -160,17 +175,45 @@ export default function ProjectPage() {
header: "State",
cell: ({ row }) => {
const project = row.original
if (!project.state) return <span className="text-gray-400"></span>
if (!project.state) return <span></span>
const states = project.state.split(',').map(s => s.trim()).filter(Boolean)
if (states.length === 0) return <span></span>
const firstState = states[0]
const remainingStates = states.slice(1)
return (
<div className="flex flex-wrap gap-1.5">
{project.state.split(',').map((item, idx) => (
<span
key={idx}
className="px-2 py-0.5 rounded-full bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 font-semibold border border-blue-100 dark:border-blue-800"
>
{item.trim()}
</span>
))}
<div className="flex items-center gap-1.5">
<Badge
variant="secondary"
>
{firstState}
</Badge>
{remainingStates.length > 0 && (
<Popover>
<PopoverTrigger asChild>
<button className="flex items-center justify-center rounded-full bg-muted/50 hover:bg-muted px-1.5 py-0.5 text-[10px] font-bold text-muted-foreground transition-colors border border-border/40">
+{remainingStates.length}
</button>
</PopoverTrigger>
<PopoverContent className="w-auto p-2" align="start">
<div className="flex flex-col gap-1.5">
<p className="text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-0.5 px-1">Other States</p>
{remainingStates.map((item, idx) => (
<Badge
key={idx}
variant="secondary"
>
{item}
</Badge>
))}
</div>
</PopoverContent>
</Popover>
)}
</div>
)
}
@@ -182,27 +225,26 @@ export default function ProjectPage() {
]
return (
<div className="min-h-screen">
<main className="min-h-screen relative overflow-hidden">
<div className="mx-auto px-6 py-8 max-w-340 relative z-10">
<>
<main className="relative z-10">
{/* Refined Header */}
<div className="mb-8">
<PageHeader
title="Project Management"
description="Manage road infrastructure projects"
icon={FolderPlus}
actions={
<Button
onClick={() => { setIsEditing(false); setIsModalOpen(true); }}
>
<FolderPlus className="mr-2 h-5 w-5" />
Add New Project
</Button>
}
/>
</div>
{/* Error Message */}
{error && !isModalOpen && (
<div className="mb-6 flex items-center gap-3 p-4 rounded-xl bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-800">
<div className="w-5 h-5 rounded-full bg-red-100 dark:bg-red-900 flex items-center justify-center shrink-0">
<span className="text-xs font-bold text-red-500">!</span>
</div>
<p className="text-sm text-red-600 dark:text-red-400 break-all">{error}</p>
</div>
)}
{/* Data Table */}
<div>
@@ -210,10 +252,8 @@ export default function ProjectPage() {
title="Projects"
data={projects}
columns={columns}
onAddNew={() => { setIsEditing(false); setIsModalOpen(true); }}
onEdit={handleEdit}
onDelete={handleDelete}
addButtonText="Add New Project"
isLoading={isLoading}
pagination={{
skip,
@@ -228,8 +268,8 @@ export default function ProjectPage() {
</div>
<PoweredBy />
</div>
</main>
</main>
{/* Modal Dialog */}
<Dialog open={isModalOpen} onOpenChange={(open) => { if (!open) { setIsModalOpen(false); resetForm(); } else { setIsModalOpen(true); } }}>
@@ -250,15 +290,6 @@ export default function ProjectPage() {
</DialogHeader>
{/* Error Message in Modal */}
{error && (
<div className="flex items-center gap-3 p-4 rounded-xl bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-800">
<div className="w-5 h-5 rounded-full bg-red-100 dark:bg-red-900 flex items-center justify-center shrink-0">
<span className="text-xs font-bold text-red-500">!</span>
</div>
<p className="text-sm text-red-600 dark:text-red-400 break-all">{error}</p>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
{/* Project Name */}
@@ -409,6 +440,6 @@ export default function ProjectPage() {
</form>
</DialogContent>
</Dialog>
</div>
</>
)
}