Files
road-monitoring-ui/src/app/(modules)/project/page.tsx

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>
</>
);
}