Merge pull request #186 from R0m1k3/1.1

Update announcement system to use new dashboard messages table
This commit is contained in:
LogiFlow authored and GitHub committed 2025-08-16 16:13:39 +02:00
commit f4bbd8cb75
4 files changed
+73 -49

No files matched your search

+20 -23
View File
@@ -17,16 +17,15 @@ class AnnouncementMemoryStorage {
} }
private initializeTestData() { private initializeTestData() {
// Créer quelques annonces de test // Créer quelques annonces de test avec la nouvelle structure DASHBOARD_MESSAGES
const testAnnouncement: Announcement = { const testAnnouncement: Announcement = {
id: this.idCounter++, id: this.idCounter++,
title: "Bienvenue dans le système d'informations", 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.", 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', type: 'warning',
authorId: 'admin', createdBy: 'admin',
groupId: 1, storeId: 1,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(),
}; };
this.announcements.set(testAnnouncement.id, testAnnouncement); this.announcements.set(testAnnouncement.id, testAnnouncement);
@@ -34,11 +33,10 @@ class AnnouncementMemoryStorage {
id: this.idCounter++, id: this.idCounter++,
title: "Maintenance programmée", title: "Maintenance programmée",
content: "Une maintenance système aura lieu dimanche prochain de 2h à 4h du matin.", content: "Une maintenance système aura lieu dimanche prochain de 2h à 4h du matin.",
priority: 'normal', type: 'info',
authorId: 'admin', createdBy: 'admin',
groupId: null, // Annonce globale storeId: null, // Annonce globale
createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // Hier 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); this.announcements.set(testAnnouncement2.id, testAnnouncement2);
} }
@@ -51,7 +49,7 @@ class AnnouncementMemoryStorage {
// Filtrer par groupIds si fourni (pour le filtrage admin par magasin) // Filtrer par groupIds si fourni (pour le filtrage admin par magasin)
if (groupIds && groupIds.length > 0) { if (groupIds && groupIds.length > 0) {
filteredAnnouncements = announcements.filter(announcement => 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 // Ajouter les relations
const announcementsWithRelations = filteredAnnouncements.map(announcement => { const announcementsWithRelations = filteredAnnouncements.map(announcement => {
const author = usersMap.get(announcement.authorId); const author = usersMap.get(announcement.createdBy);
const group = announcement.groupId ? groupsMap.get(announcement.groupId) : undefined; const group = announcement.storeId ? groupsMap.get(announcement.storeId) : undefined;
return { return {
...announcement, ...announcement,
@@ -76,12 +74,12 @@ class AnnouncementMemoryStorage {
}; };
}).filter(a => a.author); // Filtrer celles sans auteur valide }).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) => { return announcementsWithRelations.sort((a, b) => {
const priorityOrder = { urgent: 3, important: 2, normal: 1 }; const typeOrder = { error: 4, warning: 3, success: 2, info: 1 };
const priorityDiff = (priorityOrder[b.priority as keyof typeof priorityOrder] || 1) - const typeDiff = (typeOrder[b.type as keyof typeof typeOrder] || 1) -
(priorityOrder[a.priority as keyof typeof priorityOrder] || 1); (typeOrder[a.type as keyof typeof typeOrder] || 1);
if (priorityDiff !== 0) return priorityDiff; if (typeDiff !== 0) return typeDiff;
return new Date(b.createdAt!).getTime() - new Date(a.createdAt!).getTime(); return new Date(b.createdAt!).getTime() - new Date(a.createdAt!).getTime();
}); });
} }
@@ -103,10 +101,9 @@ class AnnouncementMemoryStorage {
const announcement: Announcement = { const announcement: Announcement = {
id, id,
...announcementData, ...announcementData,
priority: announcementData.priority || 'normal', type: announcementData.type || 'info',
groupId: announcementData.groupId || null, storeId: announcementData.storeId || null,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(),
}; };
this.announcements.set(id, announcement); this.announcements.set(id, announcement);
@@ -130,8 +127,8 @@ class AnnouncementMemoryStorage {
const usersMap = new Map(users.map(u => [u.id, u])); const usersMap = new Map(users.map(u => [u.id, u]));
const groupsMap = new Map(groups.map(g => [g.id, g])); const groupsMap = new Map(groups.map(g => [g.id, g]));
const author = usersMap.get(announcement.authorId); const author = usersMap.get(announcement.createdBy);
const group = announcement.groupId ? groupsMap.get(announcement.groupId) : undefined; const group = announcement.storeId ? groupsMap.get(announcement.storeId) : undefined;
if (!author) { if (!author) {
return undefined; return undefined;
@@ -154,7 +151,7 @@ class AnnouncementMemoryStorage {
...existingAnnouncement, ...existingAnnouncement,
...announcementData, ...announcementData,
id, // Ensure ID doesn't change id, // Ensure ID doesn't change
updatedAt: new Date(), // Note: DASHBOARD_MESSAGES table n'a pas de champ updatedAt
}; };
this.announcements.set(id, updatedAnnouncement); this.announcements.set(id, updatedAnnouncement);
+2 -8
View File
@@ -49,14 +49,8 @@ if (isProduction && dbUrl && (dbUrl.includes('logiflow-db') || dbUrl.includes('l
neonConfig.webSocketConstructor = ws.default; neonConfig.webSocketConstructor = ws.default;
if (dbUrl) { if (dbUrl) {
try { pool = new Pool({ connectionString: dbUrl });
pool = new Pool({ connectionString: dbUrl }); db = drizzle({ client: pool, schema });
db = drizzle({ client: pool, schema });
} catch (error) {
console.error('❌ Neon connection failed, will use MemStorage:', error);
pool = null;
db = null;
}
} else { } else {
// Create dummy instances for development // Create dummy instances for development
pool = null; pool = null;
+17
View File
@@ -10,6 +10,7 @@ import {
customerOrders, customerOrders,
dlcProducts, dlcProducts,
tasks, tasks,
dashboardMessages,
announcements, announcements,
nocodbConfig, nocodbConfig,
invoiceVerificationCache, invoiceVerificationCache,
@@ -3346,6 +3347,22 @@ export class MemStorage implements IStorage {
return await announcementStorage.getAnnouncements(groupIds); return await announcementStorage.getAnnouncements(groupIds);
} }
async getAnnouncement(id: number): Promise<AnnouncementWithRelations | undefined> {
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<InsertAnnouncement>): Promise<AnnouncementWithRelations> {
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<boolean> { async deleteAnnouncement(id: number): Promise<boolean> {
const announcementStorage = getAnnouncementStorage( const announcementStorage = getAnnouncementStorage(
async () => Array.from(this.users.values()), async () => Array.from(this.users.values()),
+34 -18
View File
@@ -255,18 +255,20 @@ export const tasks = pgTable("tasks", {
updatedAt: timestamp("updated_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(),
}); });
// Announcements - Système d'information du dashboard // Dashboard Messages - Utilise la table existante DASHBOARD_MESSAGES
export const announcements = pgTable("announcements", { export const dashboardMessages = pgTable("dashboard_messages", {
id: serial("id").primaryKey(), 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 content: text("content").notNull(), // Contenu du message
priority: varchar("priority").notNull().default("normal"), // normal, important, urgent type: varchar("type", { length: 50 }).notNull().default("info"), // info, warning, error, success
authorId: varchar("author_id").notNull(), // Référence vers l'utilisateur créateur storeId: integer("store_id"), // Filtrage par magasin (null = global)
groupId: integer("group_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(), createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(),
}); });
// Alias pour la compatibilité avec le code existant
export const announcements = dashboardMessages;
// Relations // Relations
export const usersRelations = relations(users, ({ many }) => ({ export const usersRelations = relations(users, ({ many }) => ({
userGroups: many(userGroups), userGroups: many(userGroups),
@@ -277,7 +279,7 @@ export const usersRelations = relations(users, ({ many }) => ({
createdDlcProducts: many(dlcProducts), createdDlcProducts: many(dlcProducts),
createdTasks: many(tasks), createdTasks: many(tasks),
assignedTasks: many(tasks), assignedTasks: many(tasks),
createdAnnouncements: many(announcements), createdAnnouncements: many(dashboardMessages),
})); }));
export const groupsRelations = relations(groups, ({ one, many }) => ({ export const groupsRelations = relations(groups, ({ one, many }) => ({
@@ -288,7 +290,7 @@ export const groupsRelations = relations(groups, ({ one, many }) => ({
customerOrders: many(customerOrders), customerOrders: many(customerOrders),
dlcProducts: many(dlcProducts), dlcProducts: many(dlcProducts),
tasks: many(tasks), tasks: many(tasks),
announcements: many(announcements), announcements: many(dashboardMessages),
})); }));
export const userGroupsRelations = relations(userGroups, ({ one }) => ({ 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, { author: one(users, {
fields: [announcements.authorId], fields: [dashboardMessages.createdBy],
references: [users.id], references: [users.id],
}), }),
group: one(groups, { group: one(groups, {
fields: [announcements.groupId], fields: [dashboardMessages.storeId],
references: [groups.id], references: [groups.id],
}), }),
})); }));
// Alias pour compatibilité
export const announcementsRelations = dashboardMessagesRelations;
// Zod schemas // Zod schemas
export const insertUserSchema = createInsertSchema(users).pick({ export const insertUserSchema = createInsertSchema(users).pick({
id: true, 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 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, id: true,
createdAt: 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({ export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({
id: true, id: true,
createdAt: true, createdAt: true,
@@ -587,8 +596,12 @@ export type DlcProductFrontend = Omit<DlcProduct, 'expiryDate'> & { dlcDate: Dat
export type InsertDlcProductFrontend = z.infer<typeof insertDlcProductFrontendSchema>; export type InsertDlcProductFrontend = z.infer<typeof insertDlcProductFrontendSchema>;
export type Task = typeof tasks.$inferSelect; export type Task = typeof tasks.$inferSelect;
export type InsertTask = z.infer<typeof insertTaskSchema>; export type InsertTask = z.infer<typeof insertTaskSchema>;
export type Announcement = typeof announcements.$inferSelect; export type DashboardMessage = typeof dashboardMessages.$inferSelect;
export type InsertAnnouncement = z.infer<typeof insertAnnouncementSchema>; export type InsertDashboardMessage = z.infer<typeof insertDashboardMessageSchema>;
// Alias pour compatibilité
export type Announcement = DashboardMessage;
export type InsertAnnouncement = InsertDashboardMessage;
// Complex types with relations // Complex types with relations
@@ -637,12 +650,15 @@ export type TaskWithRelations = Task & {
group: Group; group: Group;
}; };
// Announcement with relations type // Dashboard Message with relations type
export type AnnouncementWithRelations = Announcement & { export type DashboardMessageWithRelations = DashboardMessage & {
author: User; author: User;
group?: Group; group?: Group;
}; };
// Alias pour compatibilité
export type AnnouncementWithRelations = DashboardMessageWithRelations;
// SAV (Service Après-Vente) Tables // SAV (Service Après-Vente) Tables
export const savTickets = pgTable("sav_tickets", { export const savTickets = pgTable("sav_tickets", {
id: serial("id").primaryKey(), id: serial("id").primaryKey(),