173 lines
4.7 KiB
TypeScript
173 lines
4.7 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useState } from 'react';
|
|
import { Layers, Plus } from 'lucide-react';
|
|
|
|
import { PageHeader } from '@/components/page-header';
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from '@/components/ui/alert-dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import type { Project } from '@/types';
|
|
|
|
import { ProjectDialog } from './components/ProjectDialog';
|
|
import { ProjectTable } from './components/ProjectTable';
|
|
import { useProjectColumns } from './components/ProjectColumns';
|
|
import { useProjectFilters } from './hooks/useProjectFilters';
|
|
import { useProjectForm } from './hooks/useProjectForm';
|
|
import {
|
|
useDeleteProjectMutation,
|
|
useProjectsQuery,
|
|
} from './hooks/useProjectQueries';
|
|
|
|
export default function ProjectPage() {
|
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
const [projectToDelete, setProjectToDelete] = useState<Project | null>(null);
|
|
const { skip, setSkip, limit, setLimit } = useProjectFilters();
|
|
const projectsQuery = useProjectsQuery({ skip, limit });
|
|
const deleteProjectMutation = useDeleteProjectMutation();
|
|
const projectForm = useProjectForm({
|
|
onSaved: () => setIsDialogOpen(false),
|
|
});
|
|
const {
|
|
register,
|
|
onSubmit,
|
|
openCreate: prepareCreateProject,
|
|
openEdit: prepareEditProject,
|
|
resetForm,
|
|
projectId,
|
|
errors,
|
|
isSubmitted,
|
|
canSubmit,
|
|
isSaving,
|
|
} = projectForm;
|
|
|
|
const openCreate = useCallback(() => {
|
|
prepareCreateProject();
|
|
setIsDialogOpen(true);
|
|
}, [prepareCreateProject]);
|
|
|
|
const openEdit = useCallback(
|
|
(project: Project) => {
|
|
prepareEditProject(project);
|
|
setIsDialogOpen(true);
|
|
},
|
|
[prepareEditProject],
|
|
);
|
|
|
|
const handleDialogOpenChange = useCallback(
|
|
(open: boolean) => {
|
|
setIsDialogOpen(open);
|
|
if (!open) {
|
|
resetForm();
|
|
}
|
|
},
|
|
[resetForm],
|
|
);
|
|
|
|
const deleteProject = useCallback(
|
|
(project: Project) => {
|
|
setProjectToDelete(project);
|
|
},
|
|
[],
|
|
);
|
|
|
|
const handleDeleteDialogOpenChange = useCallback((open: boolean) => {
|
|
if (!open) {
|
|
setProjectToDelete(null);
|
|
}
|
|
}, []);
|
|
|
|
const confirmDeleteProject = useCallback(() => {
|
|
if (!projectToDelete) return;
|
|
|
|
deleteProjectMutation.mutate(projectToDelete, {
|
|
onSettled: () => setProjectToDelete(null),
|
|
});
|
|
}, [deleteProjectMutation, projectToDelete]);
|
|
|
|
const columns = useProjectColumns();
|
|
const projects = projectsQuery.data?.items ?? [];
|
|
const total = projectsQuery.data?.totalItems ?? 0;
|
|
|
|
return (
|
|
<>
|
|
<main className="relative z-10 space-y-5">
|
|
<PageHeader
|
|
title="Project Management"
|
|
description="Manage road infrastructure projects"
|
|
icon={Layers}
|
|
actions={
|
|
<Button onClick={openCreate} size="sm">
|
|
<Plus />
|
|
Add Project
|
|
</Button>
|
|
}
|
|
/>
|
|
|
|
<ProjectTable
|
|
columns={columns}
|
|
projects={projects}
|
|
isLoading={projectsQuery.isLoading || deleteProjectMutation.isPending}
|
|
skip={skip}
|
|
limit={limit}
|
|
total={total}
|
|
onPageChange={setSkip}
|
|
onLimitChange={setLimit}
|
|
onEdit={openEdit}
|
|
onDelete={deleteProject}
|
|
/>
|
|
</main>
|
|
|
|
<ProjectDialog
|
|
open={isDialogOpen}
|
|
onOpenChange={handleDialogOpenChange}
|
|
projectId={projectId}
|
|
register={register}
|
|
errors={errors}
|
|
isSubmitted={isSubmitted}
|
|
onSubmit={onSubmit}
|
|
canSubmit={canSubmit}
|
|
isSaving={isSaving}
|
|
/>
|
|
|
|
<AlertDialog
|
|
open={Boolean(projectToDelete)}
|
|
onOpenChange={handleDeleteDialogOpenChange}
|
|
>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Delete project?</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
This will permanently delete{' '}
|
|
<span className="font-medium text-foreground">
|
|
{projectToDelete?.name}
|
|
</span>
|
|
. This action cannot be undone.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel disabled={deleteProjectMutation.isPending}>
|
|
Cancel
|
|
</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
variant="destructive"
|
|
disabled={deleteProjectMutation.isPending}
|
|
onClick={confirmDeleteProject}
|
|
>
|
|
Delete
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</>
|
|
);
|
|
}
|