Compare commits

...

3 Commits

Author SHA1 Message Date
ee127f431c
Add @tanstack/react-table dependency
Included @tanstack/react-table version ^8.20.5 in package.json to enhance table handling capabilities. Updated pnpm-lock.yaml to reflect the new dependencies and their respective versions.
2024-10-21 14:51:58 +02:00
13c77bfc32
Add files table and refactor sub-page components
Introduced a files table component for managing file data in the UI. Refactored sub-page components into a separate module for better code organization and maintainability. Adjusted text and links for consistency with language and configuration standards.
2024-10-21 14:51:49 +02:00
2d6815efb6
Update HTTP status code and import statements
Changed HTTP status code from FOUND to OK in files.controller.ts for better clarity. Added ApiResponse and IMachinesTable imports to machines.controller.ts to enhance API documentation and type checking.
2024-10-21 14:51:13 +02:00
12 changed files with 327 additions and 57 deletions

View File

@ -203,7 +203,7 @@ export class FilesController {
return await this.filesService.removeFileType(typeId);
}
@HttpCode(HttpStatus.FOUND)
@HttpCode(HttpStatus.OK)
@Get(":fileId")
async getFile(@Param("fileId") fileId: string) {
return await this.filesService.get(fileId);

View File

@ -19,7 +19,8 @@ import {
TypeDto,
} from "apps/backend/src/app/machines/machines.dto";
import { MachinesService } from "apps/backend/src/app/machines/machines.service";
import { ApiTags } from '@nestjs/swagger';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
import { IMachinesTable } from 'apps/backend/src/app/db/schema';
@ApiTags('Machines')
@Controller("machines")

View File

@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

View File

@ -15,7 +15,7 @@ export default function RootLayout({
}) {
return (
<html lang="en">
<body className={"h-screen w-screen bg-card flex flex-col justify-between items-center police-ubuntu dark"}>
<body className={"h-screen w-screen bg-card flex flex-col justify-between items-center police-ubuntu"}>
<Header/>
{children}
<Footer/>

View File

@ -1,21 +1,11 @@
"use client"
import { Dispatch, SetStateAction, useState } from 'react';
import { Button } from '../components/ui/button';
import { Home, NotepadTextDashed } from 'lucide-react';
import { NewFileModal } from 'apps/frontend/src/components/new-file-modal';
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup
} from 'apps/frontend/src/components/ui/resizable';
import {
SubHomePage
} from 'apps/frontend/src/components/sub-pages/sub-home-page';
export enum ESubPage {
Home,
Documentation,
}
import { ESubPage, SubPage, SubPageSelector } from '../components/sub-pages';
export default function HomePage() {
const [currentSubPage, setCurrentSubPage] = useState<ESubPage>(0)
@ -43,43 +33,4 @@ export default function HomePage() {
);
}
interface SubPageSelectorProps {
currentSubPage: ESubPage
setCurrentSubPage: Dispatch<SetStateAction<ESubPage>>
}
function SubPageSelector(props: SubPageSelectorProps) {
return (
<div className={"w-full flex flex-col justify-center items-stretch pt-4 p-4 gap-2"}>
<Button
onClick={()=>props.setCurrentSubPage(ESubPage.Home)}
disabled={props.currentSubPage === ESubPage.Home}
className={"gap-1 font-bold"}>
<Home/>
Accueil
</Button>
<NewFileModal/>
<Button
onClick={()=>props.setCurrentSubPage(ESubPage.Documentation)}
disabled={props.currentSubPage === ESubPage.Documentation}>
<NotepadTextDashed />
Documentation
</Button>
</div>
)
}
export interface ISubPageProps {
currentSubPage: ESubPage
}
function SubPage(props: ISubPageProps) {
switch (props.currentSubPage) {
case ESubPage.Home:
return (<SubHomePage/>)
case ESubPage.Documentation:
return (<>Doc</>)
default:
return (<>Default</>)
}
}

View File

@ -23,7 +23,7 @@ export function NewFileModal(props: NewFileModalProps) {
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogTitle>Ajout d'un fichier</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.

View File

@ -0,0 +1,56 @@
import { NewFileModal } from "../new-file-modal";
import { Button } from "../ui/button"
import { Home, NotepadTextDashed } from 'lucide-react';
import {
SubHomePage
} from './sub-home-page';
import { Dispatch, SetStateAction } from 'react';
import {
SubDocPage
} from 'apps/frontend/src/components/sub-pages/sub-doc-page';
export interface SubPageSelectorProps {
currentSubPage: ESubPage
setCurrentSubPage: Dispatch<SetStateAction<ESubPage>>
}
export enum ESubPage {
Home,
Documentation,
}
export function SubPageSelector(props: SubPageSelectorProps) {
return (
<div className={"w-full flex flex-col justify-center items-stretch pt-4 p-4 gap-2"}>
<Button
onClick={()=>props.setCurrentSubPage(ESubPage.Home)}
disabled={props.currentSubPage === ESubPage.Home}
className={"gap-1 font-bold"}>
<Home/>
Accueil
</Button>
<NewFileModal/>
<Button
onClick={()=>props.setCurrentSubPage(ESubPage.Documentation)}
disabled={props.currentSubPage === ESubPage.Documentation}>
<NotepadTextDashed />
Documentation
</Button>
</div>
)
}
export interface ISubPageProps {
currentSubPage: ESubPage
}
export function SubPage(props: ISubPageProps) {
switch (props.currentSubPage) {
case ESubPage.Home:
return (<SubHomePage/>)
case ESubPage.Documentation:
return (<SubDocPage/>)
default:
return (<>Default</>)
}
}

View File

@ -0,0 +1,14 @@
import { useState } from 'react';
export interface SubHomePageProps {
}
export function SubDocPage(props: SubHomePageProps) {
const [isLoaded, setIsLoaded] = useState<boolean>(false);
return (<section className={"w-full h-full rounded bg-card flex flex-col"}>
<h1 className={"text-2xl m-2 font-bold"}>Documentations</h1>
</section>)
}

View File

@ -1,4 +1,8 @@
import { useState } from 'react';
import { HomeIcon } from 'lucide-react';
import {
FilesDataTable, filesTableColumns
} from 'apps/frontend/src/components/tables/files-table';
export interface SubHomePageProps {
@ -8,7 +12,27 @@ export interface SubHomePageProps {
export function SubHomePage(props: SubHomePageProps) {
const [isLoaded, setIsLoaded] = useState<boolean>(false);
return (<section className={"w-full h-full rounded bg-card"}>
return (<section className={"w-full h-full rounded bg-card flex flex-col"}>
<div className={"flex flex-row justify-start items-center gap-2 m-2"}>
<HomeIcon
className={"w-8 h-8 text-secondary"}
/>
<h1 className={"text-2xl font-bold"}>Page principal</h1>
</div>
<div className={"m-1 flex flex-col justify-start items-center w-5/6 self-center h-full"}>
<FilesDataTable columns={filesTableColumns} data={[{
"uuid": "bbc17f8c-244d-4a44-8faf-c5e1ec0786bf",
"fileName": "test",
"checksum": "60d6473dc75edd2e885cc32c098f0379a5dd2d8175de0df1ef7526636b2a03f5",
"extension": "jpeg",
"groupId": null,
"fileSize": 483636,
"fileType": "2c1fb8eb-59b1-4bef-b50d-6bc854f46105",
"isRestricted": false,
"isDocumentation": false,
"uploadedAt": "2024-10-21T11:40:36.350Z",
"uploadedBy": "Avnyr"
}]}/>
</div>
</section>)
}

View File

@ -0,0 +1,201 @@
"use client"
import {
ColumnDef,
flexRender,
getCoreRowModel, getPaginationRowModel, getSortedRowModel, SortingState,
useReactTable
} from '@tanstack/react-table';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../ui/table"
import { Button } from '../ui/button';
import { Badge } from '../ui/badge'
import { ArrowUpDown, Clock, Download, Trash } from 'lucide-react';
import { useState } from 'react';
import Link from 'next/link';
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type IFile = {
uuid: string;
fileName: string;
checksum: string;
extension: string;
groupId: string | null;
fileSize: number;
fileType: string;
isRestricted: boolean;
isDocumentation: boolean;
uploadedAt: string;
uploadedBy: string;
}
function ContextButtonForFile() {
return (<div className={"scale-75"}>
<Button variant={"destructive"}><Trash/></Button>
</div>)
}
export const filesTableColumns: ColumnDef<IFile>[] = [
{
accessorKey: "fileName",
header: ({ column }) => {
return (<div className={"flex justify-center items-center"}>
Nom du fichier
</div>)
},
},
{
accessorKey: "uploadedBy",
header: ({ column }) => {
return (<div className={"flex justify-center items-center"}>
Autheur(s)
</div>)
},
},
{
accessorKey: "uploadedAt",
header: ({ column }) => {
return (
<Button
variant="ghost"
className={"flex w-full"}
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Ajouté le
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => {
const date = new Date(row.getValue("uploadedAt"))
const formatted = `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()} à ${date.getHours()}:${date.getMinutes()}`
return (<div className={"flex justify-center items-center"}>
<Badge
variant="outline"
className={"gap-1 flex w-fit items-center"}>
<Clock className={"w-4 h-4"} />
<p className={"font-light"}>
{formatted}
</p>
</Badge>
</div>)
},
},
{
accessorKey: "extension",
header: ({ column }) => {
return (<div className={"flex justify-center items-center"}>
Extension du fichier
</div>)
},
cell: ({ row }) => {
const extension = row.getValue("extension") as string;
return (<div className={"flex justify-center items-center"}>
<code className={"bg-gray-300 p-1 px-2 rounded-full"}>{extension}</code>
</div>)
},
},
{
id: "actions",
header: ({ column }) => {
return (<div className={"flex justify-center items-center"}>
Actions
</div>)
},
cell: ({ row }) => {
const file = row.original
return (<div className={"flex gap"}>
<Button variant={"ghost"} asChild>
<Link
href={`http://localhost:3333/api/files/${file.uuid}`}
>
<Download />
Télécharger
</Link>
</Button>
<ContextButtonForFile/>
</div>)
},
},
]
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
export function FilesDataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([])
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
state: {
sorting,
},
})
return (
<div className="rounded-md border w-full">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
Auccun résultat
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}

View File

@ -45,6 +45,7 @@
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3",
"@tanstack/react-table": "^8.20.5",
"argon2": "^0.41.1",
"axios": "^1.7.7",
"class-transformer": "^0.5.1",

22
pnpm-lock.yaml generated
View File

@ -110,6 +110,9 @@ importers:
'@radix-ui/react-tooltip':
specifier: ^1.1.3
version: 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-table':
specifier: ^8.20.5
version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
argon2:
specifier: ^0.41.1
version: 0.41.1
@ -3193,6 +3196,17 @@ packages:
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
engines: {node: '>=10'}
'@tanstack/react-table@8.20.5':
resolution: {integrity: sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==}
engines: {node: '>=12'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
'@tanstack/table-core@8.20.5':
resolution: {integrity: sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==}
engines: {node: '>=12'}
'@tokenizer/token@0.3.0':
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
@ -12641,6 +12655,14 @@ snapshots:
dependencies:
defer-to-connect: 2.0.1
'@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/table-core': 8.20.5
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@tanstack/table-core@8.20.5': {}
'@tokenizer/token@0.3.0': {}
'@tootallnate/once@2.0.0': {}