From 6e4fd4e53daf49aac6ac739c0026f9e1e51ec2ac Mon Sep 17 00:00:00 2001 From: BBUSHRA Date: Sat, 17 May 2025 13:39:10 +0000 Subject: [PATCH] fonctionnalites des notifications --- .../components/notifications.table.tsx | 102 ++++++++++++++++++ src/app/board/notifications/page.tsx | 80 ++++++++++++++ src/config/navigation-items.tsx | 9 +- src/config/routes.ts | 4 +- src/hooks/queries/use-notification.query.ts | 23 ++++ src/server/services/notification.service.ts | 41 +++++++ src/types/attendance.types.ts | 28 +++-- src/types/notification.types.ts | 16 +++ 8 files changed, 291 insertions(+), 12 deletions(-) create mode 100644 src/app/board/notifications/components/notifications.table.tsx create mode 100644 src/app/board/notifications/page.tsx create mode 100644 src/hooks/queries/use-notification.query.ts create mode 100644 src/server/services/notification.service.ts create mode 100644 src/types/notification.types.ts diff --git a/src/app/board/notifications/components/notifications.table.tsx b/src/app/board/notifications/components/notifications.table.tsx new file mode 100644 index 0000000..b0ce62c --- /dev/null +++ b/src/app/board/notifications/components/notifications.table.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Notification } from "@/types/notification.types"; +import { formatDate } from "@/lib/utils"; + +interface NotificationsTableProps { + notifications: Notification[]; + isLoading: boolean; +} + +export function NotificationsTable({ notifications, isLoading }: NotificationsTableProps) { + if (isLoading) { + return ( +
+

Chargement ...

+
+ ); + } + + if (notifications.length === 0) { + return ( +
+

Aucune notification trouvée.

+
+ ); + } + + return ( +
+ + + + Message + Status + Cours + Date + Destinataire + + + + {notifications.map((notification) => ( + + {notification.message} + + + + + + + + {notification.emargement.classSession.course.name} + + +
+

Date: {formatDate(notification.emargement.classSession.date)}

+

Début: {notification.emargement.classSession.heureDebut}

+

Fin: {notification.emargement.classSession.heureFin}

+
+
+
+
+
+ {formatDate(notification.createdAt)} + {notification.recipient.name} +
+ ))} +
+
+
+ ); +} + +function StatusBadge({ status }: { status: Notification["status"] }) { + const variant = { + SENT: "default", + READ: "success", + DELETED: "destructive", + }[status]; + + const label = { + SENT: "Envoyé", + READ: "Lu", + DELETED: "Supprimé", + }[status]; + + return {label}; +} diff --git a/src/app/board/notifications/page.tsx b/src/app/board/notifications/page.tsx new file mode 100644 index 0000000..48afcdd --- /dev/null +++ b/src/app/board/notifications/page.tsx @@ -0,0 +1,80 @@ +"use client"; + +import { AppSidebar } from "@/components/shared/navigation/app.sidebar"; +import UserDropdown from "@/components/shared/navigation/user.dropdown"; +import FeedbackDialog from "@/components/shared/others/feedback.dialog"; +import { ModeToggle } from "@/components/shared/theme/mode-toggle"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; +import { Separator } from "@/components/ui/separator"; +import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; +import { useNotificationsQuery } from "@/hooks/queries/use-notification.query"; +import { RiBellLine, RiScanLine } from "@remixicon/react"; +import { NotificationsTable } from "./components/notifications.table"; +import { PiBellDuotone, PiBuildingsDuotone } from "react-icons/pi"; + + + + +export default function NotificationsPage() { + const { data: notifications = [], isLoading } = useNotificationsQuery(); + + return ( + + + +
+
+ + + + + + + + + + + Notifications + + + +
+
+ + + +
+
+
+ {/* Page intro */} +
+
+

+ + Notifications +

+

Gérez vos notifications liées aux émargements de cours.

+
+
+ + {/* Table */} +
+ + +
+
+
+ + +
+ ); +} diff --git a/src/config/navigation-items.tsx b/src/config/navigation-items.tsx index ded1f18..8286614 100644 --- a/src/config/navigation-items.tsx +++ b/src/config/navigation-items.tsx @@ -9,6 +9,7 @@ import { PiGearDuotone, PiHouseDuotone, PiUserDuotone, + PiBellDuotone, } from "react-icons/pi"; import { routes } from "./routes"; @@ -76,12 +77,16 @@ export const mainSections: NavMenuGroup[] = [ title: "Programmes", url: routes.board.programs, icon: PiBookmarkDuotone, - }, - { + }, { title: "Cours", // Nouvel élément pour les cours url: routes.board.courses, icon: PiBookOpenDuotone, }, + { + title: "Notifications", + url: routes.board.notifications, + icon: PiBellDuotone, + }, { title: "Utilisateurs", url: routes.board.users, diff --git a/src/config/routes.ts b/src/config/routes.ts index db57d46..fca2de3 100644 --- a/src/config/routes.ts +++ b/src/config/routes.ts @@ -9,12 +9,12 @@ export const routes = { home: "/board", users: "/board/users", profile: "/board/profile", - settings: "/board/settings", - academicYears: "/board/academic-years", + settings: "/board/settings", academicYears: "/board/academic-years", organizations: "/board/organizations", attendance: "/board/attendance", programs: "/board/programs", universities: "/board/universities", courses: "/board/courses", + notifications: "/board/notifications", }, }; diff --git a/src/hooks/queries/use-notification.query.ts b/src/hooks/queries/use-notification.query.ts new file mode 100644 index 0000000..f05be91 --- /dev/null +++ b/src/hooks/queries/use-notification.query.ts @@ -0,0 +1,23 @@ +import { NotificationService } from "@/server/services/notification.service"; +import { Notification } from "@/types/notification.types"; +import { useQuery } from "@tanstack/react-query"; + +export const notificationQueryKeys = { + notifications: ["notifications"], + notification: (id: string) => ["notification", id], +}; + +export function useNotificationsQuery() { + return useQuery({ + queryKey: notificationQueryKeys.notifications, + queryFn: () => NotificationService.getNotifications(), + }); +} + +export function useNotificationQuery(id: string) { + return useQuery({ + queryKey: notificationQueryKeys.notification(id), + queryFn: () => NotificationService.getNotificationById(id), + enabled: !!id, + }); +} diff --git a/src/server/services/notification.service.ts b/src/server/services/notification.service.ts new file mode 100644 index 0000000..d11e892 --- /dev/null +++ b/src/server/services/notification.service.ts @@ -0,0 +1,41 @@ +import { Notification } from "@/types/notification.types"; +import { getAuthToken } from "@/utils/auth-utils"; +import api from "@/utils/axios"; + +export class NotificationService { + static async getNotifications(): Promise { + try { + const token = getAuthToken(); + if (!token) { + throw new Error("Vous devez être connecté pour accéder à cette ressource"); + } + const { data } = await api.get(`/api/v1/notifications`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return data; + } catch (error) { + console.error("Erreur lors de la récupération des notifications:", error); + throw error; + } + } + + static async getNotificationById(id: string): Promise { + try { + const token = getAuthToken(); + if (!token) { + throw new Error("Vous devez être connecté pour accéder à cette ressource"); + } + const { data } = await api.get(`/api/v1/notifications/${id}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return data; + } catch (error) { + console.error(`Erreur lors de la récupération de la notification ${id}:`, error); + throw error; + } + } +} diff --git a/src/types/attendance.types.ts b/src/types/attendance.types.ts index 558dc97..b4445df 100644 --- a/src/types/attendance.types.ts +++ b/src/types/attendance.types.ts @@ -1,9 +1,28 @@ +import { Course } from "./course.types" +import { AcademicYear } from "./academic-year.types" +import { User } from "./user.types" + +export interface ClassSession { + id: string; + date: string; + heureDebut: string; + heureFin: string; + academicYear: AcademicYear; + course: Course; + professor: User; + classRepresentative: User; + createdAt: string; + updatedAt: string; +} + export interface Attendance { id: string; professorId: string; courseId: string; date: string; status: "PRESENT" | "ABSENT" | "LATE"; + classSession: ClassSession; + professor: User; comments?: string; createdAt: string; updatedAt: string; @@ -28,11 +47,4 @@ export interface UpdateAttendanceInput { comments?: string; } -export interface Course { - id: string; - title: string; - startTime: string; - endTime: string; - location: string; - hasAttendance: boolean; -} + \ No newline at end of file diff --git a/src/types/notification.types.ts b/src/types/notification.types.ts new file mode 100644 index 0000000..f09c9a6 --- /dev/null +++ b/src/types/notification.types.ts @@ -0,0 +1,16 @@ +import { Attendance } from './attendance.types'; +import { User } from './user.types'; + +export type NotificationStatus = 'SENT' | 'READ' | 'DELETED'; + +export interface Notification { + id: string; + message: string; + status: NotificationStatus; + emargement: Attendance; + recipient: User; + createdAt: string; + updatedAt: string; +} + +export type NotificationResponse = Notification[];