From 9f694d0251ff8f9bcf9366f3d399a0ad29f7e309 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Sun, 17 Aug 2025 09:02:23 +0000 Subject: [PATCH] Update announcement system to use production database and improve card design Integrate PostgreSQL for announcement storage, aligning with production requirements and updating the announcement card UI for better visual consistency and user experience. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e437ffed-c31b-44eb-9fd5-b6f9faa8e7ce Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/e437ffed-c31b-44eb-9fd5-b6f9faa8e7ce/DII5ZDt --- client/src/components/AnnouncementCard.tsx | 52 +++++---- server/announcementStorage.ts | 121 +++++++++++++++++++-- shared/schema.ts | 9 +- 3 files changed, 138 insertions(+), 44 deletions(-) diff --git a/client/src/components/AnnouncementCard.tsx b/client/src/components/AnnouncementCard.tsx index d56e520..e982708 100644 --- a/client/src/components/AnnouncementCard.tsx +++ b/client/src/components/AnnouncementCard.tsx @@ -387,33 +387,31 @@ export default function AnnouncementCard() { return (
-
- -
-
-

- {announcement.title} -

- - {priorityConfig.label} - -
- - {announcement.content && ( -

- {announcement.content} -

- )} - -
- - {safeFormat(safeDate(announcement.createdAt), 'dd/MM à HH:mm')} - - - {announcement.author.firstName} {announcement.author.lastName} - {announcement.group && ` • ${announcement.group.name}`} - -
+
+
+

+ {announcement.title} +

+ + + {priorityConfig.label} + +
+ + {announcement.content && ( +

+ {announcement.content} +

+ )} + +
+ + {safeFormat(safeDate(announcement.createdAt), 'dd/MM à HH:mm')} + + + {announcement.author.firstName} {announcement.author.lastName} + {announcement.group && ` • ${announcement.group.name}`} +
{user?.role === 'admin' && ( diff --git a/server/announcementStorage.ts b/server/announcementStorage.ts index 4927da8..12ea3ef 100644 --- a/server/announcementStorage.ts +++ b/server/announcementStorage.ts @@ -1,7 +1,90 @@ -import { Announcement, InsertAnnouncement, AnnouncementWithRelations, User, Group } from "@shared/schema"; +import { DashboardMessage, InsertDashboardMessage, DashboardMessageWithRelations, User, Group } from "@shared/schema"; +import { db } from "./db"; +import { dashboardMessages, users, groups } from "@shared/schema"; +import { eq, desc } from "drizzle-orm"; -// Stockage en mémoire partagé pour les annonces (maximum 5) -class AnnouncementMemoryStorage { +// Type aliases pour compatibilité +type Announcement = DashboardMessage; +type InsertAnnouncement = InsertDashboardMessage; +type AnnouncementWithRelations = DashboardMessageWithRelations; + +// Interface commune pour le stockage +interface IAnnouncementStorage { + getAnnouncements(groupIds?: number[]): Promise; + createAnnouncement(announcement: InsertAnnouncement): Promise; + updateAnnouncement(id: number, announcement: Partial): Promise; + deleteAnnouncement(id: number): Promise; +} + +// Stockage PostgreSQL pour la production +class AnnouncementDatabaseStorage implements IAnnouncementStorage { + async getAnnouncements(groupIds?: number[]): Promise { + let query = db + .select({ + id: dashboardMessages.id, + title: dashboardMessages.title, + content: dashboardMessages.content, + type: dashboardMessages.type, + storeId: dashboardMessages.storeId, + createdBy: dashboardMessages.createdBy, + createdAt: dashboardMessages.createdAt, + author: { + id: users.id, + firstName: users.firstName, + lastName: users.lastName, + username: users.username, + }, + group: { + id: groups.id, + name: groups.name, + }, + }) + .from(dashboardMessages) + .leftJoin(users, eq(dashboardMessages.createdBy, users.id)) + .leftJoin(groups, eq(dashboardMessages.storeId, groups.id)) + .orderBy(desc(dashboardMessages.createdAt)); + + const results = await query; + + return results.map(result => ({ + id: result.id, + title: result.title, + content: result.content, + type: result.type, + storeId: result.storeId, + createdBy: result.createdBy, + createdAt: result.createdAt, + author: result.author as User, + group: result.group as Group | undefined, + })); + } + + async createAnnouncement(announcement: InsertAnnouncement): Promise { + const [created] = await db + .insert(dashboardMessages) + .values(announcement) + .returning(); + return created; + } + + async updateAnnouncement(id: number, announcement: Partial): Promise { + const [updated] = await db + .update(dashboardMessages) + .set(announcement) + .where(eq(dashboardMessages.id, id)) + .returning(); + return updated; + } + + async deleteAnnouncement(id: number): Promise { + await db + .delete(dashboardMessages) + .where(eq(dashboardMessages.id, id)); + } +} + +// Stockage en mémoire pour le développement +class AnnouncementMemoryStorage implements IAnnouncementStorage { private announcements = new Map(); private idCounter = 1; private readonly MAX_ANNOUNCEMENTS = 5; @@ -178,16 +261,30 @@ class AnnouncementMemoryStorage { } } -// Instance globale partagée -let globalAnnouncementStorage: AnnouncementMemoryStorage | null = null; +// Instances globales pour les différents environnements +let memoryInstance: AnnouncementMemoryStorage | null = null; +let databaseInstance: AnnouncementDatabaseStorage | null = null; export function getAnnouncementStorage( - usersGetter: () => Promise, - groupsGetter: () => Promise -): AnnouncementMemoryStorage { - if (!globalAnnouncementStorage) { - globalAnnouncementStorage = new AnnouncementMemoryStorage(usersGetter, groupsGetter); - console.log('📢 Stockage mémoire des annonces initialisé'); + usersGetter?: () => Promise, + groupsGetter?: () => Promise +): IAnnouncementStorage { + const isProduction = process.env.NODE_ENV === 'production'; + + if (isProduction) { + if (!databaseInstance) { + databaseInstance = new AnnouncementDatabaseStorage(); + console.log('📢 Stockage PostgreSQL des annonces initialisé pour la production'); + } + return databaseInstance; + } else { + if (!memoryInstance) { + if (!usersGetter || !groupsGetter) { + throw new Error("usersGetter and groupsGetter are required for memory storage initialization"); + } + memoryInstance = new AnnouncementMemoryStorage(usersGetter, groupsGetter); + console.log('📢 Stockage mémoire des annonces initialisé pour le développement'); + } + return memoryInstance; } - return globalAnnouncementStorage; } \ No newline at end of file diff --git a/shared/schema.ts b/shared/schema.ts index ec72b24..44cbc37 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -70,6 +70,8 @@ export const userGroups = pgTable("user_groups", { createdAt: timestamp("created_at").defaultNow(), }); + + // Suppliers - Schema exact correspondant à la production export const suppliers = pgTable("suppliers", { id: serial("id").primaryKey(), @@ -530,15 +532,12 @@ export const insertTaskSchema = createInsertSchema(tasks).omit({ dueDate: z.coerce.date().optional().nullable(), // Convertit automatiquement les chaînes en Date }); -export const insertAnnouncementSchema = createInsertSchema(dashboardMessages).omit({ +export const insertDashboardMessageSchema = createInsertSchema(dashboardMessages).omit({ id: true, createdAt: true, -}).extend({ - type: z.enum(["info", "warning", "error", "success"]).default("info"), }); -// Mapping pour la compatibilité avec l'interface actuelle -export const insertDashboardMessageSchema = insertAnnouncementSchema; +export const insertAnnouncementSchema = insertDashboardMessageSchema; export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({ id: true,