create-crm-tmp 1.1.3 → 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/.prettierignore +2 -0
- package/template/README.md +230 -115
- package/template/components.json +22 -0
- package/template/eslint.config.mjs +13 -0
- package/template/exemple-contacts.csv +54 -0
- package/template/next.config.ts +41 -1
- package/template/package.json +63 -15
- package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
- package/template/prisma/schema.prisma +311 -67
- package/template/src/app/(auth)/invite/[token]/page.tsx +28 -29
- package/template/src/app/(auth)/layout.tsx +1 -1
- package/template/src/app/(auth)/reset-password/complete/page.tsx +21 -27
- package/template/src/app/(auth)/reset-password/page.tsx +14 -10
- package/template/src/app/(auth)/reset-password/verify/page.tsx +14 -10
- package/template/src/app/(auth)/signin/page.tsx +34 -23
- package/template/src/app/(dashboard)/agenda/page.tsx +3655 -2357
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +609 -338
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
- package/template/src/app/(dashboard)/automatisation/page.tsx +463 -186
- package/template/src/app/(dashboard)/closing/page.tsx +517 -469
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6151 -4210
- package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1702 -0
- package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +4124 -2130
- package/template/src/app/(dashboard)/dashboard/page.tsx +119 -105
- package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
- package/template/src/app/(dashboard)/error.tsx +37 -0
- package/template/src/app/(dashboard)/layout.tsx +6 -2
- package/template/src/app/(dashboard)/loading.tsx +5 -0
- package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
- package/template/src/app/(dashboard)/settings/page.tsx +1773 -3362
- package/template/src/app/(dashboard)/templates/page.tsx +504 -303
- package/template/src/app/(dashboard)/users/list/page.tsx +364 -355
- package/template/src/app/(dashboard)/users/page.tsx +279 -310
- package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
- package/template/src/app/(dashboard)/users/roles/page.tsx +169 -140
- package/template/src/app/api/agenda/google-events/route.ts +92 -0
- package/template/src/app/api/audit-logs/route.ts +1 -1
- package/template/src/app/api/auth/check-active/route.ts +3 -2
- package/template/src/app/api/auth/google/callback/route.ts +8 -5
- package/template/src/app/api/auth/google/disconnect/route.ts +2 -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 +129 -0
- package/template/src/app/api/companies/[id]/route.ts +194 -0
- package/template/src/app/api/companies/export/route.ts +206 -0
- package/template/src/app/api/companies/route.ts +196 -0
- package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
- package/template/src/app/api/contact-views/[id]/route.ts +197 -0
- package/template/src/app/api/contact-views/route.ts +146 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +55 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +20 -48
- package/template/src/app/api/contacts/[id]/files/route.ts +125 -186
- package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
- package/template/src/app/api/contacts/[id]/interactions/route.ts +45 -8
- package/template/src/app/api/contacts/[id]/kyc/route.ts +81 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +55 -29
- package/template/src/app/api/contacts/[id]/route.ts +184 -21
- package/template/src/app/api/contacts/[id]/send-email/route.ts +33 -11
- package/template/src/app/api/contacts/[id]/workflows/run/route.ts +67 -0
- package/template/src/app/api/contacts/export/route.ts +22 -31
- package/template/src/app/api/contacts/import/route.ts +77 -44
- package/template/src/app/api/contacts/import-preview/route.ts +139 -0
- package/template/src/app/api/contacts/origins/route.ts +63 -0
- package/template/src/app/api/contacts/route.ts +322 -57
- package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
- package/template/src/app/api/dashboard/stats/route.ts +9 -292
- package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -3
- package/template/src/app/api/dashboard/widgets/route.ts +19 -19
- 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 +28 -542
- package/template/src/app/api/invite/complete/route.ts +20 -23
- 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 +165 -39
- package/template/src/app/api/reminders/state/route.ts +164 -0
- package/template/src/app/api/reset-password/complete/route.ts +11 -13
- 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 +25 -47
- package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
- package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
- package/template/src/app/api/settings/company/route.ts +19 -26
- package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
- package/template/src/app/api/settings/google-ads/route.ts +34 -23
- 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 +48 -23
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +56 -32
- package/template/src/app/api/settings/google-sheet/preview/route.ts +110 -0
- package/template/src/app/api/settings/google-sheet/route.ts +34 -23
- 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 +20 -24
- package/template/src/app/api/settings/meta-leads/route.ts +34 -25
- package/template/src/app/api/settings/smtp/route.ts +53 -6
- package/template/src/app/api/settings/statuses/[id]/route.ts +29 -32
- package/template/src/app/api/settings/statuses/route.ts +24 -22
- package/template/src/app/api/statuses/route.ts +2 -5
- package/template/src/app/api/tasks/[id]/attendees/route.ts +36 -13
- package/template/src/app/api/tasks/[id]/route.ts +357 -145
- package/template/src/app/api/tasks/meet/route.ts +37 -26
- package/template/src/app/api/tasks/route.ts +201 -96
- package/template/src/app/api/templates/[id]/route.ts +22 -13
- package/template/src/app/api/templates/route.ts +22 -5
- package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
- package/template/src/app/api/users/[id]/route.ts +22 -16
- package/template/src/app/api/users/commercials/route.ts +38 -0
- package/template/src/app/api/users/for-agenda/route.ts +1 -2
- package/template/src/app/api/users/list/route.ts +57 -19
- package/template/src/app/api/users/route.ts +89 -34
- package/template/src/app/api/webhooks/google-ads/route.ts +40 -1
- package/template/src/app/api/webhooks/meta-leads/route.ts +38 -1
- package/template/src/app/api/workflows/[id]/route.ts +29 -6
- package/template/src/app/api/workflows/process/route.ts +505 -170
- package/template/src/app/api/workflows/route.ts +42 -4
- package/template/src/app/globals.css +512 -32
- package/template/src/app/layout.tsx +28 -9
- package/template/src/app/page.tsx +37 -7
- package/template/src/components/address-autocomplete.tsx +233 -0
- package/template/src/components/config-error-alert.tsx +46 -0
- package/template/src/components/contacts/filter-bar.tsx +190 -0
- package/template/src/components/contacts/filter-builder.tsx +574 -0
- package/template/src/components/contacts/save-view-dialog.tsx +160 -0
- package/template/src/components/contacts/views-tab-bar.tsx +449 -0
- package/template/src/components/dashboard/activity-chart.tsx +6 -1
- package/template/src/components/dashboard/add-widget-dialog.tsx +13 -17
- package/template/src/components/dashboard/color-picker.tsx +7 -8
- package/template/src/components/dashboard/recent-activity.tsx +2 -5
- package/template/src/components/dashboard/stat-card.tsx +1 -3
- package/template/src/components/dashboard/status-distribution-chart.tsx +0 -1
- package/template/src/components/dashboard/top-contacts-list.tsx +7 -13
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +2 -5
- package/template/src/components/dashboard/widget-wrapper.tsx +3 -6
- package/template/src/components/date-picker.tsx +399 -0
- package/template/src/components/editor/upload-editor-image.ts +42 -0
- package/template/src/components/editor.tsx +188 -35
- package/template/src/components/email-template.tsx +4 -2
- package/template/src/components/global-search.tsx +360 -0
- package/template/src/components/header.tsx +200 -107
- 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 +4 -2
- package/template/src/components/lazy-editor.tsx +11 -0
- package/template/src/components/meet-cancellation-email-template.tsx +11 -3
- package/template/src/components/meet-confirmation-email-template.tsx +10 -3
- package/template/src/components/meet-update-email-template.tsx +10 -3
- package/template/src/components/page-header.tsx +19 -15
- package/template/src/components/protected-page.tsx +94 -0
- package/template/src/components/reset-password-email-template.tsx +4 -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 +117 -100
- package/template/src/components/skeleton.tsx +128 -45
- package/template/src/components/ui/accordion.tsx +64 -0
- package/template/src/components/ui/alert-dialog.tsx +139 -0
- package/template/src/components/ui/button.tsx +71 -0
- 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-banner.tsx +1 -1
- package/template/src/components/view-as-modal.tsx +30 -19
- package/template/src/config/nav-pages.ts +108 -0
- package/template/src/contexts/app-toast-context.tsx +362 -0
- package/template/src/contexts/dashboard-theme-context.tsx +2 -7
- package/template/src/contexts/sidebar-context.tsx +27 -53
- package/template/src/contexts/task-reminder-context.tsx +134 -160
- package/template/src/contexts/view-as-context.tsx +32 -10
- package/template/src/hooks/use-alert.tsx +65 -0
- package/template/src/hooks/use-confirm.tsx +87 -0
- package/template/src/hooks/use-contact-views.ts +140 -0
- package/template/src/hooks/use-contacts.ts +69 -0
- package/template/src/hooks/use-fetch.ts +17 -0
- package/template/src/hooks/use-focus-trap.ts +73 -0
- package/template/src/hooks/use-statuses.ts +22 -0
- package/template/src/hooks/useIntegrationNotifications.ts +49 -0
- package/template/src/lib/address-api.ts +155 -0
- package/template/src/lib/auth.ts +8 -1
- package/template/src/lib/cache.ts +73 -0
- package/template/src/lib/check-permission.ts +12 -177
- 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 +24 -22
- package/template/src/lib/contact-view-filters.ts +301 -0
- package/template/src/lib/contacts-list-url.ts +190 -0
- package/template/src/lib/dashboard-stats.ts +282 -0
- package/template/src/lib/dashboard-themes.ts +0 -5
- package/template/src/lib/date-utils.ts +176 -0
- package/template/src/lib/default-widgets.ts +0 -2
- 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/get-auth-user.ts +25 -0
- package/template/src/lib/google-calendar-agenda.ts +201 -0
- package/template/src/lib/google-calendar.ts +309 -17
- package/template/src/lib/google-fetch.ts +63 -0
- 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/local-storage.ts +34 -0
- package/template/src/lib/permissions.ts +268 -40
- package/template/src/lib/prisma.ts +15 -12
- 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/roles.ts +12 -15
- package/template/src/lib/supabase-storage.ts +113 -0
- package/template/src/lib/template-variables.ts +204 -29
- package/template/src/lib/utils.ts +71 -11
- package/template/src/lib/widget-registry.ts +0 -4
- package/template/src/lib/workflow-executor.ts +391 -228
- package/template/src/proxy.ts +35 -73
- package/template/src/types/contact-views.ts +351 -0
- package/template/vercel.json +5 -0
- package/template/WORKFLOWS_CRON.md +0 -185
- package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
- package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
- package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
- package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
- package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
- package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
- package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
- package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
- package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
- package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
- package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
- package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
- package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
- package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
- package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
- package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
- package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
- package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
- package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
- package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
- package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
- package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
- package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
- package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
- package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
- package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
- package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
- package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
- package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
- package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
- package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
- package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
- package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
- package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
- package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
- package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
- package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
- package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
- package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
- package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
- package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
- package/template/prisma/migrations/20260226093949_fix_cascade_on_user_delete/migration.sql +0 -69
- package/template/src/app/(dashboard)/users/layout.tsx +0 -30
- package/template/src/lib/google-drive.ts +0 -380
|
@@ -0,0 +1,451 @@
|
|
|
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
|
+
interface MetaLeadConfig {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
pageId: string;
|
|
13
|
+
verifyToken: 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 MetaLeadFormData {
|
|
22
|
+
name: string;
|
|
23
|
+
active: boolean;
|
|
24
|
+
pageId: string;
|
|
25
|
+
accessToken: string;
|
|
26
|
+
verifyToken: string;
|
|
27
|
+
defaultStatusId: string | null;
|
|
28
|
+
defaultAssignedUserId: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const EMPTY_FORM: MetaLeadFormData = {
|
|
32
|
+
name: '',
|
|
33
|
+
active: true,
|
|
34
|
+
pageId: '',
|
|
35
|
+
accessToken: '',
|
|
36
|
+
verifyToken: '',
|
|
37
|
+
defaultStatusId: null,
|
|
38
|
+
defaultAssignedUserId: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface MetaLeadIntegrationProps {
|
|
42
|
+
readonly statuses: ReadonlyArray<{ id: string; name: string; color: string }>;
|
|
43
|
+
readonly users: ReadonlyArray<{ id: string; name: string; email: string }>;
|
|
44
|
+
readonly onOpenLogs: (configId: string, configName: string) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function MetaLeadIntegration({ statuses, users, onOpenLogs }: MetaLeadIntegrationProps) {
|
|
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<MetaLeadConfig[]>([]);
|
|
54
|
+
const [showModal, setShowModal] = useState(false);
|
|
55
|
+
const [editingConfig, setEditingConfig] = useState<string | null>(null);
|
|
56
|
+
const [formData, setFormData] = useState<MetaLeadFormData>(EMPTY_FORM);
|
|
57
|
+
|
|
58
|
+
const loadConfigs = useCallback(async () => {
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch('/api/settings/meta-leads');
|
|
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 Meta. Veuillez rafraîchir la page.');
|
|
67
|
+
} finally {
|
|
68
|
+
setLoading(false);
|
|
69
|
+
}
|
|
70
|
+
}, [toast]);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
loadConfigs();
|
|
74
|
+
}, [loadConfigs]);
|
|
75
|
+
|
|
76
|
+
const resetForm = () => {
|
|
77
|
+
setShowModal(false);
|
|
78
|
+
setEditingConfig(null);
|
|
79
|
+
setFormData(EMPTY_FORM);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleOpenAdd = () => {
|
|
83
|
+
setEditingConfig(null);
|
|
84
|
+
setFormData(EMPTY_FORM);
|
|
85
|
+
setShowModal(true);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const handleEdit = (config: MetaLeadConfig) => {
|
|
89
|
+
setEditingConfig(config.id);
|
|
90
|
+
setFormData({
|
|
91
|
+
name: config.name,
|
|
92
|
+
active: config.active,
|
|
93
|
+
pageId: config.pageId,
|
|
94
|
+
accessToken: '',
|
|
95
|
+
verifyToken: config.verifyToken,
|
|
96
|
+
defaultStatusId: config.defaultStatusId,
|
|
97
|
+
defaultAssignedUserId: config.defaultAssignedUserId,
|
|
98
|
+
});
|
|
99
|
+
setShowModal(true);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
setSaving(true);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const url = editingConfig
|
|
108
|
+
? `/api/settings/meta-leads/${editingConfig}`
|
|
109
|
+
: '/api/settings/meta-leads';
|
|
110
|
+
const method = editingConfig ? 'PUT' : 'POST';
|
|
111
|
+
|
|
112
|
+
const response = await fetch(url, {
|
|
113
|
+
method,
|
|
114
|
+
headers: { 'Content-Type': 'application/json' },
|
|
115
|
+
body: JSON.stringify(formData),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const data = await response.json();
|
|
119
|
+
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(data.error || 'Erreur lors de la sauvegarde de la configuration Meta');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
toast.success(
|
|
125
|
+
editingConfig
|
|
126
|
+
? 'Configuration Meta Lead Ads mise à jour avec succès'
|
|
127
|
+
: 'Configuration Meta Lead Ads créée avec succès',
|
|
128
|
+
);
|
|
129
|
+
resetForm();
|
|
130
|
+
await loadConfigs();
|
|
131
|
+
} catch (error: any) {
|
|
132
|
+
toast.error(devToast('Impossible de sauvegarder la configuration Meta. 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 Meta Lead',
|
|
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/meta-leads/${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 loadConfigs();
|
|
161
|
+
} catch (error: any) {
|
|
162
|
+
toast.error(devToast('Impossible de supprimer la configuration. Veuillez réessayer.', error));
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const getWebhookUrl = () => {
|
|
167
|
+
const origin = globalThis.window?.location.origin ?? '';
|
|
168
|
+
return `${origin}/api/webhooks/meta-leads`;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const handleCopyWebhook = () => {
|
|
172
|
+
const url = getWebhookUrl();
|
|
173
|
+
navigator.clipboard?.writeText(url).then(() => toast.success('Lien webhook copié'));
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<>
|
|
178
|
+
<div className="rounded-lg bg-white p-6 shadow-sm">
|
|
179
|
+
<div className="flex items-center justify-between">
|
|
180
|
+
<div>
|
|
181
|
+
<h2 className="text-lg font-bold text-gray-900">Intégration Meta Lead Ads</h2>
|
|
182
|
+
<p className="mt-1 text-sm text-gray-600">
|
|
183
|
+
Recevez automatiquement les leads depuis Facebook Lead Ads.
|
|
184
|
+
</p>
|
|
185
|
+
</div>
|
|
186
|
+
<button
|
|
187
|
+
type="button"
|
|
188
|
+
onClick={handleOpenAdd}
|
|
189
|
+
className="cursor-pointer rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
|
|
190
|
+
>
|
|
191
|
+
+ Ajouter
|
|
192
|
+
</button>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{loading && (
|
|
196
|
+
<div className="mt-6 text-center text-gray-500">Chargement...</div>
|
|
197
|
+
)}
|
|
198
|
+
|
|
199
|
+
{!loading && configs.length === 0 && (
|
|
200
|
+
<div className="mt-6 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8 text-center">
|
|
201
|
+
<p className="text-sm text-gray-600">Aucune configuration Meta Lead Ads</p>
|
|
202
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
203
|
+
Cliquez sur "+ Ajouter" pour créer votre première configuration
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
|
|
208
|
+
{!loading && configs.length > 0 && (
|
|
209
|
+
<div className="mt-6 space-y-3">
|
|
210
|
+
{configs.map((config) => (
|
|
211
|
+
<div
|
|
212
|
+
key={config.id}
|
|
213
|
+
className="flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 p-4"
|
|
214
|
+
>
|
|
215
|
+
<div className="flex-1">
|
|
216
|
+
<div className="flex items-center gap-2">
|
|
217
|
+
<h3 className="font-medium text-gray-900">{config.name}</h3>
|
|
218
|
+
{config.active ? (
|
|
219
|
+
<span className="rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
|
|
220
|
+
Actif
|
|
221
|
+
</span>
|
|
222
|
+
) : (
|
|
223
|
+
<span className="rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-800">
|
|
224
|
+
Inactif
|
|
225
|
+
</span>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
<p className="mt-1 text-xs text-gray-500">Page ID: {config.pageId}</p>
|
|
229
|
+
<p className="mt-0.5 text-xs text-gray-400">Webhook : {getWebhookUrl()}</p>
|
|
230
|
+
</div>
|
|
231
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
onClick={() => onOpenLogs(config.id, config.name)}
|
|
235
|
+
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"
|
|
236
|
+
>
|
|
237
|
+
Voir les logs
|
|
238
|
+
</button>
|
|
239
|
+
<button
|
|
240
|
+
type="button"
|
|
241
|
+
onClick={handleCopyWebhook}
|
|
242
|
+
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"
|
|
243
|
+
>
|
|
244
|
+
Copier le lien webhook
|
|
245
|
+
</button>
|
|
246
|
+
<button
|
|
247
|
+
type="button"
|
|
248
|
+
onClick={() => handleEdit(config)}
|
|
249
|
+
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"
|
|
250
|
+
>
|
|
251
|
+
Modifier
|
|
252
|
+
</button>
|
|
253
|
+
<button
|
|
254
|
+
type="button"
|
|
255
|
+
onClick={() => handleDelete(config.id)}
|
|
256
|
+
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"
|
|
257
|
+
>
|
|
258
|
+
Supprimer
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
))}
|
|
263
|
+
</div>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{showModal && (
|
|
268
|
+
<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">
|
|
269
|
+
<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">
|
|
270
|
+
<div className="shrink-0 border-b border-gray-100 pb-4">
|
|
271
|
+
<div className="flex items-center justify-between">
|
|
272
|
+
<h2 className="text-xl font-bold text-gray-900 sm:text-2xl">
|
|
273
|
+
{editingConfig ? 'Modifier' : 'Ajouter'} une configuration Meta Lead Ads
|
|
274
|
+
</h2>
|
|
275
|
+
<button
|
|
276
|
+
type="button"
|
|
277
|
+
onClick={resetForm}
|
|
278
|
+
className="cursor-pointer rounded-lg p-2 text-gray-400 transition-colors hover:bg-gray-100"
|
|
279
|
+
>
|
|
280
|
+
<svg
|
|
281
|
+
className="h-6 w-6"
|
|
282
|
+
fill="none"
|
|
283
|
+
stroke="currentColor"
|
|
284
|
+
viewBox="0 0 24 24"
|
|
285
|
+
>
|
|
286
|
+
<path
|
|
287
|
+
strokeLinecap="round"
|
|
288
|
+
strokeLinejoin="round"
|
|
289
|
+
strokeWidth={2}
|
|
290
|
+
d="M6 18L18 6M6 6l12 12"
|
|
291
|
+
/>
|
|
292
|
+
</svg>
|
|
293
|
+
</button>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
<form
|
|
298
|
+
id="meta-lead-form"
|
|
299
|
+
onSubmit={handleSubmit}
|
|
300
|
+
className="flex-1 space-y-4 overflow-y-auto pt-4"
|
|
301
|
+
>
|
|
302
|
+
<div>
|
|
303
|
+
<label htmlFor="meta-lead-name" className="block text-sm font-medium text-gray-700">
|
|
304
|
+
Nom de la configuration *
|
|
305
|
+
</label>
|
|
306
|
+
<input
|
|
307
|
+
id="meta-lead-name"
|
|
308
|
+
type="text"
|
|
309
|
+
required
|
|
310
|
+
value={formData.name}
|
|
311
|
+
onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
|
|
312
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
313
|
+
placeholder="Ex: Facebook Lead Ads"
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div className="flex items-center">
|
|
318
|
+
<input
|
|
319
|
+
id="meta-lead-active"
|
|
320
|
+
type="checkbox"
|
|
321
|
+
checked={formData.active}
|
|
322
|
+
onChange={(e) =>
|
|
323
|
+
setFormData((prev) => ({ ...prev, active: e.target.checked }))
|
|
324
|
+
}
|
|
325
|
+
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-gray-400/30"
|
|
326
|
+
/>
|
|
327
|
+
<label
|
|
328
|
+
htmlFor="meta-lead-active"
|
|
329
|
+
className="ml-2 text-sm font-medium text-gray-700"
|
|
330
|
+
>
|
|
331
|
+
Activer l'intégration Meta Lead Ads
|
|
332
|
+
</label>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<div>
|
|
336
|
+
<label htmlFor="meta-lead-page-id" className="block text-sm font-medium text-gray-700">Page ID *</label>
|
|
337
|
+
<input
|
|
338
|
+
id="meta-lead-page-id"
|
|
339
|
+
type="text"
|
|
340
|
+
required
|
|
341
|
+
value={formData.pageId}
|
|
342
|
+
onChange={(e) => setFormData((prev) => ({ ...prev, pageId: e.target.value }))}
|
|
343
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
344
|
+
placeholder="Page ID Facebook"
|
|
345
|
+
/>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<div>
|
|
349
|
+
<label htmlFor="meta-lead-access-token" className="block text-sm font-medium text-gray-700">Access Token *</label>
|
|
350
|
+
<input
|
|
351
|
+
id="meta-lead-access-token"
|
|
352
|
+
type="password"
|
|
353
|
+
autoComplete="off"
|
|
354
|
+
required
|
|
355
|
+
value={formData.accessToken}
|
|
356
|
+
onChange={(e) =>
|
|
357
|
+
setFormData((prev) => ({ ...prev, accessToken: e.target.value }))
|
|
358
|
+
}
|
|
359
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
360
|
+
placeholder="Access Token"
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div>
|
|
365
|
+
<label htmlFor="meta-lead-verify-token" className="block text-sm font-medium text-gray-700">Verify Token *</label>
|
|
366
|
+
<input
|
|
367
|
+
id="meta-lead-verify-token"
|
|
368
|
+
type="text"
|
|
369
|
+
required
|
|
370
|
+
value={formData.verifyToken}
|
|
371
|
+
onChange={(e) =>
|
|
372
|
+
setFormData((prev) => ({ ...prev, verifyToken: e.target.value }))
|
|
373
|
+
}
|
|
374
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
375
|
+
placeholder="Verify Token"
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
380
|
+
<div>
|
|
381
|
+
<label htmlFor="meta-lead-assigned-user" className="block text-sm font-medium text-gray-700">
|
|
382
|
+
Utilisateur assigné par défaut (optionnel)
|
|
383
|
+
</label>
|
|
384
|
+
<select
|
|
385
|
+
id="meta-lead-assigned-user"
|
|
386
|
+
value={formData.defaultAssignedUserId || ''}
|
|
387
|
+
onChange={(e) =>
|
|
388
|
+
setFormData((prev) => ({
|
|
389
|
+
...prev,
|
|
390
|
+
defaultAssignedUserId: e.target.value || null,
|
|
391
|
+
}))
|
|
392
|
+
}
|
|
393
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
394
|
+
>
|
|
395
|
+
<option value="">Aucun utilisateur par défaut</option>
|
|
396
|
+
{users.map((user) => (
|
|
397
|
+
<option key={user.id} value={user.id}>
|
|
398
|
+
{user.name} ({user.email})
|
|
399
|
+
</option>
|
|
400
|
+
))}
|
|
401
|
+
</select>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div>
|
|
405
|
+
<label htmlFor="meta-lead-default-status" className="block text-sm font-medium text-gray-700">
|
|
406
|
+
Statut par défaut
|
|
407
|
+
</label>
|
|
408
|
+
<StatusSelect
|
|
409
|
+
id="meta-lead-default-status"
|
|
410
|
+
statuses={[...statuses]}
|
|
411
|
+
value={formData.defaultStatusId || ''}
|
|
412
|
+
onChange={(v) =>
|
|
413
|
+
setFormData((prev) => ({
|
|
414
|
+
...prev,
|
|
415
|
+
defaultStatusId: v || null,
|
|
416
|
+
}))
|
|
417
|
+
}
|
|
418
|
+
placeholder="Aucun statut par défaut"
|
|
419
|
+
className="mt-1"
|
|
420
|
+
/>
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
</form>
|
|
424
|
+
|
|
425
|
+
<div className="shrink-0 border-t border-gray-100 pt-4">
|
|
426
|
+
<div className="flex flex-col gap-3 sm:flex-row sm:justify-end">
|
|
427
|
+
<button
|
|
428
|
+
type="button"
|
|
429
|
+
onClick={resetForm}
|
|
430
|
+
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"
|
|
431
|
+
>
|
|
432
|
+
Annuler
|
|
433
|
+
</button>
|
|
434
|
+
<button
|
|
435
|
+
type="submit"
|
|
436
|
+
form="meta-lead-form"
|
|
437
|
+
disabled={saving}
|
|
438
|
+
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"
|
|
439
|
+
>
|
|
440
|
+
{saving ? 'Enregistrement...' : 'Enregistrer'}
|
|
441
|
+
</button>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
447
|
+
|
|
448
|
+
<ConfirmDialog />
|
|
449
|
+
</>
|
|
450
|
+
);
|
|
451
|
+
}
|