mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
feat(contacts): add contacts page with per-store management and supplier email field
- New contacts table (group_id, name, role, phone, email, notes) per store - Suppliers table: add email field with form and card display - Page /contacts: two-column layout (suppliers read-only / free contacts CRUD) - Permissions: read all roles, create/edit/delete admin + directeur + manager - Admin can filter contacts by store; other roles see their own stores only - Migration SQL: 20260515_add_contacts_and_supplier_email.sql Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
40cf456a64
commit
805664dc32
8 files changed
+736
-6
No files matched your search
@@ -78,6 +78,9 @@ import {
|
||||
type Avoir,
|
||||
type InsertAvoir,
|
||||
type AvoirWithRelations,
|
||||
contacts,
|
||||
type Contact,
|
||||
type InsertContact,
|
||||
} from "@shared/schema";
|
||||
import { db } from "./db";
|
||||
import { eq, and, inArray, desc, sql, gte, lte, lt, gt, or, isNull, isNotNull, asc, ne } from "drizzle-orm";
|
||||
@@ -109,6 +112,12 @@ export interface IStorage {
|
||||
updateSupplier(id: number, supplier: Partial<InsertSupplier>): Promise<Supplier>;
|
||||
deleteSupplier(id: number): Promise<void>;
|
||||
|
||||
// Contact operations
|
||||
getContacts(groupIds?: number[]): Promise<Contact[]>;
|
||||
createContact(contact: InsertContact): Promise<Contact>;
|
||||
updateContact(id: number, contact: Partial<InsertContact>): Promise<Contact>;
|
||||
deleteContact(id: number): Promise<void>;
|
||||
|
||||
// Order operations
|
||||
getOrders(groupIds?: number[]): Promise<OrderWithRelations[]>;
|
||||
getOrdersByDateRange(startDate: string, endDate: string, groupIds?: number[]): Promise<OrderWithRelations[]>;
|
||||
@@ -454,6 +463,32 @@ export class DatabaseStorage implements IStorage {
|
||||
await db.delete(suppliers).where(eq(suppliers.id, id));
|
||||
}
|
||||
|
||||
// Contact operations
|
||||
async getContacts(groupIds?: number[]): Promise<Contact[]> {
|
||||
if (groupIds && groupIds.length > 0) {
|
||||
return await db.select().from(contacts).where(inArray(contacts.groupId, groupIds)).orderBy(contacts.name);
|
||||
}
|
||||
return await db.select().from(contacts).orderBy(contacts.name);
|
||||
}
|
||||
|
||||
async createContact(contactData: InsertContact): Promise<Contact> {
|
||||
const [contact] = await db.insert(contacts).values(contactData).returning();
|
||||
return contact;
|
||||
}
|
||||
|
||||
async updateContact(id: number, contactData: Partial<InsertContact>): Promise<Contact> {
|
||||
const [contact] = await db
|
||||
.update(contacts)
|
||||
.set({ ...contactData, updatedAt: new Date() })
|
||||
.where(eq(contacts.id, id))
|
||||
.returning();
|
||||
return contact;
|
||||
}
|
||||
|
||||
async deleteContact(id: number): Promise<void> {
|
||||
await db.delete(contacts).where(eq(contacts.id, id));
|
||||
}
|
||||
|
||||
// Order operations
|
||||
async getOrders(groupIds?: number[]): Promise<OrderWithRelations[]> {
|
||||
let query = db
|
||||
@@ -3330,6 +3365,7 @@ export class MemStorage implements IStorage {
|
||||
private avoirs = new Map<number, Avoir>();
|
||||
private savTickets = new Map<number, SavTicket>();
|
||||
private invoiceVerificationCache = new Map<string, InvoiceVerificationCache>();
|
||||
private contactsMap = new Map<number, Contact>();
|
||||
|
||||
private idCounters = {
|
||||
group: 1,
|
||||
@@ -3344,6 +3380,7 @@ export class MemStorage implements IStorage {
|
||||
avoir: 1,
|
||||
savTicket: 1,
|
||||
reconciliationComment: 1,
|
||||
contact: 1,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
@@ -3562,6 +3599,44 @@ export class MemStorage implements IStorage {
|
||||
this.suppliers.delete(id);
|
||||
}
|
||||
|
||||
// Contact operations
|
||||
async getContacts(groupIds?: number[]): Promise<Contact[]> {
|
||||
const all = Array.from(this.contactsMap.values());
|
||||
if (groupIds && groupIds.length > 0) {
|
||||
return all.filter(c => groupIds.includes(c.groupId));
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
async createContact(contactData: InsertContact): Promise<Contact> {
|
||||
const id = this.idCounters.contact++;
|
||||
const contact: Contact = {
|
||||
id,
|
||||
groupId: contactData.groupId,
|
||||
name: contactData.name,
|
||||
role: contactData.role || null,
|
||||
phone: contactData.phone || null,
|
||||
email: contactData.email || null,
|
||||
notes: contactData.notes || null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
this.contactsMap.set(id, contact);
|
||||
return contact;
|
||||
}
|
||||
|
||||
async updateContact(id: number, contactData: Partial<InsertContact>): Promise<Contact> {
|
||||
const existing = this.contactsMap.get(id);
|
||||
if (!existing) throw new Error('Contact not found');
|
||||
const updated = { ...existing, ...contactData, updatedAt: new Date() };
|
||||
this.contactsMap.set(id, updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
async deleteContact(id: number): Promise<void> {
|
||||
this.contactsMap.delete(id);
|
||||
}
|
||||
|
||||
// NocoDB Configuration methods
|
||||
async getNocodbConfigs(): Promise<NocodbConfig[]> {
|
||||
return []; // Empty array for development
|
||||
|
||||
Reference in new issue
Block a user