Compare commits

...

5 Commits

Author SHA1 Message Date
80cf569f8b fix: missing dependencies 2025-03-26 19:40:22 +01:00
cb14a0c7cd fix: merge conflicts 2025-03-26 19:37:08 +01:00
701b4f2d05 feat: add modal components 2025-03-26 19:21:48 +01:00
562469349f feat: add delete implementation on table 2025-03-26 17:53:38 +01:00
9b4d8cf02f feat: add stats on admin homme page 2025-03-26 15:42:58 +01:00
9 changed files with 181 additions and 82 deletions

View File

@ -9,7 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-dialog": "^1.1.6",
"@tanstack/react-query": "^5.69.0",
"@tanstack/react-table": "^8.21.2",
"axios": "^1.8.4",

15
pnpm-lock.yaml generated
View File

@ -8,9 +8,9 @@ importers:
.:
dependencies:
'@radix-ui/react-icons':
specifier: ^1.3.2
version: 1.3.2(react@19.0.0)
'@radix-ui/react-dialog':
specifier: ^1.1.6
version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@tanstack/react-query':
specifier: ^5.69.0
version: 5.69.0(react@19.0.0)
@ -1262,11 +1262,6 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-icons@1.3.2':
resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==}
peerDependencies:
react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc
'@radix-ui/react-id@1.1.0':
resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
peerDependencies:
@ -4938,10 +4933,6 @@ snapshots:
'@types/react': 19.0.12
'@types/react-dom': 19.0.4(@types/react@19.0.12)
'@radix-ui/react-icons@1.3.2(react@19.0.0)':
dependencies:
react: 19.0.0
'@radix-ui/react-id@1.1.0(@types/react@19.0.12)(react@19.0.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.12)(react@19.0.0)

View File

@ -9,12 +9,14 @@ import { ColumnDef } from "@tanstack/react-table"
import axios from "axios"
import { useSession } from "next-auth/react"
import Image from "next/image"
import { Modal } from "#/components/modal"
import { useState } from "react"
export default function HomePage () {
const {data: session, status} = useSession()
const queryClient = useQueryClient()
const [open, setOpen] = useState(false);
console.log("Session = ", session)
@ -91,7 +93,31 @@ export default function HomePage () {
},
{
accessorKey: "status",
header: "Statut"
header: "Statut",
cell: ({ cell }) => {
const status = String(cell.getValue())
return (
<p
className={`rounded-full px-2 py-1 font-medium text-sm w-20 h-6 text-center
${
status === "active" ? "bg-[#ECF9E8] text-[#49C91E]" :
status === "inactive" ? "bg-[#E7EBF3] text-[#9FA8BC]" :
status === "pending" ? "bg-[#EAF7FC] text-[#30B2EA]" :
status === "blocked" ? "bg-[#FDEBE8] text-[#F33F19]" :
""
}
`}
>
{
status === "active" ? "Actif" :
status === "inactive" ? "Inactif" :
status === "pending" ? "En attente" :
status === "blocked" ? "Bloquée" :
""
}
</p>
)
}
},
{
id: "delete",
@ -99,10 +125,43 @@ export default function HomePage () {
const id = String(cell.row.original.id)
return (
<div className="relative p-2 cursor-pointer"
onClick={() => { mutate(id) }}
// onClick={() => { mutate(id) }}
>
<Image alt="" src={icons.trash} className="absolute right-2 top-[-50%] transform translate-middle-y hover:text-blue-500" />
</div>
<Modal
open={open}
trigger={
<div onClick={() => setOpen(true)}>
<Image alt="" src={icons.trash} className="absolute right-2 top-[-50%] transform translate-middle-y hover:text-blue-500" />
</div>
}
title={
<p className="font-bold text-3xl">Supprimer une organisation</p>
}
content={
<div>
<p>Voulez-vous vraiment supprimer cette organisation ?</p>
<div className="grid grid-cols-2 gap-3 mt-3">
<button
className="bg-blue-100 text-blue-600 py-2 px-4 text-lg rounded-full text-center hover:bg-blue-200"
onClick={() => { setOpen(false) }}
>
Annuler
</button>
<button
className="bg-red-500 text-white py-2 px-4 text-lg rounded-full text-center hover:bg-red-600"
onClick={() => {
mutate(id)
setOpen(false)
}}
>
Supprimer
</button>
</div>
</div>
}
/>
</div>
)
}
}
@ -112,6 +171,8 @@ export default function HomePage () {
<div className="space-y-10">
<Statistics />
<p className="font-bold text-xl">Dernières organisations actives</p>
<Table
columns={columns}
data={companies || []}

View File

@ -1,5 +1,12 @@
@import "tailwindcss";
@font-face {
font-family: 'Urbanist'; /* Nom que vous donnerez à votre police */
src: url('../assets/fonts/Urbanist.ttf') format('truetype'); /* Chemin vers votre fichier */
font-weight: normal;
font-style: normal;
}
:root {
--foreground: #04060F;
--background: #ffffff;
@ -24,7 +31,7 @@
body {
background: var(--background);
color: var(--foreground);
font-family: Inter, sans-serif;
font-family: Urbanist, sans-serif;
}
.input-form {

View File

@ -25,7 +25,7 @@ export default function RootLayout({
<link rel="icon" href="/favicon.svg" />
<link rel="favicon.svg" href="/favicon.svg" />
</head>
<body className={inter.className}>
<body>
<AuthProvider>
<QueryClientProvide>
<NextTopLoader color="#246BFD" shadow="0" />

Binary file not shown.

View File

@ -1,62 +1,63 @@
import { GeneralHtmlAttr } from "#/lib/declarations";
// import { GeneralHtmlAttr } from "#/lib/declarations";
// import { SVGElementType } from "react";
const icons = {
CalendarMark: "Calendar Mark.svg",
Document: "Document.svg",
Ellipse2: "Ellipse 2.svg",
Group1: "Group (1).svg",
Group: "Group.svg",
Icons1: "Icons (1).svg",
Icons: "Icons.svg",
LineDuotone: "Line Duotone.svg",
MenuDots: "Menu Dots.svg",
NavItem: "NavItem.svg",
Path1: "Path (1).svg",
Path: "Path.svg",
Rectangle: "Rectangle.svg",
Search: "Search.svg",
Vector: "Vector.svg",
Add: "add.svg",
Archives: "archives.svg",
ArrowLeft: "arrowLeft.svg",
ArrowRight: "arrowRight.svg",
Buildings: "buildings.svg",
Checked: "checked.svg",
Cross: "cross.svg",
DocumentText: "document-text.svg",
Edit2: "edit-2.svg",
Element3White: "element-3-white.svg",
Element3: "element-3.svg",
EyeSlash: "eye-slash.svg",
Eye: "eye.svg",
IconAdd: "icon-add.svg",
Logo: "logo.svg",
LogoutRed: "logout-red.svg",
Logout: "logout.svg",
Maximize3: "maximize-3.svg",
Message: "message.svg",
Notifications: "notifications.svg",
PhFiles: "ph_files.svg",
PrimeFilePdf: "prime_file-pdf.svg",
PrimeFileWord: "prime_file-word.svg",
Profile2UserBlue: "profile-2user-blue.svg",
Profile2User: "profile-2user.svg",
Profile: "profile.svg",
Setting2: "setting-2.svg",
Setting3: "setting-3.svg",
Share: "share.svg",
Star: "star.svg",
Moon: "moon.svg",
Sun: "sun.svg",
};
// const icons: SVGElementType[] = {
// CalendarMark: "Calendar Mark.svg",
// Document: "Document.svg",
// Ellipse2: "Ellipse 2.svg",
// Group1: "Group (1).svg",
// Group: "Group.svg",
// Icons1: "Icons (1).svg",
// Icons: "Icons.svg",
// LineDuotone: "Line Duotone.svg",
// MenuDots: "Menu Dots.svg",
// NavItem: "NavItem.svg",
// Path1: "Path (1).svg",
// Path: "Path.svg",
// Rectangle: "Rectangle.svg",
// Search: "Search.svg",
// Vector: "Vector.svg",
// Add: "add.svg",
// Archives: "archives.svg",
// ArrowLeft: "arrowLeft.svg",
// ArrowRight: "arrowRight.svg",
// Buildings: "buildings.svg",
// Checked: "checked.svg",
// Cross: "cross.svg",
// DocumentText: "document-text.svg",
// Edit2: "edit-2.svg",
// Element3White: "element-3-white.svg",
// Element3: "element-3.svg",
// EyeSlash: "eye-slash.svg",
// Eye: "eye.svg",
// IconAdd: "icon-add.svg",
// Logo: "logo.svg",
// LogoutRed: "logout-red.svg",
// Logout: "logout.svg",
// Maximize3: "maximize-3.svg",
// Message: "message.svg",
// Notifications: "notifications.svg",
// PhFiles: "ph_files.svg",
// PrimeFilePdf: "prime_file-pdf.svg",
// PrimeFileWord: "prime_file-word.svg",
// Profile2UserBlue: "profile-2user-blue.svg",
// Profile2User: "profile-2user.svg",
// Profile: "profile.svg",
// Setting2: "setting-2.svg",
// Setting3: "setting-3.svg",
// Share: "share.svg",
// Star: "star.svg",
// Moon: "moon.svg",
// Sun: "sun.svg",
// };
export const Icon = ({ name, ...props }: { name: keyof typeof icons } & GeneralHtmlAttr) => {
const IconComponent = icons[name];
if (!IconComponent) return null;
// export const Icon = ({ name, ...props }: { name: keyof typeof icons } & GeneralHtmlAttr) => {
// const IconComponent = icons[name];
// if (!IconComponent) return null;
return (
<div {...props}>
<img src={`/icons/${IconComponent}`} alt={name} />
</div>
);
};
// return (
// <div {...props}>
// {}
// </div>
// );
// };

39
src/components/modal.tsx Normal file
View File

@ -0,0 +1,39 @@
import * as Dialog from "@radix-ui/react-dialog";
import { ReactNode, useState } from "react";
export function Modal({
trigger,
title,
content,
open
}: {
trigger: ReactNode;
title: string | ReactNode;
content: ReactNode;
open?: boolean;
}) {
const [toggle, setToggle] = useState(open);
return (
<Dialog.Root open={toggle}>
<Dialog.Trigger asChild>
{trigger}
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/25 z-40" />
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md bg-white rounded-lg shadow-xl z-50 p-6">
<Dialog.Title className="text-xl font-bold text-center my-4">
{title}
</Dialog.Title>
{content}
{/* <div className="absolute top-4 right-4 text-gray-500 hover:text-gray-700 cursor-pointer"
onClick={() => {setToggle(false)}}
>
Fermer
</div> */}
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@ -64,8 +64,8 @@ export default function Table<TData, TValue>({
return(
<div>
<div className="rounded-lg border border-gray-200">
<table className="w-full overflow-x-auto rounded-lg " style={{ borderTopLeftRadius: '10px', borderTopRightRadius: '10px', }}>
<div className="rounded-lg border border-gray-200 w-auto">
<table className="w-full overflow-x-auto rounded-lg">
<thead className="h-10">
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id} className="rounded-lg">
@ -94,7 +94,7 @@ export default function Table<TData, TValue>({
<tbody>
{table.getRowModel().rows.length ? (
table.getRowModel().rows.map((row) => (
<tr key={row.id} className={clsx('hover:bg-gray-300 border-t border-gray-200', { 'bg-gray-300': row.getIsSelected()})}>
<tr key={row.id} className={clsx('hover:bg-gray-100 border-t border-gray-200', { 'bg-gray-300': row.getIsSelected()})}>
<td className="p-3 text-start">
<input
checked={row.getIsSelected()}