mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Update the announcement card component to match the dashboard's visual style and resolve an issue where new announcements were not displaying correctly due to incorrect data handling and display logic. 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/dnQ3tT8
287 lines
10 KiB
TypeScript
287 lines
10 KiB
TypeScript
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";
|
|
|
|
// Type aliases pour compatibilité
|
|
type Announcement = DashboardMessage;
|
|
type InsertAnnouncement = InsertDashboardMessage;
|
|
type AnnouncementWithRelations = DashboardMessageWithRelations;
|
|
|
|
// Interface commune pour le stockage
|
|
interface IAnnouncementStorage {
|
|
getAnnouncements(groupIds?: number[]): Promise<AnnouncementWithRelations[]>;
|
|
createAnnouncement(announcement: InsertAnnouncement): Promise<DashboardMessage>;
|
|
updateAnnouncement(id: number, announcement: Partial<InsertAnnouncement>): Promise<DashboardMessage>;
|
|
deleteAnnouncement(id: number): Promise<void>;
|
|
}
|
|
|
|
// Stockage PostgreSQL pour la production
|
|
class AnnouncementDatabaseStorage implements IAnnouncementStorage {
|
|
async getAnnouncements(groupIds?: number[]): Promise<AnnouncementWithRelations[]> {
|
|
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: any) => ({
|
|
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<DashboardMessage> {
|
|
const [created] = await db
|
|
.insert(dashboardMessages)
|
|
.values(announcement)
|
|
.returning();
|
|
return created;
|
|
}
|
|
|
|
async updateAnnouncement(id: number, announcement: Partial<InsertAnnouncement>): Promise<DashboardMessage> {
|
|
const [updated] = await db
|
|
.update(dashboardMessages)
|
|
.set(announcement)
|
|
.where(eq(dashboardMessages.id, id))
|
|
.returning();
|
|
return updated;
|
|
}
|
|
|
|
async deleteAnnouncement(id: number): Promise<void> {
|
|
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<number, Announcement>();
|
|
private idCounter = 1;
|
|
private readonly MAX_ANNOUNCEMENTS = 5;
|
|
|
|
// Référence vers les utilisateurs et groupes pour les relations
|
|
private usersGetter: () => Promise<User[]>;
|
|
private groupsGetter: () => Promise<Group[]>;
|
|
|
|
constructor(usersGetter: () => Promise<User[]>, groupsGetter: () => Promise<Group[]>) {
|
|
this.usersGetter = usersGetter;
|
|
this.groupsGetter = groupsGetter;
|
|
this.initializeTestData();
|
|
}
|
|
|
|
private initializeTestData() {
|
|
// 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.",
|
|
type: 'warning',
|
|
createdBy: 'admin',
|
|
storeId: 1,
|
|
createdAt: new Date(),
|
|
};
|
|
this.announcements.set(testAnnouncement.id, testAnnouncement);
|
|
|
|
const testAnnouncement2: Announcement = {
|
|
id: this.idCounter++,
|
|
title: "Maintenance programmée",
|
|
content: "Une maintenance système aura lieu dimanche prochain de 2h à 4h du matin.",
|
|
type: 'info',
|
|
createdBy: 'admin',
|
|
storeId: null, // Annonce globale
|
|
createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // Hier
|
|
};
|
|
this.announcements.set(testAnnouncement2.id, testAnnouncement2);
|
|
}
|
|
|
|
async getAnnouncements(groupIds?: number[]): Promise<AnnouncementWithRelations[]> {
|
|
const announcements = Array.from(this.announcements.values());
|
|
|
|
let filteredAnnouncements = announcements;
|
|
|
|
// Filtrer par groupIds si fourni (pour le filtrage admin par magasin)
|
|
if (groupIds && groupIds.length > 0) {
|
|
filteredAnnouncements = announcements.filter(announcement =>
|
|
!announcement.storeId || groupIds.includes(announcement.storeId)
|
|
);
|
|
}
|
|
|
|
// Récupérer les utilisateurs et groupes pour les relations
|
|
const [users, groups] = await Promise.all([
|
|
this.usersGetter(),
|
|
this.groupsGetter()
|
|
]);
|
|
|
|
const usersMap = new Map(users.map(u => [u.id, u]));
|
|
const groupsMap = new Map(groups.map(g => [g.id, g]));
|
|
|
|
// Ajouter les relations
|
|
const announcementsWithRelations = filteredAnnouncements.map(announcement => {
|
|
const author = usersMap.get(announcement.createdBy);
|
|
const group = announcement.storeId ? groupsMap.get(announcement.storeId) : undefined;
|
|
|
|
return {
|
|
...announcement,
|
|
author: author!,
|
|
group,
|
|
};
|
|
}).filter(a => a.author); // Filtrer celles sans auteur valide
|
|
|
|
// Trier par type (error > warning > success > info) puis par date (plus récent d'abord)
|
|
return announcementsWithRelations.sort((a, b) => {
|
|
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();
|
|
});
|
|
}
|
|
|
|
async createAnnouncement(announcementData: InsertAnnouncement): Promise<Announcement> {
|
|
// Vérifier la limite de 5 annonces maximum
|
|
if (this.announcements.size >= this.MAX_ANNOUNCEMENTS) {
|
|
// Supprimer l'annonce la plus ancienne (par ordre de création)
|
|
const oldestAnnouncement = Array.from(this.announcements.values())
|
|
.sort((a, b) => new Date(a.createdAt!).getTime() - new Date(b.createdAt!).getTime())[0];
|
|
|
|
if (oldestAnnouncement) {
|
|
this.announcements.delete(oldestAnnouncement.id);
|
|
console.log(`🗑️ Annonce supprimée automatiquement (limite de ${this.MAX_ANNOUNCEMENTS}): ${oldestAnnouncement.title}`);
|
|
}
|
|
}
|
|
|
|
const id = this.idCounter++;
|
|
const announcement: Announcement = {
|
|
id,
|
|
...announcementData,
|
|
type: announcementData.type || 'info',
|
|
storeId: announcementData.storeId || null,
|
|
createdAt: new Date(),
|
|
};
|
|
|
|
this.announcements.set(id, announcement);
|
|
console.log(`✅ Nouvelle annonce créée: ${announcement.title} (${this.announcements.size}/${this.MAX_ANNOUNCEMENTS})`);
|
|
|
|
return announcement;
|
|
}
|
|
|
|
async getAnnouncement(id: number): Promise<AnnouncementWithRelations | undefined> {
|
|
const announcement = this.announcements.get(id);
|
|
if (!announcement) {
|
|
return undefined;
|
|
}
|
|
|
|
// Récupérer les utilisateurs et groupes pour les relations
|
|
const [users, groups] = await Promise.all([
|
|
this.usersGetter(),
|
|
this.groupsGetter()
|
|
]);
|
|
|
|
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.createdBy);
|
|
const group = announcement.storeId ? groupsMap.get(announcement.storeId) : undefined;
|
|
|
|
if (!author) {
|
|
return undefined;
|
|
}
|
|
|
|
return {
|
|
...announcement,
|
|
author,
|
|
group,
|
|
};
|
|
}
|
|
|
|
async updateAnnouncement(id: number, announcementData: Partial<InsertAnnouncement>): Promise<AnnouncementWithRelations> {
|
|
const existingAnnouncement = this.announcements.get(id);
|
|
if (!existingAnnouncement) {
|
|
throw new Error(`Announcement with ID ${id} not found`);
|
|
}
|
|
|
|
const updatedAnnouncement: Announcement = {
|
|
...existingAnnouncement,
|
|
...announcementData,
|
|
id, // Ensure ID doesn't change
|
|
// Note: DASHBOARD_MESSAGES table n'a pas de champ updatedAt
|
|
};
|
|
|
|
this.announcements.set(id, updatedAnnouncement);
|
|
console.log(`✏️ Annonce mise à jour: ${updatedAnnouncement.title} (ID: ${id})`);
|
|
|
|
// Return the announcement with relations
|
|
const announcementWithRelations = await this.getAnnouncement(id);
|
|
if (!announcementWithRelations) {
|
|
throw new Error(`Failed to get updated announcement with relations`);
|
|
}
|
|
|
|
return announcementWithRelations;
|
|
}
|
|
|
|
async deleteAnnouncement(id: number): Promise<void> {
|
|
const deleted = this.announcements.delete(id);
|
|
|
|
if (deleted) {
|
|
console.log(`🗑️ Annonce supprimée: ID ${id} (${this.announcements.size}/${this.MAX_ANNOUNCEMENTS} restantes)`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Instances globales pour les différents environnements
|
|
let memoryInstance: AnnouncementMemoryStorage | null = null;
|
|
let databaseInstance: AnnouncementDatabaseStorage | null = null;
|
|
|
|
export function getAnnouncementStorage(
|
|
usersGetter?: () => Promise<User[]>,
|
|
groupsGetter?: () => Promise<Group[]>
|
|
): 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;
|
|
}
|
|
} |