From 8dcc99d1f8a805e1e52ff9f1ebee071fc258cb60 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Sun, 10 Mar 2024 12:39:10 +0100 Subject: [PATCH] N-FIN-5: add entity page UI --- src/app/entities/columns.tsx | 41 ++++ src/app/entities/page.tsx | 32 +++- src/components/EntityPageClientComponents.tsx | 178 ++++++++++++++++++ 3 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 src/app/entities/columns.tsx create mode 100644 src/components/EntityPageClientComponents.tsx diff --git a/src/app/entities/columns.tsx b/src/app/entities/columns.tsx new file mode 100644 index 0000000..17ac7d7 --- /dev/null +++ b/src/app/entities/columns.tsx @@ -0,0 +1,41 @@ +'use client'; + +import { ColumnDef } from '@tanstack/react-table'; +import { Entity } from '@prisma/client'; +import { CellContext, ColumnDefTemplate } from '@tanstack/table-core'; + +export const columns = ( + actionCell: ColumnDefTemplate>, +) => { + + return [ + { + accessorKey: 'name', + header: 'Name', + }, + { + accessorKey: 'type', + header: 'Type', + }, + { + accessorKey: 'createdAt', + header: 'Created at', + cell: ({row}) => { + const date = row.getValue('createdAt') as Date; + return date.toDateString(); + }, + }, + { + accessorKey: 'updatedAt', + header: 'Updated at', + cell: ({row}) => { + const date = row.getValue('updatedAt') as Date; + return date.toDateString(); + }, + }, + { + id: 'actions', + cell: actionCell, + }, + ] as ColumnDef[]; +}; diff --git a/src/app/entities/page.tsx b/src/app/entities/page.tsx index 532b2d3..216eeeb 100644 --- a/src/app/entities/page.tsx +++ b/src/app/entities/page.tsx @@ -1,7 +1,33 @@ +import { prismaClient } from '@/prisma'; +import { getUser } from '@/auth'; +import React from 'react'; +import EntityPageClientContent from '@/components/EntityPageClientComponents'; +import entityCreateUpdate from '@/lib/actions/entityCreateUpdate'; +import entityDelete from '@/lib/actions/entityDelete'; + export default async function EntitiesPage() { + + const user = await getUser(); + + const entities = await prismaClient.entity.findMany({ + where: { + userId: user?.id, + }, + orderBy: [ + { + type: 'desc', + }, + { + id: 'asc', + }, + ], + }); + return ( -
- Entities -
+ ); } diff --git a/src/components/EntityPageClientComponents.tsx b/src/components/EntityPageClientComponents.tsx new file mode 100644 index 0000000..eb0f1b7 --- /dev/null +++ b/src/components/EntityPageClientComponents.tsx @@ -0,0 +1,178 @@ +'use client'; + +import { Entity } from '@prisma/client'; +import React, { useState } from 'react'; +import { CellContext } from '@tanstack/table-core'; +import { Button } from '@/components/ui/button'; +import { Edit, Trash } from 'lucide-react'; +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import EntityForm from '@/components/form/entityForm'; +import { DataTable } from '@/components/ui/data-table'; +import { columns } from '@/app/entities/columns'; +import { z } from 'zod'; +import { entityFormSchema } from '@/lib/form-schemas/entityFormSchema'; +import { ActionResponse } from '@/lib/types/ActionResponse'; +import { Input } from '@/components/ui/input'; +import { useRouter } from 'next/navigation'; +import { toast } from 'sonner'; +import { sonnerContent } from '@/components/ui/sonner'; + +export default function EntityPageClientContent({entities, onSubmit, onDelete, className}: { + entities: Entity[], + onSubmit: (data: z.infer) => Promise, + onDelete: (id: number) => Promise, + className: string, +}) { + + const router = useRouter(); + + const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + + const [selectedEntity, setSelectedEntity] = useState(undefined); + + async function handleSubmit(data: z.infer) { + const response = await onSubmit(data); + router.refresh(); + setIsEditDialogOpen(false); + return response; + } + + async function handleDelete(id: number | undefined) { + + if (!id) { + return; + } + + const response = await onDelete(id); + toast(sonnerContent(response)); + if (response.redirect) { + router.push(response.redirect); + } + router.refresh(); + setIsDeleteDialogOpen(false); + return response; + } + + function filterEntities(entities: Entity[], filter: string) { + const filterChars = filter.toLowerCase().split(''); + const filterCharCounts: Record = {}; + + // Count character occurrences in the filter + filterChars.forEach(char => { + filterCharCounts[char] = (filterCharCounts[char] || 0) + 1; + }); + + return entities.filter(entity => { + const entityChars = entity.name.toLowerCase().split(''); + const entityCharCounts: Record = {}; + + // Check if entity has enough of each character + for (const char of entityChars) { + entityCharCounts[char] = (entityCharCounts[char] || 0) + 1; + } + + // Ensure all filter characters were found + return Object.keys(filterCharCounts).every(char => { + return entityCharCounts[char] >= filterCharCounts[char]; + }); + }); + } + + const actionCell = ({row}: CellContext) => { + const entity = row.original as Entity; + + return ( +
+ + +
+ ); + }; + + const [filter, setFilter] = useState(''); + + return ( +
+
+

Entities

+ + {/* Edit dialog */} + + + + + + + {selectedEntity?.id ? 'Update Entity' : 'Create Entity'} + + + + +
+ + {/* Filter input */} + setFilter(event.target.value)} + placeholder="Filter entities"/> + + {/* Data Table */} + + + {/* Delete confirmation dialog */} + + + Delete Entity? +

Are your sure you want to delete the entity {selectedEntity?.name}?

+ + + + +
+
+
+ ); +}