create-crm-tmp 1.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 (187) hide show
  1. package/bin/create-crm-tmp.js +93 -0
  2. package/package.json +25 -0
  3. package/template/.prettierignore +33 -0
  4. package/template/.prettierrc.json +25 -0
  5. package/template/README.md +173 -0
  6. package/template/eslint.config.mjs +18 -0
  7. package/template/exemple-contacts.csv +11 -0
  8. package/template/next.config.ts +8 -0
  9. package/template/package.json +64 -0
  10. package/template/postcss.config.mjs +7 -0
  11. package/template/prisma/migrations/20251126144728_init/migration.sql +78 -0
  12. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +5 -0
  13. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +19 -0
  14. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +22 -0
  15. package/template/prisma/migrations/20251128132303_add_status/migration.sql +23 -0
  16. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +75 -0
  17. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +2 -0
  18. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +45 -0
  19. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +2 -0
  20. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +27 -0
  21. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +20 -0
  22. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +18 -0
  23. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +32 -0
  24. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +20 -0
  25. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +12 -0
  26. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +21 -0
  27. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +11 -0
  28. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +12 -0
  29. package/template/prisma/migrations/20251208094843_mg/migration.sql +14 -0
  30. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +14 -0
  31. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +26 -0
  32. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +2 -0
  33. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +2 -0
  34. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +2 -0
  35. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +3 -0
  36. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +21 -0
  37. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +2 -0
  38. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +10 -0
  39. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +26 -0
  40. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +24 -0
  41. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +11 -0
  42. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +12 -0
  43. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +25 -0
  44. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +8 -0
  45. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +2 -0
  46. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +80 -0
  47. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +32 -0
  48. package/template/prisma/migrations/migration_lock.toml +3 -0
  49. package/template/prisma/schema.prisma +582 -0
  50. package/template/prisma.config.ts +14 -0
  51. package/template/src/app/(auth)/invite/[token]/page.tsx +200 -0
  52. package/template/src/app/(auth)/layout.tsx +3 -0
  53. package/template/src/app/(auth)/reset-password/complete/page.tsx +213 -0
  54. package/template/src/app/(auth)/reset-password/page.tsx +146 -0
  55. package/template/src/app/(auth)/reset-password/verify/page.tsx +183 -0
  56. package/template/src/app/(auth)/signin/page.tsx +166 -0
  57. package/template/src/app/(dashboard)/agenda/page.tsx +3051 -0
  58. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +24 -0
  59. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +905 -0
  60. package/template/src/app/(dashboard)/automatisation/new/page.tsx +20 -0
  61. package/template/src/app/(dashboard)/automatisation/page.tsx +337 -0
  62. package/template/src/app/(dashboard)/closing/page.tsx +1052 -0
  63. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6028 -0
  64. package/template/src/app/(dashboard)/contacts/page.tsx +3713 -0
  65. package/template/src/app/(dashboard)/dashboard/page.tsx +186 -0
  66. package/template/src/app/(dashboard)/layout.tsx +30 -0
  67. package/template/src/app/(dashboard)/settings/page.tsx +4070 -0
  68. package/template/src/app/(dashboard)/templates/page.tsx +567 -0
  69. package/template/src/app/(dashboard)/users/list/page.tsx +507 -0
  70. package/template/src/app/(dashboard)/users/page.tsx +457 -0
  71. package/template/src/app/(dashboard)/users/permissions/page.tsx +181 -0
  72. package/template/src/app/(dashboard)/users/roles/page.tsx +434 -0
  73. package/template/src/app/api/audit-logs/route.ts +57 -0
  74. package/template/src/app/api/auth/[...all]/route.ts +4 -0
  75. package/template/src/app/api/auth/check-active/route.ts +31 -0
  76. package/template/src/app/api/auth/google/callback/route.ts +94 -0
  77. package/template/src/app/api/auth/google/disconnect/route.ts +32 -0
  78. package/template/src/app/api/auth/google/route.ts +34 -0
  79. package/template/src/app/api/auth/google/status/route.ts +32 -0
  80. package/template/src/app/api/closing-reasons/route.ts +27 -0
  81. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +94 -0
  82. package/template/src/app/api/contacts/[id]/files/route.ts +269 -0
  83. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +91 -0
  84. package/template/src/app/api/contacts/[id]/interactions/route.ts +103 -0
  85. package/template/src/app/api/contacts/[id]/meet/route.ts +296 -0
  86. package/template/src/app/api/contacts/[id]/route.ts +322 -0
  87. package/template/src/app/api/contacts/[id]/send-email/route.ts +254 -0
  88. package/template/src/app/api/contacts/export/route.ts +270 -0
  89. package/template/src/app/api/contacts/import/route.ts +381 -0
  90. package/template/src/app/api/contacts/route.ts +283 -0
  91. package/template/src/app/api/dashboard/stats/route.ts +299 -0
  92. package/template/src/app/api/email/track/[id]/route.ts +68 -0
  93. package/template/src/app/api/integrations/google-sheet/sync/route.ts +526 -0
  94. package/template/src/app/api/invite/complete/route.ts +88 -0
  95. package/template/src/app/api/invite/validate/route.ts +55 -0
  96. package/template/src/app/api/reminders/route.ts +95 -0
  97. package/template/src/app/api/reset-password/complete/route.ts +73 -0
  98. package/template/src/app/api/reset-password/request/route.ts +84 -0
  99. package/template/src/app/api/reset-password/validate/route.ts +49 -0
  100. package/template/src/app/api/reset-password/verify/route.ts +74 -0
  101. package/template/src/app/api/roles/[id]/route.ts +183 -0
  102. package/template/src/app/api/roles/route.ts +140 -0
  103. package/template/src/app/api/send/route.ts +282 -0
  104. package/template/src/app/api/settings/change-password/route.ts +95 -0
  105. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +84 -0
  106. package/template/src/app/api/settings/closing-reasons/route.ts +74 -0
  107. package/template/src/app/api/settings/company/route.ts +121 -0
  108. package/template/src/app/api/settings/google-ads/[id]/route.ts +117 -0
  109. package/template/src/app/api/settings/google-ads/route.ts +122 -0
  110. package/template/src/app/api/settings/google-sheet/[id]/route.ts +230 -0
  111. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +196 -0
  112. package/template/src/app/api/settings/google-sheet/route.ts +254 -0
  113. package/template/src/app/api/settings/meta-leads/[id]/route.ts +123 -0
  114. package/template/src/app/api/settings/meta-leads/route.ts +132 -0
  115. package/template/src/app/api/settings/profile/route.ts +42 -0
  116. package/template/src/app/api/settings/smtp/route.ts +130 -0
  117. package/template/src/app/api/settings/smtp/test/route.ts +121 -0
  118. package/template/src/app/api/settings/statuses/[id]/route.ts +101 -0
  119. package/template/src/app/api/settings/statuses/route.ts +83 -0
  120. package/template/src/app/api/statuses/route.ts +25 -0
  121. package/template/src/app/api/tasks/[id]/attendees/route.ts +76 -0
  122. package/template/src/app/api/tasks/[id]/route.ts +728 -0
  123. package/template/src/app/api/tasks/meet/route.ts +240 -0
  124. package/template/src/app/api/tasks/route.ts +417 -0
  125. package/template/src/app/api/templates/[id]/route.ts +140 -0
  126. package/template/src/app/api/templates/route.ts +91 -0
  127. package/template/src/app/api/users/[id]/route.ts +168 -0
  128. package/template/src/app/api/users/list/route.ts +45 -0
  129. package/template/src/app/api/users/me/route.ts +48 -0
  130. package/template/src/app/api/users/route.ts +250 -0
  131. package/template/src/app/api/webhooks/google-ads/route.ts +208 -0
  132. package/template/src/app/api/webhooks/meta-leads/route.ts +258 -0
  133. package/template/src/app/api/workflows/[id]/route.ts +192 -0
  134. package/template/src/app/api/workflows/process/route.ts +293 -0
  135. package/template/src/app/api/workflows/route.ts +124 -0
  136. package/template/src/app/favicon.ico +0 -0
  137. package/template/src/app/globals.css +1416 -0
  138. package/template/src/app/layout.tsx +31 -0
  139. package/template/src/app/page.tsx +32 -0
  140. package/template/src/components/dashboard/activity-chart.tsx +67 -0
  141. package/template/src/components/dashboard/contacts-chart.tsx +63 -0
  142. package/template/src/components/dashboard/recent-activity.tsx +164 -0
  143. package/template/src/components/dashboard/sales-analytics-chart.tsx +81 -0
  144. package/template/src/components/dashboard/stat-card.tsx +61 -0
  145. package/template/src/components/dashboard/status-distribution-chart.tsx +45 -0
  146. package/template/src/components/dashboard/tasks-pie-chart.tsx +88 -0
  147. package/template/src/components/dashboard/top-contacts-list.tsx +129 -0
  148. package/template/src/components/dashboard/upcoming-tasks-list.tsx +126 -0
  149. package/template/src/components/editor.tsx +856 -0
  150. package/template/src/components/email-template.tsx +35 -0
  151. package/template/src/components/header.tsx +320 -0
  152. package/template/src/components/invitation-email-template.tsx +79 -0
  153. package/template/src/components/meet-cancellation-email-template.tsx +120 -0
  154. package/template/src/components/meet-confirmation-email-template.tsx +156 -0
  155. package/template/src/components/meet-update-email-template.tsx +209 -0
  156. package/template/src/components/page-header.tsx +61 -0
  157. package/template/src/components/reset-password-email-template.tsx +79 -0
  158. package/template/src/components/sidebar.tsx +294 -0
  159. package/template/src/components/skeleton.tsx +380 -0
  160. package/template/src/components/ui/commands.tsx +396 -0
  161. package/template/src/components/ui/components.tsx +150 -0
  162. package/template/src/components/ui/theme.tsx +5 -0
  163. package/template/src/components/view-as-banner.tsx +45 -0
  164. package/template/src/components/view-as-modal.tsx +186 -0
  165. package/template/src/contexts/mobile-menu-context.tsx +31 -0
  166. package/template/src/contexts/sidebar-context.tsx +107 -0
  167. package/template/src/contexts/task-reminder-context.tsx +239 -0
  168. package/template/src/contexts/view-as-context.tsx +84 -0
  169. package/template/src/hooks/use-user-role.ts +82 -0
  170. package/template/src/lib/audit-log.ts +45 -0
  171. package/template/src/lib/auth-client.ts +16 -0
  172. package/template/src/lib/auth.ts +35 -0
  173. package/template/src/lib/check-permission.ts +193 -0
  174. package/template/src/lib/contact-duplicate.ts +112 -0
  175. package/template/src/lib/contact-interactions.ts +371 -0
  176. package/template/src/lib/encryption.ts +99 -0
  177. package/template/src/lib/google-calendar.ts +300 -0
  178. package/template/src/lib/google-drive.ts +372 -0
  179. package/template/src/lib/permissions.ts +412 -0
  180. package/template/src/lib/prisma.ts +32 -0
  181. package/template/src/lib/roles.ts +120 -0
  182. package/template/src/lib/template-variables.ts +76 -0
  183. package/template/src/lib/utils.ts +46 -0
  184. package/template/src/lib/workflow-executor.ts +482 -0
  185. package/template/src/proxy.ts +91 -0
  186. package/template/tsconfig.json +34 -0
  187. package/template/vercel.json +8 -0
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+
3
+ import { PageHeader } from '@/components/page-header';
4
+ import { WorkflowEditor } from '../_components/workflow-editor';
5
+
6
+ export default function NewWorkflowPage() {
7
+ return (
8
+ <div className="h-full">
9
+ <PageHeader
10
+ title="Nouvelle automatisation"
11
+ description="Créez un nouveau workflow d'automatisation étape par étape."
12
+ />
13
+ <div className="p-4 sm:p-6 lg:p-8">
14
+ <WorkflowEditor />
15
+ </div>
16
+ </div>
17
+ );
18
+ }
19
+
20
+
@@ -0,0 +1,337 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import Link from 'next/link';
5
+ import { useRouter } from 'next/navigation';
6
+ import { PageHeader } from '@/components/page-header';
7
+ import { Plus, Edit, Trash2, Play, Pause, Search } from 'lucide-react';
8
+ import { cn } from '@/lib/utils';
9
+
10
+ interface Status {
11
+ id: string;
12
+ name: string;
13
+ color: string;
14
+ }
15
+
16
+ interface Workflow {
17
+ id: string;
18
+ name: string;
19
+ description: string | null;
20
+ active: boolean;
21
+ triggerType: 'CONTACT_CREATED' | 'STATUS_CHANGED' | 'TIME_BASED' | 'MANUAL';
22
+ triggerFromStatusId: string | null;
23
+ triggerToStatusId: string | null;
24
+ triggerTimeDays: number | null;
25
+ triggerTimeHours: number | null;
26
+ actions: { id: string }[];
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ }
30
+
31
+ export default function AutomatisationPage() {
32
+ const router = useRouter();
33
+ const [workflows, setWorkflows] = useState<Workflow[]>([]);
34
+ const [statuses, setStatuses] = useState<Status[]>([]);
35
+ const [loading, setLoading] = useState(true);
36
+ const [error, setError] = useState('');
37
+ const [success, setSuccess] = useState('');
38
+ const [search, setSearch] = useState('');
39
+
40
+ async function fetchWorkflows() {
41
+ try {
42
+ setLoading(true);
43
+ const response = await fetch('/api/workflows');
44
+ if (!response.ok) {
45
+ throw new Error('Erreur lors du chargement des workflows');
46
+ }
47
+ const data = await response.json();
48
+ setWorkflows(data);
49
+ } catch (err: any) {
50
+ console.error('Erreur lors du chargement des workflows:', err);
51
+ setError(err.message || 'Erreur lors du chargement des workflows');
52
+ } finally {
53
+ setLoading(false);
54
+ }
55
+ }
56
+
57
+ async function fetchStatuses() {
58
+ try {
59
+ const response = await fetch('/api/settings/statuses');
60
+ if (!response.ok) return;
61
+ const data = await response.json();
62
+ setStatuses(data);
63
+ } catch (err) {
64
+ console.error('Erreur lors du chargement des statuts:', err);
65
+ }
66
+ }
67
+
68
+ useEffect(() => {
69
+ fetchWorkflows();
70
+ fetchStatuses();
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ }, []);
73
+
74
+ const handleDelete = async (id: string) => {
75
+ if (!confirm('Voulez-vous vraiment supprimer ce workflow ?')) {
76
+ return;
77
+ }
78
+
79
+ try {
80
+ const response = await fetch(`/api/workflows/${id}`, {
81
+ method: 'DELETE',
82
+ });
83
+
84
+ if (!response.ok) {
85
+ throw new Error('Erreur lors de la suppression');
86
+ }
87
+
88
+ setSuccess('Workflow supprimé avec succès');
89
+ fetchWorkflows();
90
+ setTimeout(() => setSuccess(''), 5000);
91
+ } catch (err: any) {
92
+ setError(err.message || 'Erreur lors de la suppression');
93
+ }
94
+ };
95
+
96
+ const handleToggleActive = async (workflow: Workflow) => {
97
+ try {
98
+ const response = await fetch(`/api/workflows/${workflow.id}`, {
99
+ method: 'PUT',
100
+ headers: { 'Content-Type': 'application/json' },
101
+ body: JSON.stringify({
102
+ ...workflow,
103
+ active: !workflow.active,
104
+ actions: workflow.actions.map((action, index) => ({
105
+ ...action,
106
+ order: index,
107
+ })),
108
+ }),
109
+ });
110
+
111
+ if (!response.ok) {
112
+ throw new Error('Erreur lors de la mise à jour');
113
+ }
114
+
115
+ fetchWorkflows();
116
+ } catch (err: any) {
117
+ setError(err.message || 'Erreur lors de la mise à jour');
118
+ }
119
+ };
120
+ const getTriggerLabel = (triggerType: Workflow['triggerType']) => {
121
+ switch (triggerType) {
122
+ case 'CONTACT_CREATED':
123
+ return 'Nouveau contact créé';
124
+ case 'STATUS_CHANGED':
125
+ return 'Changement de statut';
126
+ case 'TIME_BASED':
127
+ return 'Basé sur le temps';
128
+ case 'MANUAL':
129
+ return 'Déclencheur manuel';
130
+ default:
131
+ return '';
132
+ }
133
+ };
134
+
135
+ const filteredWorkflows = workflows.filter((workflow) => {
136
+ if (!search.trim()) return true;
137
+ const term = search.toLowerCase();
138
+ return (
139
+ workflow.name.toLowerCase().includes(term) ||
140
+ (workflow.description ?? '').toLowerCase().includes(term)
141
+ );
142
+ });
143
+
144
+ return (
145
+ <div className="h-full">
146
+ <PageHeader
147
+ title="Automatisation"
148
+ description="Créez et gérez vos workflows d'automatisation"
149
+ action={
150
+ <button
151
+ onClick={() => router.push('/automatisation/new')}
152
+ className="inline-flex cursor-pointer items-center gap-2 rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none"
153
+ >
154
+ <Plus className="h-4 w-4" />
155
+ Nouveau workflow
156
+ </button>
157
+ }
158
+ />
159
+
160
+ <div className="p-4 sm:p-6 lg:p-8">
161
+ {success && (
162
+ <div className="mb-4 rounded-lg border border-green-200 bg-green-50 p-4">
163
+ <p className="text-sm font-medium text-green-800">{success}</p>
164
+ </div>
165
+ )}
166
+
167
+ {error && (
168
+ <div className="mb-4 rounded-lg border border-red-200 bg-red-50 p-4">
169
+ <p className="text-sm font-medium text-red-800">{error}</p>
170
+ </div>
171
+ )}
172
+
173
+ {loading ? (
174
+ <div className="rounded-xl bg-white p-8 text-center shadow-sm">
175
+ <p className="text-sm text-gray-500">Chargement des workflows...</p>
176
+ </div>
177
+ ) : workflows.length === 0 ? (
178
+ <div className="rounded-xl bg-white p-12 text-center shadow-sm">
179
+ <p className="text-gray-500">Aucun workflow créé pour le moment.</p>
180
+ <button
181
+ onClick={() => router.push('/automatisation/new')}
182
+ className="mt-4 inline-flex cursor-pointer items-center gap-2 rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-indigo-700"
183
+ >
184
+ <Plus className="h-4 w-4" />
185
+ Créer votre premier workflow
186
+ </button>
187
+ </div>
188
+ ) : (
189
+ <div className="rounded-xl border border-gray-200 bg-white shadow-sm">
190
+ <div className="flex flex-col gap-3 border-b border-gray-100 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-6">
191
+ <div className="flex items-center gap-2 text-sm text-gray-500">
192
+ <span className="font-medium text-gray-900">Automatisations</span>
193
+ <span className="rounded-full bg-gray-100 px-2 py-0.5 text-xs">
194
+ {workflows.length}
195
+ </span>
196
+ </div>
197
+ <div className="relative w-full max-w-xs">
198
+ <Search className="pointer-events-none absolute top-2.5 left-3 h-4 w-4 text-gray-400" />
199
+ <input
200
+ type="text"
201
+ value={search}
202
+ onChange={(e) => setSearch(e.target.value)}
203
+ placeholder="Rechercher une automatisation..."
204
+ className="w-full rounded-full border border-gray-200 bg-gray-50 py-2 pr-3 pl-9 text-sm text-gray-900 placeholder:text-gray-400 focus:border-indigo-500 focus:bg-white focus:ring-2 focus:ring-indigo-500/30 focus:outline-none"
205
+ />
206
+ </div>
207
+ </div>
208
+
209
+ <div className="overflow-x-auto">
210
+ <table className="min-w-full divide-y divide-gray-100 text-sm">
211
+ <thead className="bg-gray-50/60">
212
+ <tr>
213
+ <th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
214
+ Nom
215
+ </th>
216
+ <th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
217
+ Déclencheur
218
+ </th>
219
+ <th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
220
+ Actions
221
+ </th>
222
+ <th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
223
+ Créé le
224
+ </th>
225
+ <th className="px-6 py-3 text-center text-xs font-medium tracking-wide text-gray-500 uppercase">
226
+ Statut
227
+ </th>
228
+ <th className="px-4 py-3 text-right text-xs font-medium tracking-wide text-gray-500 uppercase">
229
+ Actions
230
+ </th>
231
+ </tr>
232
+ </thead>
233
+ <tbody className="divide-y divide-gray-100">
234
+ {filteredWorkflows.map((workflow) => (
235
+ <tr
236
+ key={workflow.id}
237
+ className="group cursor-pointer transition-colors hover:bg-indigo-50/40"
238
+ onClick={() => router.push(`/automatisation/${workflow.id}`)}
239
+ >
240
+ <td className="px-6 py-4 whitespace-nowrap">
241
+ <div className="flex flex-col">
242
+ <span className="font-medium text-gray-900">{workflow.name}</span>
243
+ {workflow.description && (
244
+ <span className="mt-0.5 line-clamp-1 text-xs text-gray-500">
245
+ {workflow.description}
246
+ </span>
247
+ )}
248
+ </div>
249
+ </td>
250
+ <td className="px-6 py-4 whitespace-nowrap text-gray-700">
251
+ <div className="flex items-center gap-2">
252
+ <span className="rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-700">
253
+ {getTriggerLabel(workflow.triggerType)}
254
+ </span>
255
+ {workflow.triggerType === 'STATUS_CHANGED' && (
256
+ <span className="text-xs text-gray-500">
257
+ {statuses.find((s) => s.id === workflow.triggerFromStatusId)?.name ??
258
+ 'Tous'}{' '}
259
+ →{' '}
260
+ {statuses.find((s) => s.id === workflow.triggerToStatusId)?.name ??
261
+ 'Tous'}
262
+ </span>
263
+ )}
264
+ </div>
265
+ </td>
266
+ <td className="px-6 py-4 whitespace-nowrap text-gray-700">
267
+ <span className="inline-flex items-center rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700">
268
+ {workflow.actions.length} action(s)
269
+ </span>
270
+ </td>
271
+ <td className="px-6 py-4 text-xs whitespace-nowrap text-gray-500">
272
+ {new Date(workflow.createdAt).toLocaleDateString()}
273
+ </td>
274
+ <td
275
+ className="px-6 py-4 text-center whitespace-nowrap"
276
+ onClick={(e) => {
277
+ // Empêche la navigation vers la page de détail
278
+ e.stopPropagation();
279
+ }}
280
+ >
281
+ <div className="flex items-center justify-center gap-2">
282
+ <button
283
+ type="button"
284
+ aria-label={
285
+ workflow.active ? 'Désactiver le workflow' : 'Activer le workflow'
286
+ }
287
+ onClick={() => handleToggleActive(workflow)}
288
+ className={cn(
289
+ 'relative inline-flex h-5 w-9 cursor-pointer items-center rounded-full border transition-colors',
290
+ workflow.active
291
+ ? 'border-green-500 bg-green-500'
292
+ : 'border-gray-300 bg-gray-200',
293
+ )}
294
+ >
295
+ <span
296
+ className={cn(
297
+ 'inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform',
298
+ workflow.active ? 'translate-x-4' : 'translate-x-1',
299
+ )}
300
+ />
301
+ </button>
302
+ <span
303
+ className={cn(
304
+ 'text-xs font-medium',
305
+ workflow.active ? 'text-green-600' : 'text-gray-500',
306
+ )}
307
+ >
308
+ {workflow.active ? 'Actif' : 'Inactif'}
309
+ </span>
310
+ </div>
311
+ </td>
312
+ <td
313
+ className="px-4 py-4 text-right whitespace-nowrap"
314
+ onClick={(e) => e.stopPropagation()}
315
+ >
316
+ <div className="flex items-center justify-end gap-1">
317
+ <button
318
+ type="button"
319
+ onClick={() => handleDelete(workflow.id)}
320
+ className="inline-flex cursor-pointer items-center rounded-lg px-2 py-1 text-xs text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600"
321
+ >
322
+ <Trash2 className="mr-1 h-4 w-4" />
323
+ Supprimer
324
+ </button>
325
+ </div>
326
+ </td>
327
+ </tr>
328
+ ))}
329
+ </tbody>
330
+ </table>
331
+ </div>
332
+ </div>
333
+ )}
334
+ </div>
335
+ </div>
336
+ );
337
+ }