refactor: redesign table module
This commit is contained in:
@@ -23,6 +23,8 @@ import {
|
||||
} 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 PackagePage() {
|
||||
@@ -108,7 +110,9 @@ export default function PackagePage() {
|
||||
region: region.trim() || null,
|
||||
}
|
||||
await updatePackage(currentPackage.id, data)
|
||||
toast.success("Package updated successfully!")
|
||||
toast.success("Package Updated", {
|
||||
description: `${name} has been updated successfully at ${new Date().toLocaleTimeString()}`,
|
||||
})
|
||||
} else {
|
||||
const data: PackageCreate = {
|
||||
project_id: selectedProjectId,
|
||||
@@ -116,7 +120,9 @@ export default function PackagePage() {
|
||||
region: region.trim() || null,
|
||||
}
|
||||
await createPackage(data)
|
||||
toast.success("Package created successfully!")
|
||||
toast.success("Package Created", {
|
||||
description: `${name} has been established successfully at ${new Date().toLocaleTimeString()}`,
|
||||
})
|
||||
}
|
||||
|
||||
// Refresh packages list
|
||||
@@ -126,7 +132,11 @@ export default function PackagePage() {
|
||||
setIsModalOpen(false)
|
||||
resetForm()
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : `Failed to ${isEditing ? 'update' : 'create'} package`)
|
||||
const message = err instanceof Error ? err.message : `Failed to ${isEditing ? 'update' : 'create'} package`
|
||||
setError(message)
|
||||
toast.error("Operation Failed", {
|
||||
description: message,
|
||||
})
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
@@ -147,10 +157,15 @@ export default function PackagePage() {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
await deletePackage(pkg.id)
|
||||
toast.success("Package deleted successfully!")
|
||||
toast.success("Package Deleted", {
|
||||
description: `${pkg.name} has been removed from the system.`,
|
||||
})
|
||||
await loadPackages()
|
||||
} catch (err) {
|
||||
setError("Failed to delete package")
|
||||
toast.error("Deletion Failed", {
|
||||
description: "The package could not be removed. Please try again.",
|
||||
})
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
@@ -180,16 +195,38 @@ export default function PackagePage() {
|
||||
const pkg = row.original
|
||||
const project = projects.find(p => p.id === pkg.project_id)
|
||||
if (!project?.state) return <span className="text-gray-400">—</span>
|
||||
|
||||
const states = project.state.split(',').map(s => s.trim()).filter(Boolean)
|
||||
if (states.length === 0) return <span className="text-gray-400">—</span>
|
||||
|
||||
const firstState = states[0]
|
||||
const remainingStates = states.slice(1)
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{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>
|
||||
)
|
||||
}
|
||||
@@ -201,27 +238,26 @@ export default function PackagePage() {
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen text-gray-900 dark:text-gray-100">
|
||||
<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="Package Management"
|
||||
description="Manage project packages"
|
||||
icon={Package}
|
||||
actions={
|
||||
<Button
|
||||
onClick={() => { setIsEditing(false); setIsModalOpen(true); }}
|
||||
>
|
||||
<Package className="mr-2 h-4 w-4" />
|
||||
Add New Package
|
||||
</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>
|
||||
@@ -229,10 +265,8 @@ export default function PackagePage() {
|
||||
title="Packages"
|
||||
data={packages}
|
||||
columns={columns}
|
||||
onAddNew={() => { setIsEditing(false); setIsModalOpen(true); }}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
addButtonText="Add New Package"
|
||||
isLoading={isLoading}
|
||||
pagination={{
|
||||
skip,
|
||||
@@ -247,8 +281,7 @@ export default function PackagePage() {
|
||||
</div>
|
||||
|
||||
<PoweredBy />
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
|
||||
{/* Modal Dialog */}
|
||||
<Dialog open={isModalOpen} onOpenChange={(open) => { if (!open) { setIsModalOpen(false); resetForm(); } else { setIsModalOpen(true); } }}>
|
||||
@@ -269,15 +302,6 @@ export default function PackagePage() {
|
||||
</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-8">
|
||||
{/* Step 1: Select Project */}
|
||||
@@ -371,7 +395,7 @@ export default function PackagePage() {
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting || !name.trim() || !selectedProjectId}
|
||||
className="flex-1 h-12 font-bold uppercase tracking-wider text-xs shadow-lg shadow-primary/20"
|
||||
className="flex-1 h-12 font-bold uppercase tracking-wider text-xs"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -389,6 +413,6 @@ export default function PackagePage() {
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user