create-crm-tmp 1.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/package.json +1 -1
  2. package/template/.prettierignore +2 -0
  3. package/template/README.md +53 -67
  4. package/template/components.json +22 -0
  5. package/template/exemple-contacts.csv +54 -0
  6. package/template/next.config.ts +27 -1
  7. package/template/package.json +64 -27
  8. package/template/prisma/schema.prisma +821 -72
  9. package/template/skills-lock.json +25 -0
  10. package/template/src/app/(auth)/invite/[token]/page.tsx +21 -24
  11. package/template/src/app/(auth)/reset-password/complete/page.tsx +12 -21
  12. package/template/src/app/(auth)/reset-password/page.tsx +12 -8
  13. package/template/src/app/(auth)/reset-password/verify/page.tsx +12 -8
  14. package/template/src/app/(auth)/signin/page.tsx +20 -17
  15. package/template/src/app/(dashboard)/agenda/page.tsx +2231 -2188
  16. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
  17. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +680 -323
  18. package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
  19. package/template/src/app/(dashboard)/automatisation/page.tsx +473 -180
  20. package/template/src/app/(dashboard)/closing/page.tsx +500 -468
  21. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +5035 -4126
  22. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1703 -0
  23. package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
  24. package/template/src/app/(dashboard)/contacts/page.tsx +3776 -2064
  25. package/template/src/app/(dashboard)/dashboard/page.tsx +37 -519
  26. package/template/src/app/(dashboard)/error.tsx +37 -0
  27. package/template/src/app/(dashboard)/layout.tsx +1 -1
  28. package/template/src/app/(dashboard)/loading.tsx +5 -0
  29. package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
  30. package/template/src/app/(dashboard)/settings/page.tsx +2685 -2489
  31. package/template/src/app/(dashboard)/templates/page.tsx +500 -300
  32. package/template/src/app/(dashboard)/users/list/page.tsx +356 -350
  33. package/template/src/app/(dashboard)/users/page.tsx +279 -310
  34. package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
  35. package/template/src/app/(dashboard)/users/roles/page.tsx +164 -137
  36. package/template/src/app/api/audit-logs/route.ts +1 -1
  37. package/template/src/app/api/auth/google/callback/route.ts +8 -5
  38. package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
  39. package/template/src/app/api/companies/[id]/activities/route.ts +131 -0
  40. package/template/src/app/api/companies/[id]/route.ts +195 -0
  41. package/template/src/app/api/companies/export/route.ts +206 -0
  42. package/template/src/app/api/companies/route.ts +166 -0
  43. package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
  44. package/template/src/app/api/contact-views/[id]/route.ts +197 -0
  45. package/template/src/app/api/contact-views/route.ts +146 -0
  46. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +77 -0
  47. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +7 -17
  48. package/template/src/app/api/contacts/[id]/files/route.ts +83 -44
  49. package/template/src/app/api/contacts/[id]/interactions/route.ts +37 -0
  50. package/template/src/app/api/contacts/[id]/kyc/route.ts +71 -0
  51. package/template/src/app/api/contacts/[id]/meet/route.ts +38 -29
  52. package/template/src/app/api/contacts/[id]/route.ts +111 -20
  53. package/template/src/app/api/contacts/[id]/send-email/route.ts +6 -0
  54. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +61 -0
  55. package/template/src/app/api/contacts/export/route.ts +12 -17
  56. package/template/src/app/api/contacts/import/route.ts +22 -19
  57. package/template/src/app/api/contacts/import-preview/route.ts +139 -0
  58. package/template/src/app/api/contacts/route.ts +202 -49
  59. package/template/src/app/api/dashboard/stats/route.ts +9 -292
  60. package/template/src/app/api/integrations/google-sheet/sync/route.ts +203 -185
  61. package/template/src/app/api/invite/complete/route.ts +20 -23
  62. package/template/src/app/api/reminders/route.ts +1 -0
  63. package/template/src/app/api/reset-password/complete/route.ts +11 -13
  64. package/template/src/app/api/send/route.ts +9 -85
  65. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
  66. package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
  67. package/template/src/app/api/settings/company/route.ts +19 -26
  68. package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
  69. package/template/src/app/api/settings/google-ads/route.ts +20 -23
  70. package/template/src/app/api/settings/google-sheet/[id]/route.ts +20 -23
  71. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +23 -32
  72. package/template/src/app/api/settings/google-sheet/preview/route.ts +104 -0
  73. package/template/src/app/api/settings/google-sheet/route.ts +20 -23
  74. package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -23
  75. package/template/src/app/api/settings/meta-leads/route.ts +20 -23
  76. package/template/src/app/api/settings/statuses/[id]/route.ts +33 -23
  77. package/template/src/app/api/settings/statuses/route.ts +24 -22
  78. package/template/src/app/api/statuses/route.ts +2 -5
  79. package/template/src/app/api/tasks/[id]/attendees/route.ts +14 -7
  80. package/template/src/app/api/tasks/[id]/route.ts +161 -137
  81. package/template/src/app/api/tasks/meet/route.ts +11 -8
  82. package/template/src/app/api/tasks/route.ts +155 -95
  83. package/template/src/app/api/templates/[id]/route.ts +22 -13
  84. package/template/src/app/api/templates/route.ts +22 -5
  85. package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
  86. package/template/src/app/api/users/[id]/route.ts +16 -1
  87. package/template/src/app/api/users/commercials/route.ts +38 -0
  88. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  89. package/template/src/app/api/users/route.ts +94 -55
  90. package/template/src/app/api/webhooks/google-ads/route.ts +20 -1
  91. package/template/src/app/api/webhooks/meta-leads/route.ts +18 -1
  92. package/template/src/app/api/workflows/[id]/route.ts +33 -6
  93. package/template/src/app/api/workflows/process/route.ts +509 -146
  94. package/template/src/app/api/workflows/route.ts +46 -4
  95. package/template/src/app/globals.css +210 -101
  96. package/template/src/app/layout.tsx +19 -8
  97. package/template/src/app/page.tsx +37 -7
  98. package/template/src/components/address-autocomplete.tsx +232 -0
  99. package/template/src/components/contacts/filter-bar.tsx +181 -0
  100. package/template/src/components/contacts/filter-builder.tsx +589 -0
  101. package/template/src/components/contacts/save-view-dialog.tsx +160 -0
  102. package/template/src/components/contacts/views-tab-bar.tsx +440 -0
  103. package/template/src/components/dashboard/activity-chart.tsx +31 -39
  104. package/template/src/components/dashboard/dashboard-content.tsx +79 -0
  105. package/template/src/components/dashboard/stat-card.tsx +40 -42
  106. package/template/src/components/dashboard/tasks-pie-chart.tsx +34 -37
  107. package/template/src/components/dashboard/upcoming-tasks-list.tsx +78 -72
  108. package/template/src/components/date-picker.tsx +396 -0
  109. package/template/src/components/editor.tsx +27 -13
  110. package/template/src/components/email-template.tsx +4 -2
  111. package/template/src/components/global-search.tsx +358 -0
  112. package/template/src/components/header.tsx +57 -62
  113. package/template/src/components/invitation-email-template.tsx +4 -2
  114. package/template/src/components/lazy-editor.tsx +11 -0
  115. package/template/src/components/meet-cancellation-email-template.tsx +11 -3
  116. package/template/src/components/meet-confirmation-email-template.tsx +10 -3
  117. package/template/src/components/meet-update-email-template.tsx +10 -3
  118. package/template/src/components/page-header.tsx +19 -15
  119. package/template/src/components/protected-page.tsx +94 -0
  120. package/template/src/components/reset-password-email-template.tsx +4 -2
  121. package/template/src/components/sidebar.tsx +92 -94
  122. package/template/src/components/skeleton.tsx +128 -42
  123. package/template/src/components/ui/accordion.tsx +64 -0
  124. package/template/src/components/ui/alert-dialog.tsx +139 -0
  125. package/template/src/components/ui/button.tsx +60 -0
  126. package/template/src/components/view-as-banner.tsx +1 -1
  127. package/template/src/components/view-as-modal.tsx +21 -16
  128. package/template/src/config/nav-pages.ts +108 -0
  129. package/template/src/contexts/app-toast-context.tsx +174 -0
  130. package/template/src/contexts/sidebar-context.tsx +16 -47
  131. package/template/src/contexts/task-reminder-context.tsx +6 -6
  132. package/template/src/contexts/view-as-context.tsx +11 -16
  133. package/template/src/hooks/use-alert.tsx +65 -0
  134. package/template/src/hooks/use-confirm.tsx +87 -0
  135. package/template/src/hooks/use-contact-views.ts +140 -0
  136. package/template/src/hooks/use-contacts.ts +69 -0
  137. package/template/src/hooks/use-fetch.ts +17 -0
  138. package/template/src/hooks/use-focus-trap.ts +73 -0
  139. package/template/src/hooks/use-statuses.ts +22 -0
  140. package/template/src/lib/address-api.ts +155 -0
  141. package/template/src/lib/cache.ts +73 -0
  142. package/template/src/lib/check-permission.ts +12 -177
  143. package/template/src/lib/contact-interactions.ts +3 -1
  144. package/template/src/lib/contact-view-filters.ts +341 -0
  145. package/template/src/lib/dashboard-stats.ts +224 -0
  146. package/template/src/lib/date-utils.ts +49 -0
  147. package/template/src/lib/get-auth-user.ts +25 -0
  148. package/template/src/lib/google-calendar.ts +54 -12
  149. package/template/src/lib/google-drive.ts +796 -75
  150. package/template/src/lib/google-fetch.ts +63 -0
  151. package/template/src/lib/local-storage.ts +34 -0
  152. package/template/src/lib/permissions.ts +245 -47
  153. package/template/src/lib/prisma.ts +11 -11
  154. package/template/src/lib/roles.ts +14 -39
  155. package/template/src/lib/template-variables.ts +67 -33
  156. package/template/src/lib/utils.ts +26 -2
  157. package/template/src/lib/workflow-executor.ts +445 -229
  158. package/template/src/proxy.ts +34 -73
  159. package/template/src/types/contact-views.ts +351 -0
  160. package/template/src/types/yousign.ts +52 -0
  161. package/template/vercel.json +12 -0
  162. package/template/WORKFLOWS_CRON.md +0 -185
  163. package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
  164. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
  165. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
  166. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
  167. package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
  168. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
  169. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
  170. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
  171. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
  172. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
  173. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
  174. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
  175. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
  176. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
  177. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
  178. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
  179. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
  180. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
  181. package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
  182. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
  183. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
  184. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
  185. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
  186. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
  187. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
  188. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
  189. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
  190. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
  191. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
  192. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
  193. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
  194. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
  195. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
  196. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
  197. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
  198. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
  199. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
  200. package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
  201. package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
  202. package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
  203. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
  204. package/template/prisma/migrations/migration_lock.toml +0 -3
  205. package/template/src/app/(dashboard)/users/layout.tsx +0 -30
  206. package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
  207. package/template/src/app/api/dashboard/widgets/route.ts +0 -181
  208. package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
  209. package/template/src/components/dashboard/color-picker.tsx +0 -65
  210. package/template/src/components/dashboard/contacts-chart.tsx +0 -69
  211. package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
  212. package/template/src/components/dashboard/recent-activity.tsx +0 -157
  213. package/template/src/components/dashboard/sales-analytics-chart.tsx +0 -77
  214. package/template/src/components/dashboard/status-distribution-chart.tsx +0 -82
  215. package/template/src/components/dashboard/top-contacts-list.tsx +0 -119
  216. package/template/src/components/dashboard/widget-wrapper.tsx +0 -39
  217. package/template/src/contexts/dashboard-theme-context.tsx +0 -58
  218. package/template/src/lib/dashboard-themes.ts +0 -140
  219. package/template/src/lib/default-widgets.ts +0 -14
  220. package/template/src/lib/widget-registry.ts +0 -177
@@ -2,8 +2,20 @@
2
2
 
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import { useRouter } from 'next/navigation';
5
- import { Plus, Trash2, Mail, MessageSquare, Tag, CheckSquare, Clock } from 'lucide-react';
5
+ import {
6
+ Plus,
7
+ Trash2,
8
+ Mail,
9
+ MessageSquare,
10
+ Tag,
11
+ CheckSquare,
12
+ Clock,
13
+ UserPlus,
14
+ StickyNote,
15
+ Bell,
16
+ } from 'lucide-react';
6
17
  import { cn } from '@/lib/utils';
18
+ import { useAppToast } from '@/contexts/app-toast-context';
7
19
 
8
20
  interface Status {
9
21
  id: string;
@@ -17,9 +29,35 @@ interface Template {
17
29
  type: 'EMAIL' | 'SMS' | 'NOTE';
18
30
  }
19
31
 
32
+ interface UserOption {
33
+ id: string;
34
+ name: string;
35
+ role: string;
36
+ }
37
+
38
+ type ActionType =
39
+ | 'SEND_EMAIL'
40
+ | 'SEND_SMS'
41
+ | 'CHANGE_STATUS'
42
+ | 'CREATE_TASK'
43
+ | 'WAIT'
44
+ | 'ASSIGN_CONTACT'
45
+ | 'ADD_NOTE'
46
+ | 'NOTIFY_USER';
47
+
48
+ type TriggerType =
49
+ | 'CONTACT_CREATED'
50
+ | 'STATUS_CHANGED'
51
+ | 'TIME_BASED'
52
+ | 'MANUAL'
53
+ | 'TASK_COMPLETED'
54
+ | 'TRANSACTION_CREATED'
55
+ | 'TRANSACTION_STATUS_CHANGED'
56
+ | 'CONTACT_ASSIGNMENT_CHANGED';
57
+
20
58
  export interface WorkflowAction {
21
59
  id?: string;
22
- actionType: 'SEND_EMAIL' | 'SEND_SMS' | 'CHANGE_STATUS' | 'CREATE_TASK' | 'WAIT';
60
+ actionType: ActionType;
23
61
  order: number;
24
62
  delayDays: number;
25
63
  delayHours: number;
@@ -28,8 +66,17 @@ export interface WorkflowAction {
28
66
  newStatusId?: string | null;
29
67
  taskTitle?: string | null;
30
68
  taskDescription?: string | null;
69
+ taskType?: string | null;
70
+ taskPriority?: string | null;
71
+ taskAssignedUserId?: string | null;
72
+ assignCommercialId?: string | null;
73
+ assignTeleproId?: string | null;
74
+ noteContent?: string | null;
75
+ notifyUserId?: string | null;
31
76
  conditionOperator?: 'EQUALS' | 'NOT_EQUALS' | null;
32
77
  conditionStatusId?: string | null;
78
+ conditionOrigin?: string | null;
79
+ conditionHasCompany?: boolean | null;
33
80
  }
34
81
 
35
82
  export interface WorkflowEditorInitialData {
@@ -37,11 +84,15 @@ export interface WorkflowEditorInitialData {
37
84
  name: string;
38
85
  description: string | null;
39
86
  active: boolean;
40
- triggerType: 'CONTACT_CREATED' | 'STATUS_CHANGED' | 'TIME_BASED' | 'MANUAL';
87
+ triggerType: TriggerType;
41
88
  triggerFromStatusId: string | null;
42
89
  triggerToStatusId: string | null;
43
90
  triggerTimeDays: number | null;
44
91
  triggerTimeHours: number | null;
92
+ triggerTimeReference: string | null;
93
+ triggerTaskType: string | null;
94
+ triggerTransactionFromStatus: string | null;
95
+ triggerTransactionToStatus: string | null;
45
96
  actions: WorkflowAction[];
46
97
  }
47
98
 
@@ -49,8 +100,48 @@ interface WorkflowEditorProps {
49
100
  workflowId?: string;
50
101
  }
51
102
 
103
+ const TRANSACTION_STATUSES = [
104
+ { value: 'DRAFT', label: 'Brouillon' },
105
+ { value: 'PENDING_ID_VERIFICATION', label: 'Vérification identité' },
106
+ { value: 'ID_VERIFIED', label: 'Identité vérifiée' },
107
+ { value: 'ITEMS_ENTERED', label: 'Articles saisis' },
108
+ { value: 'PENDING_SIGNATURE', label: 'En attente de signature' },
109
+ { value: 'SIGNED', label: 'Signée' },
110
+ { value: 'LOCKED', label: 'Verrouillée' },
111
+ { value: 'PAYMENT_PENDING', label: 'Paiement en attente' },
112
+ { value: 'COMPLETED', label: 'Complétée' },
113
+ { value: 'PENDING_RETRACTION', label: 'Rétractation en attente' },
114
+ { value: 'CANCELLED_RETRACTION', label: 'Rétractation annulée' },
115
+ { value: 'CANCELLED', label: 'Annulée' },
116
+ ];
117
+
118
+ const TASK_TYPES = [
119
+ { value: '', label: 'Tous les types' },
120
+ { value: 'CALL', label: 'Appel' },
121
+ { value: 'MEETING', label: 'Rendez-vous' },
122
+ { value: 'EMAIL', label: 'Email' },
123
+ { value: 'VIDEO_CONFERENCE', label: 'Visioconférence' },
124
+ { value: 'PHYSICAL_APPOINTMENT', label: 'RDV physique' },
125
+ { value: 'OTHER', label: 'Autre' },
126
+ { value: 'TASK', label: 'Tâche' },
127
+ ];
128
+
129
+ const TASK_PRIORITIES = [
130
+ { value: 'LOW', label: 'Basse' },
131
+ { value: 'MEDIUM', label: 'Moyenne' },
132
+ { value: 'HIGH', label: 'Haute' },
133
+ { value: 'URGENT', label: 'Urgente' },
134
+ ];
135
+
136
+ const TIME_REFERENCES = [
137
+ { value: 'CONTACT_CREATED_DATE', label: 'Création du contact' },
138
+ { value: 'LAST_STATUS_CHANGE', label: 'Dernier changement de statut' },
139
+ { value: 'LAST_INTERACTION', label: 'Dernière interaction' },
140
+ ];
141
+
52
142
  export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
53
143
  const router = useRouter();
144
+ const toast = useAppToast();
54
145
 
55
146
  const [loading, setLoading] = useState<boolean>(!!workflowId);
56
147
  const [saving, setSaving] = useState(false);
@@ -59,6 +150,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
59
150
 
60
151
  const [statuses, setStatuses] = useState<Status[]>([]);
61
152
  const [emailTemplates, setEmailTemplates] = useState<Template[]>([]);
153
+ const [users, setUsers] = useState<UserOption[]>([]);
62
154
 
63
155
  const [formData, setFormData] = useState<WorkflowEditorInitialData>({
64
156
  id: undefined,
@@ -70,6 +162,10 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
70
162
  triggerToStatusId: null,
71
163
  triggerTimeDays: null,
72
164
  triggerTimeHours: null,
165
+ triggerTimeReference: null,
166
+ triggerTaskType: null,
167
+ triggerTransactionFromStatus: null,
168
+ triggerTransactionToStatus: null,
73
169
  actions: [],
74
170
  });
75
171
 
@@ -80,12 +176,11 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
80
176
  useEffect(() => {
81
177
  fetchStatuses();
82
178
  fetchEmailTemplates();
179
+ fetchUsers();
83
180
  }, []);
84
181
 
85
182
  useEffect(() => {
86
- if (!workflowId) {
87
- return;
88
- }
183
+ if (!workflowId) return;
89
184
 
90
185
  const fetchWorkflow = async () => {
91
186
  try {
@@ -107,6 +202,10 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
107
202
  triggerToStatusId: data.triggerToStatusId,
108
203
  triggerTimeDays: data.triggerTimeDays,
109
204
  triggerTimeHours: data.triggerTimeHours,
205
+ triggerTimeReference: data.triggerTimeReference,
206
+ triggerTaskType: data.triggerTaskType,
207
+ triggerTransactionFromStatus: data.triggerTransactionFromStatus,
208
+ triggerTransactionToStatus: data.triggerTransactionToStatus,
110
209
  actions: data.actions,
111
210
  });
112
211
  setActions(data.actions || []);
@@ -120,7 +219,6 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
120
219
  fetchWorkflow();
121
220
  }, [workflowId]);
122
221
 
123
- // Fermer le menu d'actions en cliquant en dehors
124
222
  useEffect(() => {
125
223
  const handleClickOutside = (event: MouseEvent) => {
126
224
  if (actionMenuRef.current && !actionMenuRef.current.contains(event.target as Node)) {
@@ -137,13 +235,28 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
137
235
  };
138
236
  }, [showActionMenu]);
139
237
 
238
+ useEffect(() => {
239
+ if (!error) return;
240
+ toast.error(error);
241
+ setError('');
242
+ }, [error, toast]);
243
+
244
+ // Workflow non trouvé → toast + redirection (géré par l'effet error ci-dessus)
245
+ useEffect(() => {
246
+ if (!workflowId || loading) return;
247
+ if (error) router.replace('/automatisation');
248
+ }, [workflowId, loading, error, router]);
249
+
250
+ useEffect(() => {
251
+ if (!success) return;
252
+ toast.success(success);
253
+ setSuccess('');
254
+ }, [success, toast]);
255
+
140
256
  const fetchStatuses = async () => {
141
257
  try {
142
258
  const response = await fetch('/api/settings/statuses');
143
- if (response.ok) {
144
- const data = await response.json();
145
- setStatuses(data);
146
- }
259
+ if (response.ok) setStatuses(await response.json());
147
260
  } catch (err) {
148
261
  console.error('Erreur lors du chargement des statuts:', err);
149
262
  }
@@ -152,12 +265,21 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
152
265
  const fetchEmailTemplates = async () => {
153
266
  try {
154
267
  const response = await fetch('/api/templates?type=EMAIL');
268
+ if (response.ok) setEmailTemplates(await response.json());
269
+ } catch (err) {
270
+ console.error('Erreur lors du chargement des templates:', err);
271
+ }
272
+ };
273
+
274
+ const fetchUsers = async () => {
275
+ try {
276
+ const response = await fetch('/api/users');
155
277
  if (response.ok) {
156
278
  const data = await response.json();
157
- setEmailTemplates(data);
279
+ setUsers(data.map((u: any) => ({ id: u.id, name: u.name, role: u.role })));
158
280
  }
159
281
  } catch (err) {
160
- console.error('Erreur lors du chargement des templates:', err);
282
+ console.error('Erreur lors du chargement des utilisateurs:', err);
161
283
  }
162
284
  };
163
285
 
@@ -189,6 +311,10 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
189
311
  triggerToStatusId: formData.triggerToStatusId || null,
190
312
  triggerTimeDays: formData.triggerTimeDays || null,
191
313
  triggerTimeHours: formData.triggerTimeHours || null,
314
+ triggerTimeReference: formData.triggerTimeReference || null,
315
+ triggerTaskType: formData.triggerTaskType || null,
316
+ triggerTransactionFromStatus: formData.triggerTransactionFromStatus || null,
317
+ triggerTransactionToStatus: formData.triggerTransactionToStatus || null,
192
318
  actions: actions.map((action, index) => ({
193
319
  ...action,
194
320
  order: index,
@@ -213,7 +339,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
213
339
  }
214
340
  };
215
341
 
216
- const addAction = (actionType: WorkflowAction['actionType']) => {
342
+ const addAction = (actionType: ActionType) => {
217
343
  const newAction: WorkflowAction = {
218
344
  actionType,
219
345
  order: actions.length,
@@ -221,6 +347,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
221
347
  delayHours: 0,
222
348
  };
223
349
  setActions([...actions, newAction]);
350
+ setShowActionMenu(false);
224
351
  };
225
352
 
226
353
  const removeAction = (index: number) => {
@@ -233,24 +360,30 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
233
360
  setActions(newActions);
234
361
  };
235
362
 
236
- const getActionIcon = (actionType: WorkflowAction['actionType']) => {
363
+ const getActionIcon = (actionType: ActionType) => {
237
364
  switch (actionType) {
238
365
  case 'SEND_EMAIL':
239
- return <Mail className="h-5 w-5 text-indigo-600" />;
366
+ return <Mail className="h-5 w-5 text-blue-600" />;
240
367
  case 'SEND_SMS':
241
- return <MessageSquare className="h-5 w-5 text-indigo-600" />;
368
+ return <MessageSquare className="h-5 w-5 text-blue-600" />;
242
369
  case 'CHANGE_STATUS':
243
- return <Tag className="h-5 w-5 text-indigo-600" />;
370
+ return <Tag className="h-5 w-5 text-blue-600" />;
244
371
  case 'CREATE_TASK':
245
- return <CheckSquare className="h-5 w-5 text-indigo-600" />;
372
+ return <CheckSquare className="h-5 w-5 text-blue-600" />;
246
373
  case 'WAIT':
247
- return <Clock className="h-5 w-5 text-indigo-600" />;
374
+ return <Clock className="h-5 w-5 text-blue-600" />;
375
+ case 'ASSIGN_CONTACT':
376
+ return <UserPlus className="h-5 w-5 text-blue-600" />;
377
+ case 'ADD_NOTE':
378
+ return <StickyNote className="h-5 w-5 text-blue-600" />;
379
+ case 'NOTIFY_USER':
380
+ return <Bell className="h-5 w-5 text-blue-600" />;
248
381
  default:
249
382
  return null;
250
383
  }
251
384
  };
252
385
 
253
- const getActionLabel = (actionType: WorkflowAction['actionType']) => {
386
+ const getActionLabel = (actionType: ActionType) => {
254
387
  switch (actionType) {
255
388
  case 'SEND_EMAIL':
256
389
  return 'Envoyer un email';
@@ -262,12 +395,18 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
262
395
  return 'Créer une tâche';
263
396
  case 'WAIT':
264
397
  return 'Attendre';
398
+ case 'ASSIGN_CONTACT':
399
+ return 'Assigner le contact';
400
+ case 'ADD_NOTE':
401
+ return 'Ajouter une note';
402
+ case 'NOTIFY_USER':
403
+ return 'Notifier un utilisateur';
265
404
  default:
266
405
  return '';
267
406
  }
268
407
  };
269
408
 
270
- const getTriggerLabel = (triggerType: WorkflowEditorInitialData['triggerType']) => {
409
+ const getTriggerLabel = (triggerType: TriggerType) => {
271
410
  switch (triggerType) {
272
411
  case 'CONTACT_CREATED':
273
412
  return 'Nouveau contact créé';
@@ -277,35 +416,362 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
277
416
  return 'Basé sur le temps';
278
417
  case 'MANUAL':
279
418
  return 'Déclencheur manuel';
419
+ case 'TASK_COMPLETED':
420
+ return 'Tâche complétée';
421
+ case 'TRANSACTION_CREATED':
422
+ return 'Transaction créée';
423
+ case 'TRANSACTION_STATUS_CHANGED':
424
+ return 'Changement de statut de transaction';
425
+ case 'CONTACT_ASSIGNMENT_CHANGED':
426
+ return "Changement d'assignation";
280
427
  default:
281
428
  return '';
282
429
  }
283
430
  };
284
431
 
432
+ const selectClass =
433
+ 'mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none';
434
+ const selectClassSm =
435
+ 'mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none';
436
+ const inputClassSm = selectClassSm;
437
+
438
+ const renderActionConfig = (action: WorkflowAction, index: number) => {
439
+ switch (action.actionType) {
440
+ case 'SEND_EMAIL':
441
+ return (
442
+ <div className="mt-4">
443
+ <label className="block text-xs font-medium text-gray-700">Template email</label>
444
+ <select
445
+ value={action.emailTemplateId || ''}
446
+ onChange={(e) => updateAction(index, { emailTemplateId: e.target.value || null })}
447
+ className={selectClass}
448
+ >
449
+ <option value="">Sélectionner un template</option>
450
+ {emailTemplates.map((t) => (
451
+ <option key={t.id} value={t.id}>
452
+ {t.name}
453
+ </option>
454
+ ))}
455
+ </select>
456
+ </div>
457
+ );
458
+
459
+ case 'SEND_SMS':
460
+ return (
461
+ <div className="mt-4">
462
+ <label className="block text-xs font-medium text-gray-700">Message SMS</label>
463
+ <textarea
464
+ value={action.smsMessage || ''}
465
+ onChange={(e) => updateAction(index, { smsMessage: e.target.value })}
466
+ rows={3}
467
+ className={selectClass}
468
+ placeholder="Votre message SMS... (variables: {firstName}, {lastName}, {phone})"
469
+ />
470
+ </div>
471
+ );
472
+
473
+ case 'CHANGE_STATUS':
474
+ return (
475
+ <div className="mt-4">
476
+ <label className="block text-xs font-medium text-gray-700">Nouveau statut</label>
477
+ <select
478
+ value={action.newStatusId || ''}
479
+ onChange={(e) => updateAction(index, { newStatusId: e.target.value || null })}
480
+ className={selectClass}
481
+ >
482
+ <option value="">Sélectionner un statut</option>
483
+ {statuses.map((s) => (
484
+ <option key={s.id} value={s.id}>
485
+ {s.name}
486
+ </option>
487
+ ))}
488
+ </select>
489
+ </div>
490
+ );
491
+
492
+ case 'CREATE_TASK':
493
+ return (
494
+ <div className="mt-4 space-y-3">
495
+ <div>
496
+ <label className="block text-xs font-medium text-gray-700">Titre de la tâche</label>
497
+ <input
498
+ type="text"
499
+ value={action.taskTitle || ''}
500
+ onChange={(e) => updateAction(index, { taskTitle: e.target.value })}
501
+ className={selectClass}
502
+ placeholder="Titre de la tâche..."
503
+ />
504
+ </div>
505
+ <div>
506
+ <label className="block text-xs font-medium text-gray-700">Description</label>
507
+ <textarea
508
+ value={action.taskDescription || ''}
509
+ onChange={(e) => updateAction(index, { taskDescription: e.target.value })}
510
+ rows={2}
511
+ className={selectClass}
512
+ placeholder="Description de la tâche..."
513
+ />
514
+ </div>
515
+ <div className="grid grid-cols-3 gap-2">
516
+ <div>
517
+ <label className="block text-xs font-medium text-gray-700">Type</label>
518
+ <select
519
+ value={action.taskType || 'OTHER'}
520
+ onChange={(e) => updateAction(index, { taskType: e.target.value })}
521
+ className={selectClass}
522
+ >
523
+ {TASK_TYPES.filter((t) => t.value).map((t) => (
524
+ <option key={t.value} value={t.value}>
525
+ {t.label}
526
+ </option>
527
+ ))}
528
+ </select>
529
+ </div>
530
+ <div>
531
+ <label className="block text-xs font-medium text-gray-700">Priorité</label>
532
+ <select
533
+ value={action.taskPriority || 'MEDIUM'}
534
+ onChange={(e) => updateAction(index, { taskPriority: e.target.value })}
535
+ className={selectClass}
536
+ >
537
+ {TASK_PRIORITIES.map((p) => (
538
+ <option key={p.value} value={p.value}>
539
+ {p.label}
540
+ </option>
541
+ ))}
542
+ </select>
543
+ </div>
544
+ <div>
545
+ <label className="block text-xs font-medium text-gray-700">Assigné à</label>
546
+ <select
547
+ value={action.taskAssignedUserId || ''}
548
+ onChange={(e) =>
549
+ updateAction(index, { taskAssignedUserId: e.target.value || null })
550
+ }
551
+ className={selectClass}
552
+ >
553
+ <option value="">Propriétaire du workflow</option>
554
+ {users.map((u) => (
555
+ <option key={u.id} value={u.id}>
556
+ {u.name}
557
+ </option>
558
+ ))}
559
+ </select>
560
+ </div>
561
+ </div>
562
+ </div>
563
+ );
564
+
565
+ case 'ASSIGN_CONTACT':
566
+ return (
567
+ <div className="mt-4 grid grid-cols-2 gap-3">
568
+ <div>
569
+ <label className="block text-xs font-medium text-gray-700">Commercial</label>
570
+ <select
571
+ value={action.assignCommercialId || ''}
572
+ onChange={(e) =>
573
+ updateAction(index, { assignCommercialId: e.target.value || null })
574
+ }
575
+ className={selectClass}
576
+ >
577
+ <option value="">Ne pas modifier</option>
578
+ {users.map((u) => (
579
+ <option key={u.id} value={u.id}>
580
+ {u.name}
581
+ </option>
582
+ ))}
583
+ </select>
584
+ </div>
585
+ <div>
586
+ <label className="block text-xs font-medium text-gray-700">Télépro</label>
587
+ <select
588
+ value={action.assignTeleproId || ''}
589
+ onChange={(e) => updateAction(index, { assignTeleproId: e.target.value || null })}
590
+ className={selectClass}
591
+ >
592
+ <option value="">Ne pas modifier</option>
593
+ {users.map((u) => (
594
+ <option key={u.id} value={u.id}>
595
+ {u.name}
596
+ </option>
597
+ ))}
598
+ </select>
599
+ </div>
600
+ </div>
601
+ );
602
+
603
+ case 'ADD_NOTE':
604
+ return (
605
+ <div className="mt-4">
606
+ <label className="block text-xs font-medium text-gray-700">Contenu de la note</label>
607
+ <textarea
608
+ value={action.noteContent || ''}
609
+ onChange={(e) => updateAction(index, { noteContent: e.target.value })}
610
+ rows={3}
611
+ className={selectClass}
612
+ placeholder="Contenu de la note... (variables: {{firstName}}, {{lastName}}, etc.)"
613
+ />
614
+ </div>
615
+ );
616
+
617
+ case 'NOTIFY_USER':
618
+ return (
619
+ <div className="mt-4 space-y-3">
620
+ <div>
621
+ <label className="block text-xs font-medium text-gray-700">
622
+ Utilisateur à notifier
623
+ </label>
624
+ <select
625
+ value={action.notifyUserId || ''}
626
+ onChange={(e) => updateAction(index, { notifyUserId: e.target.value || null })}
627
+ className={selectClass}
628
+ >
629
+ <option value="">Sélectionner un utilisateur</option>
630
+ {users.map((u) => (
631
+ <option key={u.id} value={u.id}>
632
+ {u.name}
633
+ </option>
634
+ ))}
635
+ </select>
636
+ </div>
637
+ <div>
638
+ <label className="block text-xs font-medium text-gray-700">Titre de la tâche</label>
639
+ <input
640
+ type="text"
641
+ value={action.taskTitle || ''}
642
+ onChange={(e) => updateAction(index, { taskTitle: e.target.value })}
643
+ className={selectClass}
644
+ placeholder="Ex : Relancer le contact {{firstName}} {{lastName}}"
645
+ />
646
+ </div>
647
+ <div>
648
+ <label className="block text-xs font-medium text-gray-700">Description</label>
649
+ <textarea
650
+ value={action.taskDescription || ''}
651
+ onChange={(e) => updateAction(index, { taskDescription: e.target.value })}
652
+ rows={2}
653
+ className={selectClass}
654
+ placeholder="Description..."
655
+ />
656
+ </div>
657
+ </div>
658
+ );
659
+
660
+ default:
661
+ return null;
662
+ }
663
+ };
664
+
665
+ const renderConditions = (action: WorkflowAction, index: number) => (
666
+ <div className="mt-4 border-t border-dashed border-gray-200 pt-3">
667
+ <label className="block text-xs font-medium text-gray-700">Conditions (optionnel)</label>
668
+
669
+ {/* Condition sur le statut */}
670
+ <div className="mt-2 grid grid-cols-2 gap-2">
671
+ <select
672
+ value={action.conditionOperator || ''}
673
+ onChange={(e) =>
674
+ updateAction(index, {
675
+ conditionOperator: (e.target.value as 'EQUALS' | 'NOT_EQUALS') || null,
676
+ })
677
+ }
678
+ className="block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
679
+ >
680
+ <option value="">Pas de condition statut</option>
681
+ <option value="EQUALS">Statut est égal à</option>
682
+ <option value="NOT_EQUALS">Statut n&apos;est pas égal à</option>
683
+ </select>
684
+ {action.conditionOperator && (
685
+ <select
686
+ value={action.conditionStatusId || ''}
687
+ onChange={(e) => updateAction(index, { conditionStatusId: e.target.value || null })}
688
+ className="block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
689
+ >
690
+ <option value="">Sélectionner...</option>
691
+ {statuses.map((s) => (
692
+ <option key={s.id} value={s.id}>
693
+ {s.name}
694
+ </option>
695
+ ))}
696
+ </select>
697
+ )}
698
+ </div>
699
+
700
+ {/* Condition sur l'origine */}
701
+ <div className="mt-2">
702
+ <input
703
+ type="text"
704
+ value={action.conditionOrigin || ''}
705
+ onChange={(e) => updateAction(index, { conditionOrigin: e.target.value || null })}
706
+ className="block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
707
+ placeholder="Origine du contact (laisser vide pour ignorer)"
708
+ />
709
+ </div>
710
+
711
+ {/* Condition sur la société */}
712
+ <div className="mt-2">
713
+ <select
714
+ value={
715
+ action.conditionHasCompany === true
716
+ ? 'true'
717
+ : action.conditionHasCompany === false
718
+ ? 'false'
719
+ : ''
720
+ }
721
+ onChange={(e) => {
722
+ const val = e.target.value;
723
+ updateAction(index, {
724
+ conditionHasCompany: val === 'true' ? true : val === 'false' ? false : null,
725
+ });
726
+ }}
727
+ className="block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
728
+ >
729
+ <option value="">Société : indifférent</option>
730
+ <option value="true">A une société</option>
731
+ <option value="false">N&apos;a pas de société</option>
732
+ </select>
733
+ </div>
734
+ </div>
735
+ );
736
+
737
+ const actionMenuItems: Array<{ type: ActionType; label: string; icon: React.ReactNode }> = [
738
+ { type: 'SEND_EMAIL', label: 'Envoyer un email', icon: <Mail className="h-4 w-4" /> },
739
+ { type: 'SEND_SMS', label: 'Envoyer un SMS', icon: <MessageSquare className="h-4 w-4" /> },
740
+ { type: 'CHANGE_STATUS', label: 'Changer le statut', icon: <Tag className="h-4 w-4" /> },
741
+ { type: 'CREATE_TASK', label: 'Créer une tâche', icon: <CheckSquare className="h-4 w-4" /> },
742
+ {
743
+ type: 'ASSIGN_CONTACT',
744
+ label: 'Assigner le contact',
745
+ icon: <UserPlus className="h-4 w-4" />,
746
+ },
747
+ { type: 'ADD_NOTE', label: 'Ajouter une note', icon: <StickyNote className="h-4 w-4" /> },
748
+ { type: 'NOTIFY_USER', label: 'Notifier un utilisateur', icon: <Bell className="h-4 w-4" /> },
749
+ { type: 'WAIT', label: 'Attendre', icon: <Clock className="h-4 w-4" /> },
750
+ ];
751
+
285
752
  return (
286
753
  <form onSubmit={handleSubmit} className="h-full">
287
754
  <div className="grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(320px,1fr)]">
288
755
  {/* Colonne centrale : flux d'actions */}
289
756
  <div className="space-y-6">
290
- <div className="rounded-2xl border border-dashed border-indigo-200 bg-indigo-50/40 p-4 text-sm text-indigo-900">
757
+ <div className="rounded-2xl border border-dashed border-blue-200 bg-blue-50/40 p-4 text-sm text-blue-900">
291
758
  <p className="font-medium">Flux du workflow</p>
292
- <p className="mt-1 text-xs text-indigo-700">
293
- Visualisez lenchaînement du déclencheur et des actions. Ajoutez des étapes pour
759
+ <p className="mt-1 text-xs text-blue-700">
760
+ Visualisez l&apos;enchaînement du déclencheur et des actions. Ajoutez des étapes pour
294
761
  construire votre automatisation.
295
762
  </p>
296
763
  </div>
297
764
 
298
765
  <div className="relative space-y-4">
299
- {/* Ligne verticale */}
300
- <div className="pointer-events-none absolute top-0 left-6 h-full w-px bg-linear-to-b from-indigo-300 via-gray-200 to-gray-200" />
766
+ <div className="pointer-events-none absolute top-0 left-6 h-full w-px bg-linear-to-b from-blue-300 via-gray-200 to-gray-200" />
301
767
 
302
768
  {/* Déclencheur */}
303
769
  <div className="relative pl-10">
304
- <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-indigo-500 shadow-sm" />
305
- <div className="rounded-xl border border-indigo-100 bg-white px-4 py-3 shadow-sm">
770
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-blue-500 shadow-sm" />
771
+ <div className="rounded-xl border border-blue-100 bg-white px-4 py-3 shadow-sm">
306
772
  <div className="flex items-center justify-between">
307
773
  <div>
308
- <p className="text-xs font-semibold tracking-wide text-indigo-600 uppercase">
774
+ <p className="text-xs font-semibold tracking-wide text-blue-600 uppercase">
309
775
  Déclencheur
310
776
  </p>
311
777
  <p className="mt-1 text-sm font-medium text-gray-900">
@@ -331,11 +797,11 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
331
797
  ) : (
332
798
  actions.map((action, index) => (
333
799
  <div key={index} className="relative pl-10">
334
- <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-indigo-500 shadow-sm" />
800
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-blue-500 shadow-sm" />
335
801
  <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md">
336
802
  <div className="mb-3 flex items-center justify-between">
337
803
  <div className="flex items-center gap-3">
338
- <div className="flex h-8 w-8 items-center justify-center rounded-full bg-indigo-50 text-xs font-semibold text-indigo-700">
804
+ <div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-50 text-xs font-semibold text-blue-700">
339
805
  {index + 1}
340
806
  </div>
341
807
  <div>
@@ -347,7 +813,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
347
813
  </div>
348
814
  <p className="mt-0.5 text-xs text-gray-500">
349
815
  Délai de {action.delayDays || 0}j {action.delayHours || 0}h après
350
- l’étape précédente
816
+ l&apos;étape précédente
351
817
  </p>
352
818
  </div>
353
819
  </div>
@@ -363,22 +829,20 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
363
829
  <div className="grid gap-4 md:grid-cols-2">
364
830
  <div>
365
831
  <label className="block text-xs font-medium text-gray-700">
366
- Type d'action
832
+ Type d&apos;action
367
833
  </label>
368
834
  <select
369
835
  value={action.actionType}
370
836
  onChange={(e) =>
371
- updateAction(index, {
372
- actionType: e.target.value as WorkflowAction['actionType'],
373
- })
837
+ updateAction(index, { actionType: e.target.value as ActionType })
374
838
  }
375
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
839
+ className={selectClass}
376
840
  >
377
- <option value="SEND_EMAIL">Envoyer un email</option>
378
- <option value="SEND_SMS">Envoyer un SMS</option>
379
- <option value="CHANGE_STATUS">Changer le statut</option>
380
- <option value="CREATE_TASK">Créer une tâche</option>
381
- <option value="WAIT">Attendre</option>
841
+ {actionMenuItems.map((item) => (
842
+ <option key={item.type} value={item.type}>
843
+ {item.label}
844
+ </option>
845
+ ))}
382
846
  </select>
383
847
  </div>
384
848
  <div className="grid grid-cols-2 gap-2">
@@ -387,179 +851,38 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
387
851
  Délai (jours)
388
852
  </label>
389
853
  <input
390
- type="number"
391
- min="0"
854
+ type="text"
855
+ inputMode="numeric"
392
856
  value={action.delayDays}
393
- onChange={(e) =>
394
- updateAction(index, {
395
- delayDays: parseInt(e.target.value, 10) || 0,
396
- })
397
- }
398
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
857
+ onChange={(e) => {
858
+ const value = e.target.value;
859
+ if (value === '' || /^\d+$/.test(value)) {
860
+ updateAction(index, { delayDays: parseInt(value, 10) || 0 });
861
+ }
862
+ }}
863
+ className={selectClass}
399
864
  />
400
865
  </div>
401
866
  <div>
402
867
  <label className="block text-xs font-medium text-gray-700">Heures</label>
403
868
  <input
404
- type="number"
405
- min="0"
869
+ type="text"
870
+ inputMode="numeric"
406
871
  value={action.delayHours}
407
- onChange={(e) =>
408
- updateAction(index, {
409
- delayHours: parseInt(e.target.value, 10) || 0,
410
- })
411
- }
412
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
872
+ onChange={(e) => {
873
+ const value = e.target.value;
874
+ if (value === '' || /^\d+$/.test(value)) {
875
+ updateAction(index, { delayHours: parseInt(value, 10) || 0 });
876
+ }
877
+ }}
878
+ className={selectClass}
413
879
  />
414
880
  </div>
415
881
  </div>
416
882
  </div>
417
883
 
418
- {/* Config spécifique */}
419
- {action.actionType === 'SEND_EMAIL' && (
420
- <div className="mt-4">
421
- <label className="block text-xs font-medium text-gray-700">
422
- Template email
423
- </label>
424
- <select
425
- value={action.emailTemplateId || ''}
426
- onChange={(e) =>
427
- updateAction(index, {
428
- emailTemplateId: e.target.value || null,
429
- })
430
- }
431
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
432
- >
433
- <option value="">Sélectionner un template</option>
434
- {emailTemplates.map((template) => (
435
- <option key={template.id} value={template.id}>
436
- {template.name}
437
- </option>
438
- ))}
439
- </select>
440
- </div>
441
- )}
442
-
443
- {action.actionType === 'SEND_SMS' && (
444
- <div className="mt-4">
445
- <label className="block text-xs font-medium text-gray-700">
446
- Message SMS
447
- </label>
448
- <textarea
449
- value={action.smsMessage || ''}
450
- onChange={(e) =>
451
- updateAction(index, {
452
- smsMessage: e.target.value,
453
- })
454
- }
455
- rows={3}
456
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
457
- placeholder="Votre message SMS..."
458
- />
459
- </div>
460
- )}
461
-
462
- {action.actionType === 'CHANGE_STATUS' && (
463
- <div className="mt-4">
464
- <label className="block text-xs font-medium text-gray-700">
465
- Nouveau statut
466
- </label>
467
- <select
468
- value={action.newStatusId || ''}
469
- onChange={(e) =>
470
- updateAction(index, {
471
- newStatusId: e.target.value || null,
472
- })
473
- }
474
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
475
- >
476
- <option value="">Sélectionner un statut</option>
477
- {statuses.map((status) => (
478
- <option key={status.id} value={status.id}>
479
- {status.name}
480
- </option>
481
- ))}
482
- </select>
483
- </div>
484
- )}
485
-
486
- {action.actionType === 'CREATE_TASK' && (
487
- <div className="mt-4 space-y-3">
488
- <div>
489
- <label className="block text-xs font-medium text-gray-700">
490
- Titre de la tâche
491
- </label>
492
- <input
493
- type="text"
494
- value={action.taskTitle || ''}
495
- onChange={(e) =>
496
- updateAction(index, {
497
- taskTitle: e.target.value,
498
- })
499
- }
500
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
501
- placeholder="Titre de la tâche..."
502
- />
503
- </div>
504
- <div>
505
- <label className="block text-xs font-medium text-gray-700">
506
- Description
507
- </label>
508
- <textarea
509
- value={action.taskDescription || ''}
510
- onChange={(e) =>
511
- updateAction(index, {
512
- taskDescription: e.target.value,
513
- })
514
- }
515
- rows={3}
516
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
517
- placeholder="Description de la tâche..."
518
- />
519
- </div>
520
- </div>
521
- )}
522
-
523
- {/* Condition */}
524
- <div className="mt-4 border-t border-dashed border-gray-200 pt-3">
525
- <label className="block text-xs font-medium text-gray-700">
526
- Condition (optionnel)
527
- </label>
528
- <div className="mt-2 grid grid-cols-2 gap-2">
529
- <select
530
- value={action.conditionOperator || ''}
531
- onChange={(e) =>
532
- updateAction(index, {
533
- conditionOperator:
534
- (e.target.value as 'EQUALS' | 'NOT_EQUALS') || null,
535
- })
536
- }
537
- className="block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
538
- >
539
- <option value="">Aucune condition</option>
540
- <option value="EQUALS">Statut est égal à</option>
541
- <option value="NOT_EQUALS">Statut n'est pas égal à</option>
542
- </select>
543
- {action.conditionOperator && (
544
- <select
545
- value={action.conditionStatusId || ''}
546
- onChange={(e) =>
547
- updateAction(index, {
548
- conditionStatusId: e.target.value || null,
549
- })
550
- }
551
- className="block w-full rounded-lg border border-gray-300 px-3 py-2 text-xs text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
552
- >
553
- <option value="">Sélectionner...</option>
554
- {statuses.map((status) => (
555
- <option key={status.id} value={status.id}>
556
- {status.name}
557
- </option>
558
- ))}
559
- </select>
560
- )}
561
- </div>
562
- </div>
884
+ {renderActionConfig(action, index)}
885
+ {renderConditions(action, index)}
563
886
  </div>
564
887
  </div>
565
888
  ))
@@ -567,13 +890,13 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
567
890
 
568
891
  {/* Bouton ajouter une action */}
569
892
  <div className="relative pl-10">
570
- <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-dashed border-indigo-300 bg-white" />
893
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-dashed border-blue-300 bg-white" />
571
894
  <div className="flex items-center justify-center">
572
895
  <div className="relative inline-flex items-center" ref={actionMenuRef}>
573
896
  <button
574
897
  type="button"
575
898
  onClick={() => setShowActionMenu(!showActionMenu)}
576
- className="inline-flex cursor-pointer items-center gap-2 rounded-full border border-indigo-200 bg-white px-4 py-2 text-xs font-medium text-indigo-700 shadow-sm transition-colors hover:border-indigo-300 hover:bg-indigo-50"
899
+ className="inline-flex cursor-pointer items-center gap-2 rounded-full border border-blue-200 bg-white px-4 py-2 text-xs font-medium text-blue-700 shadow-sm transition-colors hover:border-blue-300 hover:bg-blue-50"
577
900
  >
578
901
  <Plus className="h-4 w-4" />
579
902
  Ajouter une action
@@ -581,63 +904,19 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
581
904
  {showActionMenu && (
582
905
  <div className="absolute top-full left-1/2 z-10 mt-2 w-60 -translate-x-1/2 rounded-xl border border-gray-200 bg-white p-2 text-xs shadow-lg">
583
906
  <p className="px-2 pb-1 text-[11px] font-medium tracking-wide text-gray-500 uppercase">
584
- Types dactions
907
+ Types d&apos;actions
585
908
  </p>
586
- <button
587
- type="button"
588
- onClick={() => {
589
- addAction('SEND_EMAIL');
590
- setShowActionMenu(false);
591
- }}
592
- className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-gray-700 transition-colors hover:bg-gray-50"
593
- >
594
- <Mail className="h-4 w-4" />
595
- Envoyer un email
596
- </button>
597
- <button
598
- type="button"
599
- onClick={() => {
600
- addAction('SEND_SMS');
601
- setShowActionMenu(false);
602
- }}
603
- className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-gray-700 transition-colors hover:bg-gray-50"
604
- >
605
- <MessageSquare className="h-4 w-4" />
606
- Envoyer un SMS
607
- </button>
608
- <button
609
- type="button"
610
- onClick={() => {
611
- addAction('CHANGE_STATUS');
612
- setShowActionMenu(false);
613
- }}
614
- className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-gray-700 transition-colors hover:bg-gray-50"
615
- >
616
- <Tag className="h-4 w-4" />
617
- Changer le statut
618
- </button>
619
- <button
620
- type="button"
621
- onClick={() => {
622
- addAction('CREATE_TASK');
623
- setShowActionMenu(false);
624
- }}
625
- className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-gray-700 transition-colors hover:bg-gray-50"
626
- >
627
- <CheckSquare className="h-4 w-4" />
628
- Créer une tâche
629
- </button>
630
- <button
631
- type="button"
632
- onClick={() => {
633
- addAction('WAIT');
634
- setShowActionMenu(false);
635
- }}
636
- className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-gray-700 transition-colors hover:bg-gray-50"
637
- >
638
- <Clock className="h-4 w-4" />
639
- Attendre
640
- </button>
909
+ {actionMenuItems.map((item) => (
910
+ <button
911
+ key={item.type}
912
+ type="button"
913
+ onClick={() => addAction(item.type)}
914
+ className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-gray-700 transition-colors hover:bg-gray-50"
915
+ >
916
+ {item.icon}
917
+ {item.label}
918
+ </button>
919
+ ))}
641
920
  </div>
642
921
  )}
643
922
  </div>
@@ -675,13 +954,8 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
675
954
  type="text"
676
955
  required
677
956
  value={formData.name}
678
- onChange={(e) =>
679
- setFormData((prev) => ({
680
- ...prev,
681
- name: e.target.value,
682
- }))
683
- }
684
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
957
+ onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
958
+ className={inputClassSm}
685
959
  placeholder="Ex : Séquence de bienvenue nouveau lead"
686
960
  />
687
961
  </div>
@@ -691,13 +965,10 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
691
965
  <textarea
692
966
  value={formData.description ?? ''}
693
967
  onChange={(e) =>
694
- setFormData((prev) => ({
695
- ...prev,
696
- description: e.target.value,
697
- }))
968
+ setFormData((prev) => ({ ...prev, description: e.target.value }))
698
969
  }
699
970
  rows={3}
700
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
971
+ className={inputClassSm}
701
972
  placeholder="Expliquez brièvement ce que fait ce workflow..."
702
973
  />
703
974
  </div>
@@ -711,16 +982,11 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
711
982
  </div>
712
983
  <button
713
984
  type="button"
714
- onClick={() =>
715
- setFormData((prev) => ({
716
- ...prev,
717
- active: !prev.active,
718
- }))
719
- }
985
+ onClick={() => setFormData((prev) => ({ ...prev, active: !prev.active }))}
720
986
  className={cn(
721
987
  'relative inline-flex h-6 w-11 cursor-pointer items-center rounded-full border transition-colors',
722
988
  formData.active
723
- ? 'border-indigo-500 bg-indigo-500'
989
+ ? 'border-blue-500 bg-blue-500'
724
990
  : 'border-gray-300 bg-gray-200',
725
991
  )}
726
992
  >
@@ -745,18 +1011,25 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
745
1011
  onChange={(e) =>
746
1012
  setFormData((prev) => ({
747
1013
  ...prev,
748
- triggerType: e.target.value as WorkflowEditorInitialData['triggerType'],
1014
+ triggerType: e.target.value as TriggerType,
749
1015
  }))
750
1016
  }
751
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
1017
+ className={selectClassSm}
752
1018
  >
753
1019
  <option value="CONTACT_CREATED">Nouveau contact créé</option>
754
1020
  <option value="STATUS_CHANGED">Changement de statut</option>
755
1021
  <option value="TIME_BASED">Basé sur le temps</option>
1022
+ <option value="TASK_COMPLETED">Tâche complétée</option>
1023
+ <option value="TRANSACTION_CREATED">Transaction créée</option>
1024
+ <option value="TRANSACTION_STATUS_CHANGED">
1025
+ Changement de statut de transaction
1026
+ </option>
1027
+ <option value="CONTACT_ASSIGNMENT_CHANGED">Changement d&apos;assignation</option>
756
1028
  <option value="MANUAL">Déclencheur manuel</option>
757
1029
  </select>
758
1030
  </div>
759
1031
 
1032
+ {/* STATUS_CHANGED config */}
760
1033
  {formData.triggerType === 'STATUS_CHANGED' && (
761
1034
  <div className="grid gap-3 md:grid-cols-2">
762
1035
  <div>
@@ -771,12 +1044,12 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
771
1044
  triggerFromStatusId: e.target.value || null,
772
1045
  }))
773
1046
  }
774
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
1047
+ className={selectClassSm}
775
1048
  >
776
1049
  <option value="">Tous les statuts</option>
777
- {statuses.map((status) => (
778
- <option key={status.id} value={status.id}>
779
- {status.name}
1050
+ {statuses.map((s) => (
1051
+ <option key={s.id} value={s.id}>
1052
+ {s.name}
780
1053
  </option>
781
1054
  ))}
782
1055
  </select>
@@ -791,12 +1064,12 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
791
1064
  triggerToStatusId: e.target.value || null,
792
1065
  }))
793
1066
  }
794
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
1067
+ className={selectClassSm}
795
1068
  >
796
1069
  <option value="">Tous les statuts</option>
797
- {statuses.map((status) => (
798
- <option key={status.id} value={status.id}>
799
- {status.name}
1070
+ {statuses.map((s) => (
1071
+ <option key={s.id} value={s.id}>
1072
+ {s.name}
800
1073
  </option>
801
1074
  ))}
802
1075
  </select>
@@ -804,57 +1077,141 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
804
1077
  </div>
805
1078
  )}
806
1079
 
1080
+ {/* TIME_BASED config */}
807
1081
  {formData.triggerType === 'TIME_BASED' && (
1082
+ <div className="space-y-3">
1083
+ <div>
1084
+ <label className="block text-xs font-medium text-gray-700">
1085
+ Événement de référence
1086
+ </label>
1087
+ <select
1088
+ value={formData.triggerTimeReference ?? 'CONTACT_CREATED_DATE'}
1089
+ onChange={(e) =>
1090
+ setFormData((prev) => ({ ...prev, triggerTimeReference: e.target.value }))
1091
+ }
1092
+ className={selectClassSm}
1093
+ >
1094
+ {TIME_REFERENCES.map((r) => (
1095
+ <option key={r.value} value={r.value}>
1096
+ {r.label}
1097
+ </option>
1098
+ ))}
1099
+ </select>
1100
+ </div>
1101
+ <div className="grid gap-3 md:grid-cols-2">
1102
+ <div>
1103
+ <label className="block text-xs font-medium text-gray-700">Délai (jours)</label>
1104
+ <input
1105
+ type="text"
1106
+ inputMode="numeric"
1107
+ value={formData.triggerTimeDays ?? 0}
1108
+ onChange={(e) => {
1109
+ const value = e.target.value;
1110
+ if (value === '' || /^\d+$/.test(value)) {
1111
+ setFormData((prev) => ({
1112
+ ...prev,
1113
+ triggerTimeDays: parseInt(value, 10) || 0,
1114
+ }));
1115
+ }
1116
+ }}
1117
+ className={inputClassSm}
1118
+ />
1119
+ </div>
1120
+ <div>
1121
+ <label className="block text-xs font-medium text-gray-700">
1122
+ Délai (heures)
1123
+ </label>
1124
+ <input
1125
+ type="text"
1126
+ inputMode="numeric"
1127
+ value={formData.triggerTimeHours ?? 0}
1128
+ onChange={(e) => {
1129
+ const value = e.target.value;
1130
+ if (value === '' || /^\d+$/.test(value)) {
1131
+ setFormData((prev) => ({
1132
+ ...prev,
1133
+ triggerTimeHours: parseInt(value, 10) || 0,
1134
+ }));
1135
+ }
1136
+ }}
1137
+ className={inputClassSm}
1138
+ />
1139
+ </div>
1140
+ </div>
1141
+ </div>
1142
+ )}
1143
+
1144
+ {/* TASK_COMPLETED config */}
1145
+ {formData.triggerType === 'TASK_COMPLETED' && (
1146
+ <div>
1147
+ <label className="block text-xs font-medium text-gray-700">
1148
+ Type de tâche (optionnel)
1149
+ </label>
1150
+ <select
1151
+ value={formData.triggerTaskType ?? ''}
1152
+ onChange={(e) =>
1153
+ setFormData((prev) => ({ ...prev, triggerTaskType: e.target.value || null }))
1154
+ }
1155
+ className={selectClassSm}
1156
+ >
1157
+ {TASK_TYPES.map((t) => (
1158
+ <option key={t.value} value={t.value}>
1159
+ {t.label}
1160
+ </option>
1161
+ ))}
1162
+ </select>
1163
+ </div>
1164
+ )}
1165
+
1166
+ {/* TRANSACTION_STATUS_CHANGED config */}
1167
+ {formData.triggerType === 'TRANSACTION_STATUS_CHANGED' && (
808
1168
  <div className="grid gap-3 md:grid-cols-2">
809
1169
  <div>
810
- <label className="block text-xs font-medium text-gray-700">Délai (jours)</label>
811
- <input
812
- type="number"
813
- min="0"
814
- value={formData.triggerTimeDays ?? 0}
1170
+ <label className="block text-xs font-medium text-gray-700">
1171
+ Du statut (optionnel)
1172
+ </label>
1173
+ <select
1174
+ value={formData.triggerTransactionFromStatus ?? ''}
815
1175
  onChange={(e) =>
816
1176
  setFormData((prev) => ({
817
1177
  ...prev,
818
- triggerTimeDays: parseInt(e.target.value, 10) || 0,
1178
+ triggerTransactionFromStatus: e.target.value || null,
819
1179
  }))
820
1180
  }
821
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
822
- />
1181
+ className={selectClassSm}
1182
+ >
1183
+ <option value="">Tous les statuts</option>
1184
+ {TRANSACTION_STATUSES.map((s) => (
1185
+ <option key={s.value} value={s.value}>
1186
+ {s.label}
1187
+ </option>
1188
+ ))}
1189
+ </select>
823
1190
  </div>
824
1191
  <div>
825
- <label className="block text-xs font-medium text-gray-700">Délai (heures)</label>
826
- <input
827
- type="number"
828
- min="0"
829
- value={formData.triggerTimeHours ?? 0}
1192
+ <label className="block text-xs font-medium text-gray-700">Vers le statut</label>
1193
+ <select
1194
+ value={formData.triggerTransactionToStatus ?? ''}
830
1195
  onChange={(e) =>
831
1196
  setFormData((prev) => ({
832
1197
  ...prev,
833
- triggerTimeHours: parseInt(e.target.value, 10) || 0,
1198
+ triggerTransactionToStatus: e.target.value || null,
834
1199
  }))
835
1200
  }
836
- className="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
837
- />
1201
+ className={selectClassSm}
1202
+ >
1203
+ <option value="">Tous les statuts</option>
1204
+ {TRANSACTION_STATUSES.map((s) => (
1205
+ <option key={s.value} value={s.value}>
1206
+ {s.label}
1207
+ </option>
1208
+ ))}
1209
+ </select>
838
1210
  </div>
839
1211
  </div>
840
1212
  )}
841
1213
  </div>
842
1214
 
843
- {(error || success) && (
844
- <div className="space-y-2 text-xs">
845
- {error && (
846
- <div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-red-700">
847
- {error}
848
- </div>
849
- )}
850
- {success && (
851
- <div className="rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-green-700">
852
- {success}
853
- </div>
854
- )}
855
- </div>
856
- )}
857
-
858
1215
  <div className="flex flex-col gap-2 pt-2 sm:flex-row sm:justify-end">
859
1216
  <button
860
1217
  type="button"
@@ -866,7 +1223,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
866
1223
  <button
867
1224
  type="submit"
868
1225
  disabled={saving || loading}
869
- className="w-full cursor-pointer rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:bg-indigo-400 sm:w-auto"
1226
+ className="w-full cursor-pointer rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-blue-700 focus:ring-2 focus:ring-gray-400/30 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:bg-blue-400 sm:w-auto"
870
1227
  >
871
1228
  {saving ? 'Enregistrement...' : 'Enregistrer le workflow'}
872
1229
  </button>