create-crm-tmp 2.0.0 → 2.1.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 (190) hide show
  1. package/bin/create-crm-tmp.js +56 -35
  2. package/package.json +1 -1
  3. package/template/README.md +230 -115
  4. package/template/eslint.config.mjs +13 -0
  5. package/template/next.config.ts +14 -0
  6. package/template/package.json +15 -2
  7. package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
  8. package/template/prisma/migrations/migration_lock.toml +3 -0
  9. package/template/prisma/schema.prisma +132 -637
  10. package/template/src/app/(auth)/invite/[token]/page.tsx +10 -8
  11. package/template/src/app/(auth)/layout.tsx +1 -1
  12. package/template/src/app/(auth)/reset-password/complete/page.tsx +11 -8
  13. package/template/src/app/(auth)/reset-password/page.tsx +4 -4
  14. package/template/src/app/(auth)/reset-password/verify/page.tsx +4 -4
  15. package/template/src/app/(auth)/signin/page.tsx +14 -6
  16. package/template/src/app/(dashboard)/agenda/page.tsx +2243 -988
  17. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +18 -104
  18. package/template/src/app/(dashboard)/automatisation/page.tsx +10 -26
  19. package/template/src/app/(dashboard)/closing/page.tsx +78 -62
  20. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +2082 -1080
  21. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +46 -47
  22. package/template/src/app/(dashboard)/contacts/page.tsx +1062 -780
  23. package/template/src/app/(dashboard)/dashboard/page.tsx +533 -37
  24. package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
  25. package/template/src/app/(dashboard)/layout.tsx +6 -2
  26. package/template/src/app/(dashboard)/settings/page.tsx +797 -2582
  27. package/template/src/app/(dashboard)/templates/page.tsx +55 -54
  28. package/template/src/app/(dashboard)/users/list/page.tsx +51 -48
  29. package/template/src/app/(dashboard)/users/page.tsx +1 -1
  30. package/template/src/app/(dashboard)/users/permissions/page.tsx +2 -2
  31. package/template/src/app/(dashboard)/users/roles/page.tsx +7 -5
  32. package/template/src/app/api/agenda/google-events/route.ts +92 -0
  33. package/template/src/app/api/auth/check-active/route.ts +3 -2
  34. package/template/src/app/api/auth/google/route.ts +2 -1
  35. package/template/src/app/api/auth/google/status/route.ts +7 -31
  36. package/template/src/app/api/companies/[id]/activities/route.ts +1 -3
  37. package/template/src/app/api/companies/[id]/route.ts +1 -2
  38. package/template/src/app/api/companies/route.ts +42 -12
  39. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +9 -31
  40. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +14 -32
  41. package/template/src/app/api/contacts/[id]/files/route.ts +112 -212
  42. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
  43. package/template/src/app/api/contacts/[id]/interactions/route.ts +16 -16
  44. package/template/src/app/api/contacts/[id]/kyc/route.ts +21 -11
  45. package/template/src/app/api/contacts/[id]/meet/route.ts +19 -2
  46. package/template/src/app/api/contacts/[id]/route.ts +106 -34
  47. package/template/src/app/api/contacts/[id]/send-email/route.ts +27 -11
  48. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +6 -0
  49. package/template/src/app/api/contacts/export/route.ts +9 -13
  50. package/template/src/app/api/contacts/import/route.ts +55 -25
  51. package/template/src/app/api/contacts/import-preview/route.ts +1 -1
  52. package/template/src/app/api/contacts/origins/route.ts +63 -0
  53. package/template/src/app/api/contacts/route.ts +153 -41
  54. package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
  55. package/template/src/app/api/dashboard/widgets/[id]/route.ts +44 -0
  56. package/template/src/app/api/dashboard/widgets/route.ts +181 -0
  57. package/template/src/app/api/dev/reminders/test/route.ts +114 -0
  58. package/template/src/app/api/editor/upload-image/route.ts +61 -0
  59. package/template/src/app/api/integrations/google-sheet/jobs/[jobId]/route.ts +47 -0
  60. package/template/src/app/api/integrations/google-sheet/jobs/usage/route.ts +50 -0
  61. package/template/src/app/api/integrations/google-sheet/sync/route.ts +24 -556
  62. package/template/src/app/api/jobs/google-sheet/process/route.ts +84 -0
  63. package/template/src/app/api/jobs/google-sheet/schedule/route.ts +50 -0
  64. package/template/src/app/api/reminders/clear/route.ts +120 -0
  65. package/template/src/app/api/reminders/clear/undo/route.ts +112 -0
  66. package/template/src/app/api/reminders/route.ts +164 -39
  67. package/template/src/app/api/reminders/state/route.ts +164 -0
  68. package/template/src/app/api/reset-password/request/route.ts +1 -1
  69. package/template/src/app/api/reset-password/verify/route.ts +1 -1
  70. package/template/src/app/api/send/route.ts +16 -4
  71. package/template/src/app/api/settings/google-ads/route.ts +14 -0
  72. package/template/src/app/api/settings/google-calendar/calendars/route.ts +97 -0
  73. package/template/src/app/api/settings/google-calendar/route.ts +124 -0
  74. package/template/src/app/api/settings/google-sheet/[id]/route.ts +28 -0
  75. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +37 -4
  76. package/template/src/app/api/settings/google-sheet/preview/route.ts +9 -3
  77. package/template/src/app/api/settings/google-sheet/route.ts +14 -0
  78. package/template/src/app/api/settings/integrations/logs/route.ts +93 -0
  79. package/template/src/app/api/settings/integrations/notifications/route.ts +67 -0
  80. package/template/src/app/api/settings/meta-leads/[id]/route.ts +0 -1
  81. package/template/src/app/api/settings/meta-leads/route.ts +14 -2
  82. package/template/src/app/api/settings/smtp/route.ts +53 -6
  83. package/template/src/app/api/tasks/[id]/attendees/route.ts +24 -8
  84. package/template/src/app/api/tasks/[id]/route.ts +234 -58
  85. package/template/src/app/api/tasks/meet/route.ts +27 -19
  86. package/template/src/app/api/tasks/route.ts +62 -17
  87. package/template/src/app/api/users/[id]/route.ts +20 -14
  88. package/template/src/app/api/users/list/route.ts +57 -19
  89. package/template/src/app/api/webhooks/google-ads/route.ts +34 -14
  90. package/template/src/app/api/webhooks/meta-leads/route.ts +32 -12
  91. package/template/src/app/api/workflows/[id]/route.ts +0 -4
  92. package/template/src/app/api/workflows/process/route.ts +22 -51
  93. package/template/src/app/api/workflows/route.ts +0 -4
  94. package/template/src/app/globals.css +342 -4
  95. package/template/src/app/layout.tsx +11 -3
  96. package/template/src/app/page.tsx +1 -1
  97. package/template/src/components/address-autocomplete.tsx +7 -6
  98. package/template/src/components/config-error-alert.tsx +46 -0
  99. package/template/src/components/contacts/filter-bar.tsx +12 -3
  100. package/template/src/components/contacts/filter-builder.tsx +28 -43
  101. package/template/src/components/contacts/save-view-dialog.tsx +1 -1
  102. package/template/src/components/contacts/views-tab-bar.tsx +15 -6
  103. package/template/src/components/dashboard/activity-chart.tsx +41 -28
  104. package/template/src/components/dashboard/add-widget-dialog.tsx +157 -0
  105. package/template/src/components/dashboard/color-picker.tsx +64 -0
  106. package/template/src/components/dashboard/contacts-chart.tsx +69 -0
  107. package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
  108. package/template/src/components/dashboard/recent-activity.tsx +154 -0
  109. package/template/src/components/dashboard/stat-card.tsx +40 -40
  110. package/template/src/components/dashboard/status-distribution-chart.tsx +81 -0
  111. package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
  112. package/template/src/components/dashboard/top-contacts-list.tsx +113 -0
  113. package/template/src/components/dashboard/upcoming-tasks-list.tsx +72 -81
  114. package/template/src/components/dashboard/widget-wrapper.tsx +36 -0
  115. package/template/src/components/date-picker.tsx +9 -6
  116. package/template/src/components/editor/upload-editor-image.ts +42 -0
  117. package/template/src/components/editor.tsx +161 -22
  118. package/template/src/components/email-template.tsx +2 -2
  119. package/template/src/components/global-search.tsx +30 -28
  120. package/template/src/components/header.tsx +178 -80
  121. package/template/src/components/inactive-account-guard.tsx +58 -0
  122. package/template/src/components/integration-notifications-listener.tsx +12 -0
  123. package/template/src/components/invitation-email-template.tsx +2 -2
  124. package/template/src/components/meet-cancellation-email-template.tsx +3 -3
  125. package/template/src/components/meet-confirmation-email-template.tsx +3 -3
  126. package/template/src/components/meet-update-email-template.tsx +3 -3
  127. package/template/src/components/page-header.tsx +5 -5
  128. package/template/src/components/protected-page.tsx +1 -1
  129. package/template/src/components/reset-password-email-template.tsx +2 -2
  130. package/template/src/components/settings/integrations/GoogleAdsIntegration.tsx +428 -0
  131. package/template/src/components/settings/integrations/GoogleSheetConfigMonitoringModal.tsx +680 -0
  132. package/template/src/components/settings/integrations/GoogleSheetIntegration.tsx +809 -0
  133. package/template/src/components/settings/integrations/ImportResultDialog.tsx +124 -0
  134. package/template/src/components/settings/integrations/IntegrationLogPanel.tsx +57 -0
  135. package/template/src/components/settings/integrations/IntegrationLogsTable.tsx +186 -0
  136. package/template/src/components/settings/integrations/MetaLeadIntegration.tsx +451 -0
  137. package/template/src/components/sidebar.tsx +45 -26
  138. package/template/src/components/skeleton.tsx +40 -43
  139. package/template/src/components/ui/accordion.tsx +2 -2
  140. package/template/src/components/ui/alert-dialog.tsx +1 -1
  141. package/template/src/components/ui/button.tsx +20 -9
  142. package/template/src/components/ui/components.tsx +1 -1
  143. package/template/src/components/ui/date-picker.tsx +422 -0
  144. package/template/src/components/ui/datetime-picker.tsx +338 -0
  145. package/template/src/components/ui/status-select.tsx +271 -0
  146. package/template/src/components/ui/tooltip.tsx +37 -0
  147. package/template/src/components/view-as-modal.tsx +13 -7
  148. package/template/src/contexts/app-toast-context.tsx +245 -57
  149. package/template/src/contexts/dashboard-theme-context.tsx +53 -0
  150. package/template/src/contexts/sidebar-context.tsx +22 -17
  151. package/template/src/contexts/task-reminder-context.tsx +134 -160
  152. package/template/src/contexts/view-as-context.tsx +33 -6
  153. package/template/src/hooks/use-focus-trap.ts +2 -2
  154. package/template/src/hooks/useIntegrationNotifications.ts +49 -0
  155. package/template/src/lib/auth.ts +8 -1
  156. package/template/src/lib/config-links.ts +14 -0
  157. package/template/src/lib/contact-duplicate.ts +79 -61
  158. package/template/src/lib/contact-interactions.ts +21 -21
  159. package/template/src/lib/contact-view-filters.ts +24 -64
  160. package/template/src/lib/contacts-list-url.ts +190 -0
  161. package/template/src/lib/dashboard-stats.ts +65 -7
  162. package/template/src/lib/dashboard-themes.ts +135 -0
  163. package/template/src/lib/date-utils.ts +127 -0
  164. package/template/src/lib/default-widgets.ts +12 -0
  165. package/template/src/lib/editor-html-image-dimensions.ts +172 -0
  166. package/template/src/lib/editor-image-limits.ts +19 -0
  167. package/template/src/lib/email-html-sanitize.ts +19 -0
  168. package/template/src/lib/encryption.ts +9 -6
  169. package/template/src/lib/fr-geography.ts +192 -0
  170. package/template/src/lib/google-calendar-agenda.ts +201 -0
  171. package/template/src/lib/google-calendar.ts +255 -5
  172. package/template/src/lib/google-sheet-sync-jobs.ts +96 -0
  173. package/template/src/lib/google-sheet-sync-runner.ts +514 -0
  174. package/template/src/lib/integration-import-log.ts +21 -0
  175. package/template/src/lib/permissions.ts +40 -10
  176. package/template/src/lib/prisma.ts +4 -1
  177. package/template/src/lib/qstash.ts +65 -0
  178. package/template/src/lib/reminder-state-server.ts +80 -0
  179. package/template/src/lib/reminder-state.ts +29 -0
  180. package/template/src/lib/supabase-storage.ts +113 -0
  181. package/template/src/lib/template-variables.ts +164 -23
  182. package/template/src/lib/utils.ts +45 -0
  183. package/template/src/lib/widget-registry.ts +173 -0
  184. package/template/src/lib/workflow-executor.ts +16 -70
  185. package/template/src/proxy.ts +1 -0
  186. package/template/vercel.json +3 -10
  187. package/template/skills-lock.json +0 -25
  188. package/template/src/components/dashboard/dashboard-content.tsx +0 -79
  189. package/template/src/lib/google-drive.ts +0 -1101
  190. package/template/src/types/yousign.ts +0 -52
@@ -0,0 +1,428 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ import { useAppToast } from '@/contexts/app-toast-context';
5
+ import { useConfirm } from '@/hooks/use-confirm';
6
+ import { StatusSelect } from '@/components/ui/status-select';
7
+ import { devToast } from '@/lib/utils';
8
+
9
+
10
+ interface GoogleAdsConfig {
11
+ id: string;
12
+ name: string;
13
+ webhookKey: string;
14
+ active: boolean;
15
+ defaultStatusId: string | null;
16
+ defaultAssignedUserId: string | null;
17
+ defaultStatus: { id: string; name: string; color: string } | null;
18
+ defaultAssignedUser: { id: string; name: string; email: string } | null;
19
+ }
20
+
21
+ interface GoogleAdsFormData {
22
+ name: string;
23
+ active: boolean;
24
+ webhookKey: string;
25
+ defaultStatusId: string | null;
26
+ defaultAssignedUserId: string | null;
27
+ }
28
+
29
+ interface GoogleAdsIntegrationProps {
30
+ statuses: Array<{ id: string; name: string; color: string }>;
31
+ users: Array<{ id: string; name: string; email: string }>;
32
+ onOpenLogs: (configId: string, configName: string) => void;
33
+ }
34
+
35
+ const defaultFormData: GoogleAdsFormData = {
36
+ name: '',
37
+ active: true,
38
+ webhookKey: '',
39
+ defaultStatusId: null,
40
+ defaultAssignedUserId: null,
41
+ };
42
+
43
+ export function GoogleAdsIntegration({
44
+ statuses,
45
+ users,
46
+ onOpenLogs,
47
+ }: Readonly<GoogleAdsIntegrationProps>) {
48
+ const toast = useAppToast();
49
+ const { confirm, ConfirmDialog } = useConfirm();
50
+
51
+ const [loading, setLoading] = useState(true);
52
+ const [saving, setSaving] = useState(false);
53
+ const [configs, setConfigs] = useState<GoogleAdsConfig[]>([]);
54
+ const [showModal, setShowModal] = useState(false);
55
+ const [editingConfig, setEditingConfig] = useState<string | null>(null);
56
+ const [formData, setFormData] = useState<GoogleAdsFormData>(defaultFormData);
57
+
58
+ const fetchConfigs = useCallback(async () => {
59
+ try {
60
+ const res = await fetch('/api/settings/google-ads');
61
+ if (res.ok) {
62
+ const data = await res.json();
63
+ setConfigs(Array.isArray(data) ? data : []);
64
+ }
65
+ } catch {
66
+ toast.error('Impossible de charger les configurations Google Ads. Veuillez rafraîchir la page.');
67
+ } finally {
68
+ setLoading(false);
69
+ }
70
+ }, [toast]);
71
+
72
+ useEffect(() => {
73
+ fetchConfigs();
74
+ }, [fetchConfigs]);
75
+
76
+ const openAddModal = () => {
77
+ setEditingConfig(null);
78
+ setFormData(defaultFormData);
79
+ setShowModal(true);
80
+ };
81
+
82
+ const openEditModal = (config: GoogleAdsConfig) => {
83
+ setEditingConfig(config.id);
84
+ setFormData({
85
+ name: config.name,
86
+ active: config.active,
87
+ webhookKey: config.webhookKey,
88
+ defaultStatusId: config.defaultStatusId,
89
+ defaultAssignedUserId: config.defaultAssignedUserId,
90
+ });
91
+ setShowModal(true);
92
+ };
93
+
94
+ const closeModal = () => {
95
+ setShowModal(false);
96
+ setEditingConfig(null);
97
+ setFormData(defaultFormData);
98
+ };
99
+
100
+ const handleSubmit = async (e: React.FormEvent) => {
101
+ e.preventDefault();
102
+ setSaving(true);
103
+
104
+ try {
105
+ const url = editingConfig
106
+ ? `/api/settings/google-ads/${editingConfig}`
107
+ : '/api/settings/google-ads';
108
+ const method = editingConfig ? 'PUT' : 'POST';
109
+
110
+ const response = await fetch(url, {
111
+ method,
112
+ headers: { 'Content-Type': 'application/json' },
113
+ body: JSON.stringify(formData),
114
+ });
115
+
116
+ const data = await response.json();
117
+
118
+ if (!response.ok) {
119
+ throw new Error(
120
+ data.error || 'Erreur lors de la sauvegarde de la configuration Google Ads',
121
+ );
122
+ }
123
+
124
+ toast.success(
125
+ editingConfig
126
+ ? 'Configuration Google Ads mise à jour avec succès'
127
+ : 'Configuration Google Ads créée avec succès',
128
+ );
129
+ closeModal();
130
+ await fetchConfigs();
131
+ } catch (error: any) {
132
+ toast.error(devToast('Impossible de sauvegarder la configuration Google Ads. Veuillez réessayer.', error));
133
+ } finally {
134
+ setSaving(false);
135
+ }
136
+ };
137
+
138
+ const handleDelete = async (id: string) => {
139
+ const confirmed = await confirm({
140
+ title: 'Supprimer la configuration Google Ads',
141
+ description: 'Êtes-vous sûr de vouloir supprimer cette configuration ?',
142
+ confirmText: 'Supprimer',
143
+ cancelText: 'Annuler',
144
+ variant: 'destructive',
145
+ });
146
+
147
+ if (!confirmed) return;
148
+
149
+ try {
150
+ const response = await fetch(`/api/settings/google-ads/${id}`, {
151
+ method: 'DELETE',
152
+ });
153
+
154
+ if (!response.ok) {
155
+ const data = await response.json();
156
+ throw new Error(data.error || 'Erreur lors de la suppression');
157
+ }
158
+
159
+ toast.success('Configuration supprimée avec succès');
160
+ await fetchConfigs();
161
+ } catch (error: any) {
162
+ toast.error(devToast('Impossible de supprimer la configuration. Veuillez réessayer.', error));
163
+ }
164
+ };
165
+
166
+ const copyWebhookUrl = (config: GoogleAdsConfig) => {
167
+ const origin = globalThis.window?.location.origin ?? '';
168
+ const url = `${origin}/api/webhooks/google-ads`;
169
+ navigator.clipboard?.writeText(url).then(() => toast.success('Lien webhook copié'));
170
+ };
171
+
172
+ return (
173
+ <>
174
+ <div className="rounded-lg bg-white p-6 shadow-sm">
175
+ <div className="flex items-center justify-between">
176
+ <div>
177
+ <h2 className="text-lg font-bold text-gray-900">Intégration Google Ads</h2>
178
+ <p className="mt-1 text-sm text-gray-600">
179
+ Recevez automatiquement les leads depuis Google Ads Lead Forms.
180
+ </p>
181
+ </div>
182
+ <button
183
+ onClick={openAddModal}
184
+ className="cursor-pointer rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
185
+ >
186
+ + Ajouter
187
+ </button>
188
+ </div>
189
+
190
+ {loading && (
191
+ <div className="mt-6 text-center text-gray-500">Chargement...</div>
192
+ )}
193
+
194
+ {!loading && configs.length === 0 && (
195
+ <div className="mt-6 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8 text-center">
196
+ <p className="text-sm text-gray-600">Aucune configuration Google Ads</p>
197
+ <p className="mt-1 text-xs text-gray-500">
198
+ Cliquez sur &quot;+ Ajouter&quot; pour créer votre première configuration
199
+ </p>
200
+ </div>
201
+ )}
202
+
203
+ {!loading && configs.length > 0 && (
204
+ <div className="mt-6 space-y-3">
205
+ {configs.map((config) => (
206
+ <div
207
+ key={config.id}
208
+ className="flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 p-4"
209
+ >
210
+ <div className="flex-1">
211
+ <div className="flex items-center gap-2">
212
+ <h3 className="font-medium text-gray-900">{config.name}</h3>
213
+ {config.active ? (
214
+ <span className="rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
215
+ Actif
216
+ </span>
217
+ ) : (
218
+ <span className="rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-800">
219
+ Inactif
220
+ </span>
221
+ )}
222
+ </div>
223
+ <p className="mt-1 text-xs text-gray-500">
224
+ Webhook Key: {config.webhookKey}
225
+ </p>
226
+ <p className="mt-0.5 text-xs text-gray-400">
227
+ Webhook :{' '}
228
+ {globalThis.window?.location.origin
229
+ ? `${globalThis.window.location.origin}/api/webhooks/google-ads`
230
+ : '/api/webhooks/google-ads'}
231
+ </p>
232
+ </div>
233
+ <div className="flex flex-wrap items-center gap-2">
234
+ <button
235
+ type="button"
236
+ onClick={() => onOpenLogs(config.id, config.name)}
237
+ className="cursor-pointer rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-100"
238
+ >
239
+ Voir les logs
240
+ </button>
241
+ <button
242
+ type="button"
243
+ onClick={() => copyWebhookUrl(config)}
244
+ className="cursor-pointer rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-100"
245
+ >
246
+ Copier le lien webhook
247
+ </button>
248
+ <button
249
+ type="button"
250
+ onClick={() => openEditModal(config)}
251
+ className="cursor-pointer rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-100"
252
+ >
253
+ Modifier
254
+ </button>
255
+ <button
256
+ type="button"
257
+ onClick={() => handleDelete(config.id)}
258
+ className="cursor-pointer rounded-lg border border-blue-300 px-3 py-1.5 text-xs font-medium text-blue-700 transition-colors hover:bg-blue-50"
259
+ >
260
+ Supprimer
261
+ </button>
262
+ </div>
263
+ </div>
264
+ ))}
265
+ </div>
266
+ )}
267
+ </div>
268
+
269
+ {showModal && (
270
+ <div className="ui-fade-in fixed inset-0 z-50 flex min-h-dvh items-center justify-center bg-gray-500/20 p-4 backdrop-blur-sm sm:p-6">
271
+ <div className="ui-scale-in flex max-h-[90vh] w-full max-w-2xl flex-col rounded-lg bg-white p-6 shadow-xl sm:p-8 overscroll-contain">
272
+ <div className="shrink-0 border-b border-gray-100 pb-4">
273
+ <div className="flex items-center justify-between">
274
+ <h2 className="text-xl font-bold text-gray-900 sm:text-2xl">
275
+ {editingConfig ? 'Modifier' : 'Ajouter'} une configuration Google Ads
276
+ </h2>
277
+ <button
278
+ type="button"
279
+ onClick={closeModal}
280
+ className="cursor-pointer rounded-lg p-2 text-gray-400 transition-colors hover:bg-gray-100"
281
+ >
282
+ <svg
283
+ className="h-6 w-6"
284
+ fill="none"
285
+ stroke="currentColor"
286
+ viewBox="0 0 24 24"
287
+ >
288
+ <path
289
+ strokeLinecap="round"
290
+ strokeLinejoin="round"
291
+ strokeWidth={2}
292
+ d="M6 18L18 6M6 6l12 12"
293
+ />
294
+ </svg>
295
+ </button>
296
+ </div>
297
+ </div>
298
+
299
+ <form
300
+ id="google-ads-form"
301
+ onSubmit={handleSubmit}
302
+ className="flex-1 space-y-4 overflow-y-auto pt-4"
303
+ >
304
+ <div>
305
+ <label htmlFor="gads-name" className="block text-sm font-medium text-gray-700">
306
+ Nom de la configuration *
307
+ </label>
308
+ <input
309
+ id="gads-name"
310
+ type="text"
311
+ required
312
+ value={formData.name}
313
+ onChange={(e) =>
314
+ setFormData((prev) => ({ ...prev, name: e.target.value }))
315
+ }
316
+ className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
317
+ placeholder="Ex: Google Ads Lead Forms"
318
+ />
319
+ </div>
320
+
321
+ <div className="flex items-center">
322
+ <input
323
+ id="google-ads-active"
324
+ type="checkbox"
325
+ checked={formData.active}
326
+ onChange={(e) =>
327
+ setFormData((prev) => ({ ...prev, active: e.target.checked }))
328
+ }
329
+ className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-gray-400/30"
330
+ />
331
+ <label
332
+ htmlFor="google-ads-active"
333
+ className="ml-2 text-sm font-medium text-gray-700"
334
+ >
335
+ Activer l&apos;intégration Google Ads
336
+ </label>
337
+ </div>
338
+
339
+ <div>
340
+ <label htmlFor="gads-webhook-key" className="block text-sm font-medium text-gray-700">
341
+ Webhook Key *
342
+ </label>
343
+ <input
344
+ id="gads-webhook-key"
345
+ type="text"
346
+ required
347
+ value={formData.webhookKey}
348
+ onChange={(e) =>
349
+ setFormData((prev) => ({ ...prev, webhookKey: e.target.value }))
350
+ }
351
+ className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
352
+ placeholder="Webhook Key"
353
+ />
354
+ </div>
355
+
356
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
357
+ <div>
358
+ <label htmlFor="gads-assigned-user" className="block text-sm font-medium text-gray-700">
359
+ Utilisateur assigné par défaut (optionnel)
360
+ </label>
361
+ <select
362
+ id="gads-assigned-user"
363
+ value={formData.defaultAssignedUserId || ''}
364
+ onChange={(e) =>
365
+ setFormData((prev) => ({
366
+ ...prev,
367
+ defaultAssignedUserId: e.target.value || null,
368
+ }))
369
+ }
370
+ className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
371
+ >
372
+ <option value="">Aucun utilisateur par défaut</option>
373
+ {users.map((user) => (
374
+ <option key={user.id} value={user.id}>
375
+ {user.name} ({user.email})
376
+ </option>
377
+ ))}
378
+ </select>
379
+ </div>
380
+
381
+ <div>
382
+ <label htmlFor="gads-default-status" className="block text-sm font-medium text-gray-700">
383
+ Statut par défaut
384
+ </label>
385
+ <StatusSelect
386
+ id="gads-default-status"
387
+ statuses={statuses}
388
+ value={formData.defaultStatusId || ''}
389
+ onChange={(v) =>
390
+ setFormData((prev) => ({
391
+ ...prev,
392
+ defaultStatusId: v || null,
393
+ }))
394
+ }
395
+ placeholder="Aucun statut par défaut"
396
+ className="mt-1"
397
+ />
398
+ </div>
399
+ </div>
400
+ </form>
401
+
402
+ <div className="shrink-0 border-t border-gray-100 pt-4">
403
+ <div className="flex flex-col gap-3 sm:flex-row sm:justify-end">
404
+ <button
405
+ type="button"
406
+ onClick={closeModal}
407
+ className="w-full cursor-pointer rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 sm:w-auto"
408
+ >
409
+ Annuler
410
+ </button>
411
+ <button
412
+ type="submit"
413
+ form="google-ads-form"
414
+ disabled={saving}
415
+ className="w-full cursor-pointer rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto"
416
+ >
417
+ {saving ? 'Enregistrement...' : 'Enregistrer'}
418
+ </button>
419
+ </div>
420
+ </div>
421
+ </div>
422
+ </div>
423
+ )}
424
+
425
+ <ConfirmDialog />
426
+ </>
427
+ );
428
+ }