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.
- package/bin/create-crm-tmp.js +56 -35
- package/package.json +1 -1
- package/template/README.md +230 -115
- package/template/eslint.config.mjs +13 -0
- package/template/next.config.ts +14 -0
- package/template/package.json +15 -2
- package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
- package/template/prisma/migrations/migration_lock.toml +3 -0
- package/template/prisma/schema.prisma +132 -637
- package/template/src/app/(auth)/invite/[token]/page.tsx +10 -8
- package/template/src/app/(auth)/layout.tsx +1 -1
- package/template/src/app/(auth)/reset-password/complete/page.tsx +11 -8
- package/template/src/app/(auth)/reset-password/page.tsx +4 -4
- package/template/src/app/(auth)/reset-password/verify/page.tsx +4 -4
- package/template/src/app/(auth)/signin/page.tsx +14 -6
- package/template/src/app/(dashboard)/agenda/page.tsx +2243 -988
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +18 -104
- package/template/src/app/(dashboard)/automatisation/page.tsx +10 -26
- package/template/src/app/(dashboard)/closing/page.tsx +78 -62
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +2082 -1080
- package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +46 -47
- package/template/src/app/(dashboard)/contacts/page.tsx +1062 -780
- package/template/src/app/(dashboard)/dashboard/page.tsx +533 -37
- package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
- package/template/src/app/(dashboard)/layout.tsx +6 -2
- package/template/src/app/(dashboard)/settings/page.tsx +797 -2582
- package/template/src/app/(dashboard)/templates/page.tsx +55 -54
- package/template/src/app/(dashboard)/users/list/page.tsx +51 -48
- package/template/src/app/(dashboard)/users/page.tsx +1 -1
- package/template/src/app/(dashboard)/users/permissions/page.tsx +2 -2
- package/template/src/app/(dashboard)/users/roles/page.tsx +7 -5
- package/template/src/app/api/agenda/google-events/route.ts +92 -0
- package/template/src/app/api/auth/check-active/route.ts +3 -2
- package/template/src/app/api/auth/google/route.ts +2 -1
- package/template/src/app/api/auth/google/status/route.ts +7 -31
- package/template/src/app/api/companies/[id]/activities/route.ts +1 -3
- package/template/src/app/api/companies/[id]/route.ts +1 -2
- package/template/src/app/api/companies/route.ts +42 -12
- package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +9 -31
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +14 -32
- package/template/src/app/api/contacts/[id]/files/route.ts +112 -212
- package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
- package/template/src/app/api/contacts/[id]/interactions/route.ts +16 -16
- package/template/src/app/api/contacts/[id]/kyc/route.ts +21 -11
- package/template/src/app/api/contacts/[id]/meet/route.ts +19 -2
- package/template/src/app/api/contacts/[id]/route.ts +106 -34
- package/template/src/app/api/contacts/[id]/send-email/route.ts +27 -11
- package/template/src/app/api/contacts/[id]/workflows/run/route.ts +6 -0
- package/template/src/app/api/contacts/export/route.ts +9 -13
- package/template/src/app/api/contacts/import/route.ts +55 -25
- package/template/src/app/api/contacts/import-preview/route.ts +1 -1
- package/template/src/app/api/contacts/origins/route.ts +63 -0
- package/template/src/app/api/contacts/route.ts +153 -41
- package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
- package/template/src/app/api/dashboard/widgets/[id]/route.ts +44 -0
- package/template/src/app/api/dashboard/widgets/route.ts +181 -0
- package/template/src/app/api/dev/reminders/test/route.ts +114 -0
- package/template/src/app/api/editor/upload-image/route.ts +61 -0
- package/template/src/app/api/integrations/google-sheet/jobs/[jobId]/route.ts +47 -0
- package/template/src/app/api/integrations/google-sheet/jobs/usage/route.ts +50 -0
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +24 -556
- package/template/src/app/api/jobs/google-sheet/process/route.ts +84 -0
- package/template/src/app/api/jobs/google-sheet/schedule/route.ts +50 -0
- package/template/src/app/api/reminders/clear/route.ts +120 -0
- package/template/src/app/api/reminders/clear/undo/route.ts +112 -0
- package/template/src/app/api/reminders/route.ts +164 -39
- package/template/src/app/api/reminders/state/route.ts +164 -0
- package/template/src/app/api/reset-password/request/route.ts +1 -1
- package/template/src/app/api/reset-password/verify/route.ts +1 -1
- package/template/src/app/api/send/route.ts +16 -4
- package/template/src/app/api/settings/google-ads/route.ts +14 -0
- package/template/src/app/api/settings/google-calendar/calendars/route.ts +97 -0
- package/template/src/app/api/settings/google-calendar/route.ts +124 -0
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +28 -0
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +37 -4
- package/template/src/app/api/settings/google-sheet/preview/route.ts +9 -3
- package/template/src/app/api/settings/google-sheet/route.ts +14 -0
- package/template/src/app/api/settings/integrations/logs/route.ts +93 -0
- package/template/src/app/api/settings/integrations/notifications/route.ts +67 -0
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +0 -1
- package/template/src/app/api/settings/meta-leads/route.ts +14 -2
- package/template/src/app/api/settings/smtp/route.ts +53 -6
- package/template/src/app/api/tasks/[id]/attendees/route.ts +24 -8
- package/template/src/app/api/tasks/[id]/route.ts +234 -58
- package/template/src/app/api/tasks/meet/route.ts +27 -19
- package/template/src/app/api/tasks/route.ts +62 -17
- package/template/src/app/api/users/[id]/route.ts +20 -14
- package/template/src/app/api/users/list/route.ts +57 -19
- package/template/src/app/api/webhooks/google-ads/route.ts +34 -14
- package/template/src/app/api/webhooks/meta-leads/route.ts +32 -12
- package/template/src/app/api/workflows/[id]/route.ts +0 -4
- package/template/src/app/api/workflows/process/route.ts +22 -51
- package/template/src/app/api/workflows/route.ts +0 -4
- package/template/src/app/globals.css +342 -4
- package/template/src/app/layout.tsx +11 -3
- package/template/src/app/page.tsx +1 -1
- package/template/src/components/address-autocomplete.tsx +7 -6
- package/template/src/components/config-error-alert.tsx +46 -0
- package/template/src/components/contacts/filter-bar.tsx +12 -3
- package/template/src/components/contacts/filter-builder.tsx +28 -43
- package/template/src/components/contacts/save-view-dialog.tsx +1 -1
- package/template/src/components/contacts/views-tab-bar.tsx +15 -6
- package/template/src/components/dashboard/activity-chart.tsx +41 -28
- package/template/src/components/dashboard/add-widget-dialog.tsx +157 -0
- package/template/src/components/dashboard/color-picker.tsx +64 -0
- package/template/src/components/dashboard/contacts-chart.tsx +69 -0
- package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
- package/template/src/components/dashboard/recent-activity.tsx +154 -0
- package/template/src/components/dashboard/stat-card.tsx +40 -40
- package/template/src/components/dashboard/status-distribution-chart.tsx +81 -0
- package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
- package/template/src/components/dashboard/top-contacts-list.tsx +113 -0
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +72 -81
- package/template/src/components/dashboard/widget-wrapper.tsx +36 -0
- package/template/src/components/date-picker.tsx +9 -6
- package/template/src/components/editor/upload-editor-image.ts +42 -0
- package/template/src/components/editor.tsx +161 -22
- package/template/src/components/email-template.tsx +2 -2
- package/template/src/components/global-search.tsx +30 -28
- package/template/src/components/header.tsx +178 -80
- package/template/src/components/inactive-account-guard.tsx +58 -0
- package/template/src/components/integration-notifications-listener.tsx +12 -0
- package/template/src/components/invitation-email-template.tsx +2 -2
- package/template/src/components/meet-cancellation-email-template.tsx +3 -3
- package/template/src/components/meet-confirmation-email-template.tsx +3 -3
- package/template/src/components/meet-update-email-template.tsx +3 -3
- package/template/src/components/page-header.tsx +5 -5
- package/template/src/components/protected-page.tsx +1 -1
- package/template/src/components/reset-password-email-template.tsx +2 -2
- package/template/src/components/settings/integrations/GoogleAdsIntegration.tsx +428 -0
- package/template/src/components/settings/integrations/GoogleSheetConfigMonitoringModal.tsx +680 -0
- package/template/src/components/settings/integrations/GoogleSheetIntegration.tsx +809 -0
- package/template/src/components/settings/integrations/ImportResultDialog.tsx +124 -0
- package/template/src/components/settings/integrations/IntegrationLogPanel.tsx +57 -0
- package/template/src/components/settings/integrations/IntegrationLogsTable.tsx +186 -0
- package/template/src/components/settings/integrations/MetaLeadIntegration.tsx +451 -0
- package/template/src/components/sidebar.tsx +45 -26
- package/template/src/components/skeleton.tsx +40 -43
- package/template/src/components/ui/accordion.tsx +2 -2
- package/template/src/components/ui/alert-dialog.tsx +1 -1
- package/template/src/components/ui/button.tsx +20 -9
- package/template/src/components/ui/components.tsx +1 -1
- package/template/src/components/ui/date-picker.tsx +422 -0
- package/template/src/components/ui/datetime-picker.tsx +338 -0
- package/template/src/components/ui/status-select.tsx +271 -0
- package/template/src/components/ui/tooltip.tsx +37 -0
- package/template/src/components/view-as-modal.tsx +13 -7
- package/template/src/contexts/app-toast-context.tsx +245 -57
- package/template/src/contexts/dashboard-theme-context.tsx +53 -0
- package/template/src/contexts/sidebar-context.tsx +22 -17
- package/template/src/contexts/task-reminder-context.tsx +134 -160
- package/template/src/contexts/view-as-context.tsx +33 -6
- package/template/src/hooks/use-focus-trap.ts +2 -2
- package/template/src/hooks/useIntegrationNotifications.ts +49 -0
- package/template/src/lib/auth.ts +8 -1
- package/template/src/lib/config-links.ts +14 -0
- package/template/src/lib/contact-duplicate.ts +79 -61
- package/template/src/lib/contact-interactions.ts +21 -21
- package/template/src/lib/contact-view-filters.ts +24 -64
- package/template/src/lib/contacts-list-url.ts +190 -0
- package/template/src/lib/dashboard-stats.ts +65 -7
- package/template/src/lib/dashboard-themes.ts +135 -0
- package/template/src/lib/date-utils.ts +127 -0
- package/template/src/lib/default-widgets.ts +12 -0
- package/template/src/lib/editor-html-image-dimensions.ts +172 -0
- package/template/src/lib/editor-image-limits.ts +19 -0
- package/template/src/lib/email-html-sanitize.ts +19 -0
- package/template/src/lib/encryption.ts +9 -6
- package/template/src/lib/fr-geography.ts +192 -0
- package/template/src/lib/google-calendar-agenda.ts +201 -0
- package/template/src/lib/google-calendar.ts +255 -5
- package/template/src/lib/google-sheet-sync-jobs.ts +96 -0
- package/template/src/lib/google-sheet-sync-runner.ts +514 -0
- package/template/src/lib/integration-import-log.ts +21 -0
- package/template/src/lib/permissions.ts +40 -10
- package/template/src/lib/prisma.ts +4 -1
- package/template/src/lib/qstash.ts +65 -0
- package/template/src/lib/reminder-state-server.ts +80 -0
- package/template/src/lib/reminder-state.ts +29 -0
- package/template/src/lib/supabase-storage.ts +113 -0
- package/template/src/lib/template-variables.ts +164 -23
- package/template/src/lib/utils.ts +45 -0
- package/template/src/lib/widget-registry.ts +173 -0
- package/template/src/lib/workflow-executor.ts +16 -70
- package/template/src/proxy.ts +1 -0
- package/template/vercel.json +3 -10
- package/template/skills-lock.json +0 -25
- package/template/src/components/dashboard/dashboard-content.tsx +0 -79
- package/template/src/lib/google-drive.ts +0 -1101
- 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 "+ Ajouter" 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'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
|
+
}
|