mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-21 14:08:15 +02:00
7 files changed
+401
-2
No files matched your search
+204
@@ -0,0 +1,204 @@
|
|||||||
|
tions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Error fetching SAV stats: error: column "priority" does not exist
|
||||||
|
|
||||||
|
at /app/node_modules/pg-pool/index.js:45:11
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22
|
||||||
|
|
||||||
|
at async DatabaseStorage.getSavTicketStats (file:///app/dist/index.js:2046:25)
|
||||||
|
|
||||||
|
at async file:///app/dist/index.js:6203:21 {
|
||||||
|
|
||||||
|
length: 108,
|
||||||
|
|
||||||
|
severity: 'ERROR',
|
||||||
|
|
||||||
|
code: '42703',
|
||||||
|
|
||||||
|
detail: undefined,
|
||||||
|
|
||||||
|
hint: undefined,
|
||||||
|
|
||||||
|
position: '28',
|
||||||
|
|
||||||
|
internalPosition: undefined,
|
||||||
|
|
||||||
|
internalQuery: undefined,
|
||||||
|
|
||||||
|
where: undefined,
|
||||||
|
|
||||||
|
schema: undefined,
|
||||||
|
|
||||||
|
table: undefined,
|
||||||
|
|
||||||
|
column: undefined,
|
||||||
|
|
||||||
|
dataType: undefined,
|
||||||
|
|
||||||
|
constraint: undefined,
|
||||||
|
|
||||||
|
file: 'parse_relation.c',
|
||||||
|
|
||||||
|
line: '3665',
|
||||||
|
|
||||||
|
routine: 'errorMissingColumn'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GET /api/sav/stats 500 in 11ms
|
||||||
|
|
||||||
|
Error fetching SAV tickets: error: column sav_tickets.priority does not exist
|
||||||
|
|
||||||
|
at /app/node_modules/pg-pool/index.js:45:11
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22
|
||||||
|
|
||||||
|
at async DatabaseStorage.getSavTickets (file:///app/dist/index.js:1974:25)
|
||||||
|
|
||||||
|
at async file:///app/dist/index.js:6016:23 {
|
||||||
|
|
||||||
|
length: 119,
|
||||||
|
|
||||||
|
severity: 'ERROR',
|
||||||
|
|
||||||
|
code: '42703',
|
||||||
|
|
||||||
|
detail: undefined,
|
||||||
|
|
||||||
|
hint: undefined,
|
||||||
|
|
||||||
|
position: '350',
|
||||||
|
|
||||||
|
internalPosition: undefined,
|
||||||
|
|
||||||
|
internalQuery: undefined,
|
||||||
|
|
||||||
|
where: undefined,
|
||||||
|
|
||||||
|
schema: undefined,
|
||||||
|
|
||||||
|
table: undefined,
|
||||||
|
|
||||||
|
column: undefined,
|
||||||
|
|
||||||
|
dataType: undefined,
|
||||||
|
|
||||||
|
constraint: undefined,
|
||||||
|
|
||||||
|
file: 'parse_relation.c',
|
||||||
|
|
||||||
|
line: '3665',
|
||||||
|
|
||||||
|
routine: 'errorMissingColumn'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GET /api/sav/tickets 500 in 14ms
|
||||||
|
|
||||||
|
Error fetching SAV stats: error: column "priority" does not exist
|
||||||
|
|
||||||
|
at /app/node_modules/pg-pool/index.js:45:11
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22
|
||||||
|
|
||||||
|
at async DatabaseStorage.getSavTicketStats (file:///app/dist/index.js:2046:25)
|
||||||
|
|
||||||
|
at async file:///app/dist/index.js:6203:21 {
|
||||||
|
|
||||||
|
length: 108,
|
||||||
|
|
||||||
|
severity: 'ERROR',
|
||||||
|
|
||||||
|
code: '42703',
|
||||||
|
|
||||||
|
detail: undefined,
|
||||||
|
|
||||||
|
hint: undefined,
|
||||||
|
|
||||||
|
position: '28',
|
||||||
|
|
||||||
|
internalPosition: undefined,
|
||||||
|
|
||||||
|
internalQuery: undefined,
|
||||||
|
|
||||||
|
where: undefined,
|
||||||
|
|
||||||
|
schema: undefined,
|
||||||
|
|
||||||
|
table: undefined,
|
||||||
|
|
||||||
|
column: undefined,
|
||||||
|
|
||||||
|
dataType: undefined,
|
||||||
|
|
||||||
|
constraint: undefined,
|
||||||
|
|
||||||
|
file: 'parse_relation.c',
|
||||||
|
|
||||||
|
line: '3665',
|
||||||
|
|
||||||
|
routine: 'errorMissingColumn'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GET /api/sav/stats 500 in 13ms
|
||||||
|
|
||||||
|
Error fetching SAV tickets: error: column sav_tickets.priority does not exist
|
||||||
|
|
||||||
|
at /app/node_modules/pg-pool/index.js:45:11
|
||||||
|
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
|
||||||
|
|
||||||
|
at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22
|
||||||
|
|
||||||
|
at async DatabaseStorage.getSavTickets (file:///app/dist/index.js:1974:25)
|
||||||
|
|
||||||
|
at async file:///app/dist/index.js:6016:23 {
|
||||||
|
|
||||||
|
length: 119,
|
||||||
|
|
||||||
|
severity: 'ERROR',
|
||||||
|
|
||||||
|
code: '42703',
|
||||||
|
|
||||||
|
detail: undefined,
|
||||||
|
|
||||||
|
hint: undefined,
|
||||||
|
|
||||||
|
position: '350',
|
||||||
|
|
||||||
|
internalPosition: undefined,
|
||||||
|
|
||||||
|
internalQuery: undefined,
|
||||||
|
|
||||||
|
where: undefined,
|
||||||
|
|
||||||
|
schema: undefined,
|
||||||
|
|
||||||
|
table: undefined,
|
||||||
|
|
||||||
|
column: undefined,
|
||||||
|
|
||||||
|
dataType: undefined,
|
||||||
|
|
||||||
|
constraint: undefined,
|
||||||
|
|
||||||
|
file: 'parse_relation.c',
|
||||||
|
|
||||||
|
line: '3665',
|
||||||
|
|
||||||
|
routine: 'errorMissingColumn'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GET /api/sav/tickets 500 in 14ms
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
-- Migration pour corriger les tables SAV en production
|
||||||
|
-- À exécuter sur le serveur PostgreSQL de production
|
||||||
|
|
||||||
|
-- 1. Ajouter les colonnes manquantes à la table sav_tickets
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
-- Ajouter la colonne priority si elle n'existe pas
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='priority') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN priority varchar(50) NOT NULL DEFAULT 'normale';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Ajouter la colonne problem_type si elle n'existe pas
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='problem_type') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN problem_type varchar(100) NOT NULL DEFAULT 'panne';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Ajouter la colonne resolution_description si elle n'existe pas
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='resolution_description') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN resolution_description text;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Ajouter la colonne resolved_at si elle n'existe pas
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='resolved_at') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN resolved_at timestamp;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Ajouter la colonne closed_at si elle n'existe pas
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='closed_at') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN closed_at timestamp;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Vérifier si la colonne status existe, sinon l'ajouter
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='sav_tickets' AND column_name='status') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN status varchar(50) NOT NULL DEFAULT 'nouveau';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RAISE NOTICE 'Colonnes manquantes ajoutées à sav_tickets';
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 2. Créer la table sav_ticket_history si elle n'existe pas
|
||||||
|
CREATE TABLE IF NOT EXISTS sav_ticket_history (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
ticket_id integer NOT NULL,
|
||||||
|
action varchar(100) NOT NULL,
|
||||||
|
description text,
|
||||||
|
created_by varchar(255) NOT NULL,
|
||||||
|
created_at timestamp DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3. Ajouter les contraintes de clé étrangère si elles n'existent pas
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
-- Vérifier et ajouter la contrainte FK vers suppliers
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name='sav_tickets_supplier_id_fkey') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD CONSTRAINT sav_tickets_supplier_id_fkey
|
||||||
|
FOREIGN KEY (supplier_id) REFERENCES suppliers(id);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Vérifier et ajouter la contrainte FK vers groups
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name='sav_tickets_group_id_fkey') THEN
|
||||||
|
ALTER TABLE sav_tickets ADD CONSTRAINT sav_tickets_group_id_fkey
|
||||||
|
FOREIGN KEY (group_id) REFERENCES groups(id);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Vérifier et ajouter la contrainte FK pour sav_ticket_history
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_name='sav_ticket_history_ticket_id_fkey') THEN
|
||||||
|
ALTER TABLE sav_ticket_history ADD CONSTRAINT sav_ticket_history_ticket_id_fkey
|
||||||
|
FOREIGN KEY (ticket_id) REFERENCES sav_tickets(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RAISE NOTICE 'Contraintes de clé étrangère vérifiées/ajoutées';
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- 4. Créer les index pour optimiser les performances
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sav_tickets_status ON sav_tickets(status);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sav_tickets_priority ON sav_tickets(priority);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sav_tickets_supplier_id ON sav_tickets(supplier_id);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sav_tickets_group_id ON sav_tickets(group_id);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sav_tickets_created_at ON sav_tickets(created_at);
|
||||||
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sav_ticket_history_ticket_id ON sav_ticket_history(ticket_id);
|
||||||
|
|
||||||
|
-- 5. Mettre à jour les valeurs existantes si nécessaire
|
||||||
|
UPDATE sav_tickets
|
||||||
|
SET priority = 'normale'
|
||||||
|
WHERE priority IS NULL OR priority = '';
|
||||||
|
|
||||||
|
UPDATE sav_tickets
|
||||||
|
SET problem_type = 'panne'
|
||||||
|
WHERE problem_type IS NULL OR problem_type = '';
|
||||||
|
|
||||||
|
-- 6. Vérification finale
|
||||||
|
SELECT
|
||||||
|
'sav_tickets' as table_name,
|
||||||
|
COUNT(*) as record_count,
|
||||||
|
COUNT(CASE WHEN priority IS NOT NULL THEN 1 END) as records_with_priority
|
||||||
|
FROM sav_tickets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
'sav_ticket_history' as table_name,
|
||||||
|
COUNT(*) as record_count,
|
||||||
|
NULL as records_with_priority
|
||||||
|
FROM sav_ticket_history;
|
||||||
|
|
||||||
|
RAISE NOTICE 'Migration SAV production terminée avec succès !';
|
||||||
@@ -19,6 +19,15 @@ La plateforme fournit une gestion robuste des flux de travail de livraison avec
|
|||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
|
||||||
|
### 2025-08-14 - Correction critique SAV production PostgreSQL + script de migration
|
||||||
|
🔧 **Problème production SAV résolu** :
|
||||||
|
- **Problème** : Tables SAV en production manquaient colonnes essentielles (priority, problem_type, etc.)
|
||||||
|
- **Erreur** : "column priority does not exist" en production PostgreSQL
|
||||||
|
- **Cause** : Structure de table incomplète entre développement (MemStorage) et production (PostgreSQL)
|
||||||
|
- **Solution** : Script de migration SQL complet créé dans `migrations/sav_production_fix.sql`
|
||||||
|
- **Corrections** : Harmonisation des valeurs par défaut ("normale" vs "normal") dans le schéma
|
||||||
|
- **Migration inclut** : Colonnes manquantes, contraintes FK, index optimisés, données existantes mises à jour
|
||||||
|
|
||||||
### 2025-08-13 - CORRECTION COMPLÈTE PostgreSQL production - Statut et Relations
|
### 2025-08-13 - CORRECTION COMPLÈTE PostgreSQL production - Statut et Relations
|
||||||
✅ **Problème production PostgreSQL entièrement résolu** :
|
✅ **Problème production PostgreSQL entièrement résolu** :
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Pool } from 'pg';
|
import { Pool } from 'pg';
|
||||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
import * as schema from "@shared/schema";
|
import * as schema from "@shared/schema";
|
||||||
|
import { runProductionMigrations } from './migrations.production.js';
|
||||||
|
|
||||||
console.log('🐳 PRODUCTION: Configuring standard PostgreSQL database');
|
console.log('🐳 PRODUCTION: Configuring standard PostgreSQL database');
|
||||||
|
|
||||||
@@ -36,4 +37,16 @@ pool.on('error', (err) => {
|
|||||||
console.error('❌ PostgreSQL connection error:', err);
|
console.error('❌ PostgreSQL connection error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Run migrations automatically when database is configured
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
console.log('🔄 Running automatic database migrations...');
|
||||||
|
await runProductionMigrations();
|
||||||
|
console.log('✅ Database migrations completed');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Database migrations failed:', error);
|
||||||
|
// Don't fail the application startup for migration errors
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
console.log('✅ Production PostgreSQL database configured');
|
console.log('✅ Production PostgreSQL database configured');
|
||||||
@@ -6,6 +6,7 @@ import { dirname, join } from 'path';
|
|||||||
// Database will be configured in storage.js
|
// Database will be configured in storage.js
|
||||||
import { storage } from "./storage.js";
|
import { storage } from "./storage.js";
|
||||||
import { setupLocalAuth, requireAuth } from "./localAuth.production.js";
|
import { setupLocalAuth, requireAuth } from "./localAuth.production.js";
|
||||||
|
import { runProductionMigrations } from "./migrations.production.js";
|
||||||
|
|
||||||
// Get directory paths
|
// Get directory paths
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -43,6 +44,10 @@ import { registerRoutes } from "./routes.js";
|
|||||||
async function registerProductionRoutes(app: Express): Promise<void> {
|
async function registerProductionRoutes(app: Express): Promise<void> {
|
||||||
console.log('🔧 Registering all production routes...');
|
console.log('🔧 Registering all production routes...');
|
||||||
|
|
||||||
|
// Run automatic migrations before registering routes
|
||||||
|
console.log('🔄 Running automatic production migrations...');
|
||||||
|
await runProductionMigrations();
|
||||||
|
|
||||||
// Register ALL API routes (same as development)
|
// Register ALL API routes (same as development)
|
||||||
await registerRoutes(app);
|
await registerRoutes(app);
|
||||||
console.log('✅ All routes registered successfully for production');
|
console.log('✅ All routes registered successfully for production');
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { neon } from '@neondatabase/serverless';
|
||||||
|
|
||||||
|
export async function runProductionMigrations() {
|
||||||
|
const databaseUrl = process.env.DATABASE_URL;
|
||||||
|
if (!databaseUrl) {
|
||||||
|
console.error('❌ DATABASE_URL not found for migrations');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sql = neon(databaseUrl);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('🔄 Checking and running SAV production migrations...');
|
||||||
|
|
||||||
|
// Vérifier si la colonne priority existe
|
||||||
|
const checkPriorityColumn = await sql`
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_name='sav_tickets' AND column_name='priority'
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (checkPriorityColumn.length === 0) {
|
||||||
|
console.log('📝 Running SAV table migrations...');
|
||||||
|
|
||||||
|
// Ajouter seulement la colonne priority manquante
|
||||||
|
await sql`
|
||||||
|
ALTER TABLE sav_tickets ADD COLUMN priority varchar(50) NOT NULL DEFAULT 'normale';
|
||||||
|
`;
|
||||||
|
console.log('✅ Colonne priority ajoutée à sav_tickets');
|
||||||
|
|
||||||
|
// Créer les index pour optimiser les performances
|
||||||
|
await sql`
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_sav_tickets_priority ON sav_tickets(priority);
|
||||||
|
`;
|
||||||
|
console.log('✅ Index priority créé');
|
||||||
|
|
||||||
|
console.log('✅ SAV production migrations completed successfully!');
|
||||||
|
|
||||||
|
// Vérification finale
|
||||||
|
const verificationResult = await sql`
|
||||||
|
SELECT
|
||||||
|
'sav_tickets' as table_name,
|
||||||
|
COUNT(*) as record_count,
|
||||||
|
COUNT(CASE WHEN priority IS NOT NULL THEN 1 END) as records_with_priority
|
||||||
|
FROM sav_tickets
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
'sav_ticket_history' as table_name,
|
||||||
|
COUNT(*) as record_count,
|
||||||
|
NULL as records_with_priority
|
||||||
|
FROM sav_ticket_history;
|
||||||
|
`;
|
||||||
|
|
||||||
|
console.log('📊 SAV tables status:', verificationResult);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('✅ SAV migrations already applied, skipping');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error running SAV production migrations:', error);
|
||||||
|
// Ne pas faire échouer le démarrage du serveur pour les erreurs de migration
|
||||||
|
// L'application peut continuer à fonctionner même si la migration échoue
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -618,7 +618,7 @@ export const savTickets = pgTable("sav_tickets", {
|
|||||||
problemDescription: text("problem_description").notNull(),
|
problemDescription: text("problem_description").notNull(),
|
||||||
resolutionDescription: text("resolution_description"),
|
resolutionDescription: text("resolution_description"),
|
||||||
status: varchar("status", { length: 50 }).notNull().default("nouveau"),
|
status: varchar("status", { length: 50 }).notNull().default("nouveau"),
|
||||||
priority: varchar("priority", { length: 50 }).notNull().default("normal"), // normal, urgent, critique
|
priority: varchar("priority", { length: 50 }).notNull().default("normale"), // normale, haute, critique
|
||||||
clientName: varchar("client_name", { length: 255 }),
|
clientName: varchar("client_name", { length: 255 }),
|
||||||
clientPhone: varchar("client_phone", { length: 255 }),
|
clientPhone: varchar("client_phone", { length: 255 }),
|
||||||
createdBy: varchar("created_by", { length: 255 }).notNull(),
|
createdBy: varchar("created_by", { length: 255 }).notNull(),
|
||||||
@@ -673,7 +673,7 @@ export const insertSavTicketSchema = createInsertSchema(savTickets).omit({
|
|||||||
resolvedAt: true,
|
resolvedAt: true,
|
||||||
closedAt: true,
|
closedAt: true,
|
||||||
}).extend({
|
}).extend({
|
||||||
priority: z.enum(["normal", "urgent", "critique"]).default("normal"),
|
priority: z.enum(["faible", "normale", "haute", "critique"]).default("normale"),
|
||||||
status: z.enum(["nouveau", "en_cours", "attente_pieces", "attente_echange", "resolu", "ferme"]).default("nouveau"),
|
status: z.enum(["nouveau", "en_cours", "attente_pieces", "attente_echange", "resolu", "ferme"]).default("nouveau"),
|
||||||
problemType: z.enum(["defectueux", "pieces_manquantes", "non_conforme", "autre"]).default("defectueux"),
|
problemType: z.enum(["defectueux", "pieces_manquantes", "non_conforme", "autre"]).default("defectueux"),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user