CognxSafeTrack commited on
Commit
55a1607
·
1 Parent(s): 93faeee

feat: add organization useCase selection to modal and backend

Browse files
apps/admin/src/pages/ClientsManagementView.tsx CHANGED
@@ -9,6 +9,8 @@ interface Organization {
9
  id: string;
10
  name: string;
11
  status: 'ACTIVE' | 'PENDING' | 'CONFIG_REQUIRED';
 
 
12
  wabaId?: string;
13
  phoneNumbers?: { id: string, phoneNumber: string }[];
14
  lastActivity?: string;
@@ -41,7 +43,8 @@ export default function ClientsManagementView() {
41
  slug: '',
42
  adminEmail: '',
43
  adminName: '',
44
- mode: 'PEDAGOGY' as const
 
45
  });
46
  const [isCreating, setIsCreating] = useState(false);
47
  const [showGuide, setShowGuide] = useState(false);
@@ -69,7 +72,7 @@ export default function ClientsManagementView() {
69
  await api.post('/v1/organizations', newOrg, token!);
70
  await fetchClients();
71
  setIsModalOpen(false);
72
- setNewOrg({ name: '', slug: '', adminEmail: '', adminName: '', mode: 'PEDAGOGY' });
73
  } catch (error) {
74
  console.error("Failed to create organization:", error);
75
  alert("Erreur lors de la création de l'organisation. Vérifiez que le slug est unique.");
@@ -287,6 +290,25 @@ export default function ClientsManagementView() {
287
  <option value="CUSTOMER_SERVICE">Service Client IA</option>
288
  </select>
289
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  </div>
291
 
292
  <div className="p-6 bg-slate-50 rounded-2xl border border-slate-100 space-y-4">
 
9
  id: string;
10
  name: string;
11
  status: 'ACTIVE' | 'PENDING' | 'CONFIG_REQUIRED';
12
+ mode: 'EDTECH' | 'CRM_MARKETING' | 'PEDAGOGY' | 'CUSTOMER_SERVICE';
13
+ useCase: 'EDUCATION' | 'CRM_WHATSAPP';
14
  wabaId?: string;
15
  phoneNumbers?: { id: string, phoneNumber: string }[];
16
  lastActivity?: string;
 
43
  slug: '',
44
  adminEmail: '',
45
  adminName: '',
46
+ mode: 'PEDAGOGY' as 'CRM_MARKETING' | 'PEDAGOGY' | 'CUSTOMER_SERVICE',
47
+ useCase: 'EDUCATION' as 'EDUCATION' | 'CRM_WHATSAPP'
48
  });
49
  const [isCreating, setIsCreating] = useState(false);
50
  const [showGuide, setShowGuide] = useState(false);
 
72
  await api.post('/v1/organizations', newOrg, token!);
73
  await fetchClients();
74
  setIsModalOpen(false);
75
+ setNewOrg({ name: '', slug: '', adminEmail: '', adminName: '', mode: 'PEDAGOGY', useCase: 'EDUCATION' });
76
  } catch (error) {
77
  console.error("Failed to create organization:", error);
78
  alert("Erreur lors de la création de l'organisation. Vérifiez que le slug est unique.");
 
290
  <option value="CUSTOMER_SERVICE">Service Client IA</option>
291
  </select>
292
  </div>
293
+ <div className="col-span-2">
294
+ <label className="block text-xs font-bold uppercase text-slate-400 mb-2">Espace de travail (Workstream)</label>
295
+ <select
296
+ value={newOrg.useCase}
297
+ onChange={e => {
298
+ const val = e.target.value as any;
299
+ setNewOrg({
300
+ ...newOrg,
301
+ useCase: val,
302
+ // Sync mode for convenience if they pick CRM
303
+ mode: val === 'CRM_WHATSAPP' ? 'CRM_MARKETING' : 'PEDAGOGY'
304
+ });
305
+ }}
306
+ className="w-full border border-slate-200 rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-slate-900 transition bg-white"
307
+ >
308
+ <option value="EDUCATION">🎓 Plateforme Éducative (Modules)</option>
309
+ <option value="CRM_WHATSAPP">💬 CRM & Messages WhatsApp (Excel)</option>
310
+ </select>
311
+ </div>
312
  </div>
313
 
314
  <div className="p-6 bg-slate-50 rounded-2xl border border-slate-100 space-y-4">
apps/api/src/routes/organizations.ts CHANGED
@@ -13,7 +13,8 @@ export async function organizationRoutes(fastify: FastifyInstance) {
13
  slug: z.string().min(3).regex(/^[a-z0-9-]+$/),
14
  adminEmail: z.string().email(),
15
  adminName: z.string().min(2),
16
- mode: z.enum(['CRM_MARKETING', 'PEDAGOGY', 'CUSTOMER_SERVICE']).default('PEDAGOGY')
 
17
  });
18
 
19
  const PersonalityConfigSchema = z.object({
@@ -42,7 +43,7 @@ export async function organizationRoutes(fastify: FastifyInstance) {
42
  const body = OrganizationCreationSchema.safeParse(req.body);
43
  if (!body.success) return reply.code(400).send({ error: body.error.flatten() });
44
 
45
- const { adminEmail, adminName, slug, mode, ...orgData } = body.data;
46
 
47
  // Check if slug already exists
48
  const existing = await prisma.organization.findUnique({ where: { slug } });
@@ -53,7 +54,7 @@ export async function organizationRoutes(fastify: FastifyInstance) {
53
  // Use a transaction to ensure Org, Admin and Email Queueing are linked
54
  const result = await prisma.$transaction(async (tx) => {
55
  const org = await tx.organization.create({
56
- data: { ...data, slug, mode }
57
  });
58
 
59
  // Temporary password (user will reset it)
 
13
  slug: z.string().min(3).regex(/^[a-z0-9-]+$/),
14
  adminEmail: z.string().email(),
15
  adminName: z.string().min(2),
16
+ mode: z.enum(['CRM_MARKETING', 'PEDAGOGY', 'CUSTOMER_SERVICE']).default('PEDAGOGY'),
17
+ useCase: z.enum(['EDUCATION', 'CRM_WHATSAPP']).default('EDUCATION')
18
  });
19
 
20
  const PersonalityConfigSchema = z.object({
 
43
  const body = OrganizationCreationSchema.safeParse(req.body);
44
  if (!body.success) return reply.code(400).send({ error: body.error.flatten() });
45
 
46
+ const { adminEmail, adminName, slug, mode, useCase, ...orgData } = body.data;
47
 
48
  // Check if slug already exists
49
  const existing = await prisma.organization.findUnique({ where: { slug } });
 
54
  // Use a transaction to ensure Org, Admin and Email Queueing are linked
55
  const result = await prisma.$transaction(async (tx) => {
56
  const org = await tx.organization.create({
57
+ data: { ...data, slug, mode, useCase }
58
  });
59
 
60
  // Temporary password (user will reset it)
packages/database/prisma/schema.prisma CHANGED
@@ -21,6 +21,7 @@ model Organization {
21
  flowConfig Json?
22
  knowledgeBaseUrl String?
23
  mode OrganizationMode @default(EDTECH)
 
24
  webhookSecret String?
25
  webhookUrl String?
26
  openAiApiKey String?
@@ -400,6 +401,11 @@ enum OrganizationMode {
400
  CUSTOMER_SERVICE
401
  }
402
 
 
 
 
 
 
403
  enum EnrollmentStatus {
404
  ACTIVE
405
  COMPLETED
 
21
  flowConfig Json?
22
  knowledgeBaseUrl String?
23
  mode OrganizationMode @default(EDTECH)
24
+ useCase OrganizationUseCase @default(EDUCATION)
25
  webhookSecret String?
26
  webhookUrl String?
27
  openAiApiKey String?
 
401
  CUSTOMER_SERVICE
402
  }
403
 
404
+ enum OrganizationUseCase {
405
+ EDUCATION
406
+ CRM_WHATSAPP
407
+ }
408
+
409
  enum EnrollmentStatus {
410
  ACTIVE
411
  COMPLETED
packages/shared-types/src/organization.ts CHANGED
@@ -1,6 +1,9 @@
1
 
2
  import { z } from 'zod';
3
 
 
 
 
4
  export const SectorSchema = z.object({
5
  id: z.string(),
6
  label: z.string(),
@@ -27,6 +30,7 @@ export const OrganizationSchema = z.object({
27
  contactEmail: z.string().email().optional(),
28
  customPrompt: z.string().optional(),
29
  mode: z.enum(['EDTECH', 'WEBHOOK', 'AI_AGENT', 'CRM_MARKETING', 'PEDAGOGY', 'CUSTOMER_SERVICE']).optional(),
 
30
  flowConfig: FlowConfigSchema.optional(),
31
  webhookUrl: z.string().url().optional().or(z.literal('')),
32
  webhookSecret: z.string().optional(),
 
1
 
2
  import { z } from 'zod';
3
 
4
+ export const OrganizationUseCaseSchema = z.enum(['EDUCATION', 'CRM_WHATSAPP']);
5
+ export type OrganizationUseCase = z.infer<typeof OrganizationUseCaseSchema>;
6
+
7
  export const SectorSchema = z.object({
8
  id: z.string(),
9
  label: z.string(),
 
30
  contactEmail: z.string().email().optional(),
31
  customPrompt: z.string().optional(),
32
  mode: z.enum(['EDTECH', 'WEBHOOK', 'AI_AGENT', 'CRM_MARKETING', 'PEDAGOGY', 'CUSTOMER_SERVICE']).optional(),
33
+ useCase: OrganizationUseCaseSchema.default('EDUCATION').optional(),
34
  flowConfig: FlowConfigSchema.optional(),
35
  webhookUrl: z.string().url().optional().or(z.literal('')),
36
  webhookSecret: z.string().optional(),