mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-22 14:38:19 +02:00
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
This commit is contained in:
1 parent
3b180b84a4
commit
9f694d0251
3 files changed
+136
-42
No files matched your search
@@ -387,33 +387,31 @@ export default function AnnouncementCard() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={announcement.id} className="flex items-start justify-between p-3 rounded-lg border bg-card hover:bg-accent/50 transition-colors">
|
<div key={announcement.id} className="flex items-start justify-between p-3 rounded-lg border bg-card hover:bg-accent/50 transition-colors">
|
||||||
<div className="flex items-start gap-3 flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<TypeIcon className="h-4 w-4 text-blue-600 mt-0.5 flex-shrink-0" />
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<div className="flex-1 min-w-0">
|
<h4 className="font-medium text-sm truncate">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
{announcement.title}
|
||||||
<h4 className="font-medium text-sm truncate">
|
</h4>
|
||||||
{announcement.title}
|
<Badge variant={priorityConfig.color} className="flex items-center gap-1 text-xs">
|
||||||
</h4>
|
<TypeIcon className="w-2.5 h-2.5" />
|
||||||
<Badge variant={priorityConfig.color} className="flex items-center gap-1 text-xs">
|
{priorityConfig.label}
|
||||||
{priorityConfig.label}
|
</Badge>
|
||||||
</Badge>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{announcement.content && (
|
{announcement.content && (
|
||||||
<p className="text-xs text-muted-foreground mb-1 line-clamp-1">
|
<p className="text-xs text-muted-foreground mb-1 line-clamp-1">
|
||||||
{announcement.content}
|
{announcement.content}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
<div className="flex items-center gap-3 text-xs text-muted-foreground">
|
||||||
<span>
|
<span>
|
||||||
{safeFormat(safeDate(announcement.createdAt), 'dd/MM à HH:mm')}
|
{safeFormat(safeDate(announcement.createdAt), 'dd/MM à HH:mm')}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{announcement.author.firstName} {announcement.author.lastName}
|
{announcement.author.firstName} {announcement.author.lastName}
|
||||||
{announcement.group && ` • ${announcement.group.name}`}
|
{announcement.group && ` • ${announcement.group.name}`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{user?.role === 'admin' && (
|
{user?.role === 'admin' && (
|
||||||
|
|||||||
+109
-12
@@ -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)
|
// Type aliases pour compatibilité
|
||||||
class AnnouncementMemoryStorage {
|
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 => ({
|
||||||
|
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 announcements = new Map<number, Announcement>();
|
||||||
private idCounter = 1;
|
private idCounter = 1;
|
||||||
private readonly MAX_ANNOUNCEMENTS = 5;
|
private readonly MAX_ANNOUNCEMENTS = 5;
|
||||||
@@ -178,16 +261,30 @@ class AnnouncementMemoryStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance globale partagée
|
// Instances globales pour les différents environnements
|
||||||
let globalAnnouncementStorage: AnnouncementMemoryStorage | null = null;
|
let memoryInstance: AnnouncementMemoryStorage | null = null;
|
||||||
|
let databaseInstance: AnnouncementDatabaseStorage | null = null;
|
||||||
|
|
||||||
export function getAnnouncementStorage(
|
export function getAnnouncementStorage(
|
||||||
usersGetter: () => Promise<User[]>,
|
usersGetter?: () => Promise<User[]>,
|
||||||
groupsGetter: () => Promise<Group[]>
|
groupsGetter?: () => Promise<Group[]>
|
||||||
): AnnouncementMemoryStorage {
|
): IAnnouncementStorage {
|
||||||
if (!globalAnnouncementStorage) {
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
globalAnnouncementStorage = new AnnouncementMemoryStorage(usersGetter, groupsGetter);
|
|
||||||
console.log('📢 Stockage mémoire des annonces initialisé');
|
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;
|
|
||||||
}
|
}
|
||||||
+4
-5
@@ -70,6 +70,8 @@ export const userGroups = pgTable("user_groups", {
|
|||||||
createdAt: timestamp("created_at").defaultNow(),
|
createdAt: timestamp("created_at").defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Suppliers - Schema exact correspondant à la production
|
// Suppliers - Schema exact correspondant à la production
|
||||||
export const suppliers = pgTable("suppliers", {
|
export const suppliers = pgTable("suppliers", {
|
||||||
id: serial("id").primaryKey(),
|
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
|
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,
|
id: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
}).extend({
|
|
||||||
type: z.enum(["info", "warning", "error", "success"]).default("info"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mapping pour la compatibilité avec l'interface actuelle
|
export const insertAnnouncementSchema = insertDashboardMessageSchema;
|
||||||
export const insertDashboardMessageSchema = insertAnnouncementSchema;
|
|
||||||
|
|
||||||
export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({
|
export const insertNocodbConfigSchema = createInsertSchema(nocodbConfig).omit({
|
||||||
id: true,
|
id: true,
|
||||||
|
|||||||
Reference in new issue
Block a user