From e7b33c5325235094b721766377e432f7c359cafd Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Sat, 16 Aug 2025 14:11:58 +0000 Subject: [PATCH] Update announcement system to use new dashboard messages table Refactors the announcement storage and schema to utilize the new `dashboard_messages` table, renaming fields like `priority` to `type` and `groupId` to `storeId`, and updating sorting logic to reflect the new type system. 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/VpUPpsj --- server/announcementStorage.ts | 43 ++++++++++++++--------------- server/db.ts | 10 ++----- server/storage.ts | 17 ++++++++++++ shared/schema.ts | 52 +++++++++++++++++++++++------------ 4 files changed, 73 insertions(+), 49 deletions(-) diff --git a/server/announcementStorage.ts b/server/announcementStorage.ts index e3ffd6a..4927da8 100644 --- a/server/announcementStorage.ts +++ b/server/announcementStorage.ts @@ -17,16 +17,15 @@ class AnnouncementMemoryStorage { } private initializeTestData() { - // Créer quelques annonces de test + // Créer quelques annonces de test avec la nouvelle structure DASHBOARD_MESSAGES const testAnnouncement: Announcement = { id: this.idCounter++, title: "Bienvenue dans le système d'informations", content: "Ce nouveau système remplace l'ancienne carte des commandes clients. Vous pouvez maintenant créer et gérer des annonces importantes.", - priority: 'important', - authorId: 'admin', - groupId: 1, + type: 'warning', + createdBy: 'admin', + storeId: 1, createdAt: new Date(), - updatedAt: new Date(), }; this.announcements.set(testAnnouncement.id, testAnnouncement); @@ -34,11 +33,10 @@ class AnnouncementMemoryStorage { id: this.idCounter++, title: "Maintenance programmée", content: "Une maintenance système aura lieu dimanche prochain de 2h à 4h du matin.", - priority: 'normal', - authorId: 'admin', - groupId: null, // Annonce globale + type: 'info', + createdBy: 'admin', + storeId: null, // Annonce globale createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // Hier - updatedAt: new Date(Date.now() - 24 * 60 * 60 * 1000), }; this.announcements.set(testAnnouncement2.id, testAnnouncement2); } @@ -51,7 +49,7 @@ class AnnouncementMemoryStorage { // Filtrer par groupIds si fourni (pour le filtrage admin par magasin) if (groupIds && groupIds.length > 0) { filteredAnnouncements = announcements.filter(announcement => - !announcement.groupId || groupIds.includes(announcement.groupId) + !announcement.storeId || groupIds.includes(announcement.storeId) ); } @@ -66,8 +64,8 @@ class AnnouncementMemoryStorage { // Ajouter les relations const announcementsWithRelations = filteredAnnouncements.map(announcement => { - const author = usersMap.get(announcement.authorId); - const group = announcement.groupId ? groupsMap.get(announcement.groupId) : undefined; + const author = usersMap.get(announcement.createdBy); + const group = announcement.storeId ? groupsMap.get(announcement.storeId) : undefined; return { ...announcement, @@ -76,12 +74,12 @@ class AnnouncementMemoryStorage { }; }).filter(a => a.author); // Filtrer celles sans auteur valide - // Trier par priorité (urgent > important > normal) puis par date (plus récent d'abord) + // Trier par type (error > warning > success > info) puis par date (plus récent d'abord) return announcementsWithRelations.sort((a, b) => { - const priorityOrder = { urgent: 3, important: 2, normal: 1 }; - const priorityDiff = (priorityOrder[b.priority as keyof typeof priorityOrder] || 1) - - (priorityOrder[a.priority as keyof typeof priorityOrder] || 1); - if (priorityDiff !== 0) return priorityDiff; + const typeOrder = { error: 4, warning: 3, success: 2, info: 1 }; + const typeDiff = (typeOrder[b.type as keyof typeof typeOrder] || 1) - + (typeOrder[a.type as keyof typeof typeOrder] || 1); + if (typeDiff !== 0) return typeDiff; return new Date(b.createdAt!).getTime() - new Date(a.createdAt!).getTime(); }); } @@ -103,10 +101,9 @@ class AnnouncementMemoryStorage { const announcement: Announcement = { id, ...announcementData, - priority: announcementData.priority || 'normal', - groupId: announcementData.groupId || null, + type: announcementData.type || 'info', + storeId: announcementData.storeId || null, createdAt: new Date(), - updatedAt: new Date(), }; this.announcements.set(id, announcement); @@ -130,8 +127,8 @@ class AnnouncementMemoryStorage { const usersMap = new Map(users.map(u => [u.id, u])); const groupsMap = new Map(groups.map(g => [g.id, g])); - const author = usersMap.get(announcement.authorId); - const group = announcement.groupId ? groupsMap.get(announcement.groupId) : undefined; + const author = usersMap.get(announcement.createdBy); + const group = announcement.storeId ? groupsMap.get(announcement.storeId) : undefined; if (!author) { return undefined; @@ -154,7 +151,7 @@ class AnnouncementMemoryStorage { ...existingAnnouncement, ...announcementData, id, // Ensure ID doesn't change - updatedAt: new Date(), + // Note: DASHBOARD_MESSAGES table n'a pas de champ updatedAt }; this.announcements.set(id, updatedAnnouncement); diff --git a/server/db.ts b/server/db.ts index 9a8e4c7..8a1c88d 100644 --- a/server/db.ts +++ b/server/db.ts @@ -49,14 +49,8 @@ if (isProduction && dbUrl && (dbUrl.includes('logiflow-db') || dbUrl.includes('l neonConfig.webSocketConstructor = ws.default; if (dbUrl) { - try { - pool = new Pool({ connectionString: dbUrl }); - db = drizzle({ client: pool, schema }); - } catch (error) { - console.error('❌ Neon connection failed, will use MemStorage:', error); - pool = null; - db = null; - } + pool = new Pool({ connectionString: dbUrl }); + db = drizzle({ client: pool, schema }); } else { // Create dummy instances for development pool = null; diff --git a/server/storage.ts b/server/storage.ts index a700116..5569193 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -10,6 +10,7 @@ import { customerOrders, dlcProducts, tasks, + dashboardMessages, announcements, nocodbConfig, invoiceVerificationCache, @@ -3346,6 +3347,22 @@ export class MemStorage implements IStorage { return await announcementStorage.getAnnouncements(groupIds); } + async getAnnouncement(id: number): Promise { + const announcementStorage = getAnnouncementStorage( + async () => Array.from(this.users.values()), + async () => Array.from(this.groups.values()) + ); + return await announcementStorage.getAnnouncement(id); + } + + async updateAnnouncement(id: number, announcementData: Partial): Promise { + const announcementStorage = getAnnouncementStorage( + async () => Array.from(this.users.values()), + async () => Array.from(this.groups.values()) + ); + return await announcementStorage.updateAnnouncement(id, announcementData); + } + async deleteAnnouncement(id: number): Promise { const announcementStorage = getAnnouncementStorage( async () => Array.from(this.users.values()), diff --git a/shared/schema.ts b/shared/schema.ts index c4504b1..ec72b24 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -255,18 +255,20 @@ export const tasks = pgTable("tasks", { updatedAt: timestamp("updated_at").defaultNow(), }); -// Announcements - Système d'information du dashboard -export const announcements = pgTable("announcements", { +// Dashboard Messages - Utilise la table existante DASHBOARD_MESSAGES +export const dashboardMessages = pgTable("dashboard_messages", { id: serial("id").primaryKey(), - title: varchar("title").notNull(), // Titre du message + title: varchar("title", { length: 255 }).notNull(), // Titre du message content: text("content").notNull(), // Contenu du message - priority: varchar("priority").notNull().default("normal"), // normal, important, urgent - authorId: varchar("author_id").notNull(), // Référence vers l'utilisateur créateur - groupId: integer("group_id"), // Filtrage par magasin (null = global) + type: varchar("type", { length: 50 }).notNull().default("info"), // info, warning, error, success + storeId: integer("store_id"), // Filtrage par magasin (null = global) + createdBy: varchar("created_by", { length: 255 }).notNull(), // Référence vers l'utilisateur créateur createdAt: timestamp("created_at").defaultNow(), - updatedAt: timestamp("updated_at").defaultNow(), }); +// Alias pour la compatibilité avec le code existant +export const announcements = dashboardMessages; + // Relations export const usersRelations = relations(users, ({ many }) => ({ userGroups: many(userGroups), @@ -277,7 +279,7 @@ export const usersRelations = relations(users, ({ many }) => ({ createdDlcProducts: many(dlcProducts), createdTasks: many(tasks), assignedTasks: many(tasks), - createdAnnouncements: many(announcements), + createdAnnouncements: many(dashboardMessages), })); export const groupsRelations = relations(groups, ({ one, many }) => ({ @@ -288,7 +290,7 @@ export const groupsRelations = relations(groups, ({ one, many }) => ({ customerOrders: many(customerOrders), dlcProducts: many(dlcProducts), tasks: many(tasks), - announcements: many(announcements), + announcements: many(dashboardMessages), })); export const userGroupsRelations = relations(userGroups, ({ one }) => ({ @@ -409,17 +411,20 @@ export const tasksRelations = relations(tasks, ({ one }) => ({ }), })); -export const announcementsRelations = relations(announcements, ({ one }) => ({ +export const dashboardMessagesRelations = relations(dashboardMessages, ({ one }) => ({ author: one(users, { - fields: [announcements.authorId], + fields: [dashboardMessages.createdBy], references: [users.id], }), group: one(groups, { - fields: [announcements.groupId], + fields: [dashboardMessages.storeId], references: [groups.id], }), })); +// Alias pour compatibilité +export const announcementsRelations = dashboardMessagesRelations; + // Zod schemas export const insertUserSchema = createInsertSchema(users).pick({ id: true, @@ -525,12 +530,16 @@ export const insertTaskSchema = createInsertSchema(tasks).omit({ dueDate: z.coerce.date().optional().nullable(), // Convertit automatiquement les chaînes en Date }); -export const insertAnnouncementSchema = createInsertSchema(announcements).omit({ +export const insertAnnouncementSchema = createInsertSchema(dashboardMessages).omit({ id: true, createdAt: true, - updatedAt: 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 insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({ id: true, createdAt: true, @@ -587,8 +596,12 @@ export type DlcProductFrontend = Omit & { dlcDate: Dat export type InsertDlcProductFrontend = z.infer; export type Task = typeof tasks.$inferSelect; export type InsertTask = z.infer; -export type Announcement = typeof announcements.$inferSelect; -export type InsertAnnouncement = z.infer; +export type DashboardMessage = typeof dashboardMessages.$inferSelect; +export type InsertDashboardMessage = z.infer; + +// Alias pour compatibilité +export type Announcement = DashboardMessage; +export type InsertAnnouncement = InsertDashboardMessage; // Complex types with relations @@ -637,12 +650,15 @@ export type TaskWithRelations = Task & { group: Group; }; -// Announcement with relations type -export type AnnouncementWithRelations = Announcement & { +// Dashboard Message with relations type +export type DashboardMessageWithRelations = DashboardMessage & { author: User; group?: Group; }; +// Alias pour compatibilité +export type AnnouncementWithRelations = DashboardMessageWithRelations; + // SAV (Service Après-Vente) Tables export const savTickets = pgTable("sav_tickets", { id: serial("id").primaryKey(),