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
|
@@ -46,7 +46,7 @@ export async function POST(request: NextRequest) {
|
|
|
46
46
|
// Générer un token pour la réinitialisation (différent du code)
|
|
47
47
|
const resetToken = crypto.randomUUID();
|
|
48
48
|
const expiresAt = new Date();
|
|
49
|
-
expiresAt.
|
|
49
|
+
expiresAt.setMinutes(expiresAt.getMinutes() + 30); // Valide 30 minutes
|
|
50
50
|
|
|
51
51
|
// Supprimer l'ancien token de code
|
|
52
52
|
await prisma.verification.delete({
|
|
@@ -4,8 +4,8 @@ import { ResetPasswordEmailTemplate } from '@/components/reset-password-email-te
|
|
|
4
4
|
import { prisma } from '@/lib/prisma';
|
|
5
5
|
import { decrypt } from '@/lib/encryption';
|
|
6
6
|
import { auth } from '@/lib/auth';
|
|
7
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
7
8
|
import nodemailer from 'nodemailer';
|
|
8
|
-
import { render } from '@react-email/render';
|
|
9
9
|
import React from 'react';
|
|
10
10
|
|
|
11
11
|
function htmlToText(html: string): string {
|
|
@@ -102,6 +102,13 @@ export async function POST(request: Request) {
|
|
|
102
102
|
return Response.json({ error: 'Non authentifié' }, { status: 401 });
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
if (!isResetPassword) {
|
|
106
|
+
const canSendEmail = await checkPermission('contacts.send_email');
|
|
107
|
+
if (!canSendEmail) {
|
|
108
|
+
return Response.json({ error: 'Accès refusé' }, { status: 403 });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
105
112
|
let smtpConfig, smtpError;
|
|
106
113
|
if (isResetPassword) {
|
|
107
114
|
const result = await getAnyAdminSmtpConfig();
|
|
@@ -116,8 +123,11 @@ export async function POST(request: Request) {
|
|
|
116
123
|
if (!smtpConfig) {
|
|
117
124
|
const errorMsg =
|
|
118
125
|
smtpError ||
|
|
119
|
-
'Aucune configuration SMTP trouvée. Veuillez configurer SMTP dans les paramètres.';
|
|
120
|
-
return Response.json(
|
|
126
|
+
'Aucune configuration SMTP trouvée. Veuillez configurer SMTP dans les paramètres pour finaliser votre action.';
|
|
127
|
+
return Response.json(
|
|
128
|
+
{ error: errorMsg, configLink: '/settings?section=system' },
|
|
129
|
+
{ status: 400 },
|
|
130
|
+
);
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
let emailComponent: React.ReactElement;
|
|
@@ -139,6 +149,7 @@ export async function POST(request: Request) {
|
|
|
139
149
|
});
|
|
140
150
|
}
|
|
141
151
|
|
|
152
|
+
const { render } = await import('@react-email/render');
|
|
142
153
|
const emailHtml = await render(emailComponent);
|
|
143
154
|
const emailText = htmlToText(emailHtml);
|
|
144
155
|
|
|
@@ -183,8 +194,9 @@ export async function POST(request: Request) {
|
|
|
183
194
|
return Response.json(
|
|
184
195
|
{
|
|
185
196
|
error:
|
|
186
|
-
"Erreur d'authentification SMTP.
|
|
197
|
+
"Erreur d'authentification SMTP. Veuillez vérifier votre configuration SMTP pour finaliser votre action.",
|
|
187
198
|
details: error.message,
|
|
199
|
+
configLink: '/settings?section=system',
|
|
188
200
|
},
|
|
189
201
|
{ status: 400 },
|
|
190
202
|
);
|
|
@@ -100,6 +100,20 @@ export async function POST(request: NextRequest) {
|
|
|
100
100
|
},
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
await prisma.integrationImportLog.create({
|
|
104
|
+
data: {
|
|
105
|
+
integrationType: 'google_ads',
|
|
106
|
+
configId: config.id,
|
|
107
|
+
configName: config.name,
|
|
108
|
+
action: 'created',
|
|
109
|
+
actorId: session.user.id,
|
|
110
|
+
totalImported: 0,
|
|
111
|
+
totalDuplicates: 0,
|
|
112
|
+
totalUpdated: 0,
|
|
113
|
+
totalErrors: 0,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
103
117
|
return NextResponse.json({
|
|
104
118
|
success: true,
|
|
105
119
|
config: {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma } from '@/lib/prisma';
|
|
4
|
+
import { encrypt } from '@/lib/encryption';
|
|
5
|
+
import {
|
|
6
|
+
getValidAccessToken,
|
|
7
|
+
getUserGoogleAccount,
|
|
8
|
+
listGoogleCalendars,
|
|
9
|
+
} from '@/lib/google-calendar';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* GET /api/settings/google-calendar/calendars
|
|
13
|
+
* Liste les calendriers Google de l'utilisateur + préférences CRM (défaut, affichage agenda).
|
|
14
|
+
*/
|
|
15
|
+
export async function GET(request: NextRequest) {
|
|
16
|
+
try {
|
|
17
|
+
const session = await auth.api.getSession({
|
|
18
|
+
headers: request.headers,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!session) {
|
|
22
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let googleAccount;
|
|
26
|
+
try {
|
|
27
|
+
googleAccount = await getUserGoogleAccount(session.user.id);
|
|
28
|
+
} catch (e: any) {
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{
|
|
31
|
+
error: e.message || 'Compte Google non connecté',
|
|
32
|
+
calendars: [],
|
|
33
|
+
defaultGoogleCalendarId: null,
|
|
34
|
+
agendaVisibleGoogleCalendarIds: [],
|
|
35
|
+
agendaGoogleEventColor: null,
|
|
36
|
+
},
|
|
37
|
+
{ status: 200 },
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const accessToken = await getValidAccessToken(
|
|
42
|
+
googleAccount.accessToken,
|
|
43
|
+
googleAccount.refreshToken,
|
|
44
|
+
googleAccount.tokenExpiresAt,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (accessToken !== googleAccount.accessToken) {
|
|
48
|
+
const tokenExpiresAt = new Date();
|
|
49
|
+
tokenExpiresAt.setSeconds(tokenExpiresAt.getSeconds() + 3600);
|
|
50
|
+
await prisma.userGoogleAccount.update({
|
|
51
|
+
where: { userId: session.user.id },
|
|
52
|
+
data: {
|
|
53
|
+
accessToken: encrypt(accessToken),
|
|
54
|
+
tokenExpiresAt,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let calendars;
|
|
60
|
+
try {
|
|
61
|
+
calendars = await listGoogleCalendars(accessToken);
|
|
62
|
+
} catch (listErr: any) {
|
|
63
|
+
const msg = listErr?.message || String(listErr);
|
|
64
|
+
const needsReconnect =
|
|
65
|
+
/403|insufficient|calendarList|Invalid Credentials/i.test(msg) ||
|
|
66
|
+
msg.includes('Erreur lors de la liste des calendriers');
|
|
67
|
+
return NextResponse.json(
|
|
68
|
+
{
|
|
69
|
+
error: needsReconnect
|
|
70
|
+
? 'Reconnectez votre compte Google (Paramètres → Intégrations) pour autoriser la liste des calendriers.'
|
|
71
|
+
: msg,
|
|
72
|
+
calendars: [],
|
|
73
|
+
defaultGoogleCalendarId: googleAccount.defaultGoogleCalendarId ?? null,
|
|
74
|
+
agendaVisibleGoogleCalendarIds: normalizeAgendaIds(googleAccount.agendaVisibleGoogleCalendarIds),
|
|
75
|
+
agendaGoogleEventColor: googleAccount.agendaGoogleEventColor ?? null,
|
|
76
|
+
needsGoogleReconnect: Boolean(needsReconnect),
|
|
77
|
+
},
|
|
78
|
+
{ status: 200 },
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return NextResponse.json({
|
|
83
|
+
calendars,
|
|
84
|
+
defaultGoogleCalendarId: googleAccount.defaultGoogleCalendarId ?? null,
|
|
85
|
+
agendaVisibleGoogleCalendarIds: normalizeAgendaIds(googleAccount.agendaVisibleGoogleCalendarIds),
|
|
86
|
+
agendaGoogleEventColor: googleAccount.agendaGoogleEventColor ?? null,
|
|
87
|
+
});
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
console.error('GET google-calendar calendars:', error);
|
|
90
|
+
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function normalizeAgendaIds(raw: unknown): string[] {
|
|
95
|
+
if (!raw || !Array.isArray(raw)) return [];
|
|
96
|
+
return raw.filter((x): x is string => typeof x === 'string' && x.trim() !== '');
|
|
97
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma, Prisma } from '@/lib/prisma';
|
|
4
|
+
import { encrypt } from '@/lib/encryption';
|
|
5
|
+
import {
|
|
6
|
+
getValidAccessToken,
|
|
7
|
+
getUserGoogleAccount,
|
|
8
|
+
assertWritableGoogleCalendar,
|
|
9
|
+
assertKnownGoogleCalendar,
|
|
10
|
+
} from '@/lib/google-calendar';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* PATCH /api/settings/google-calendar
|
|
14
|
+
* Préférences : calendrier par défaut à la création, calendriers visibles dans l'agenda CRM.
|
|
15
|
+
*/
|
|
16
|
+
export async function PATCH(request: NextRequest) {
|
|
17
|
+
try {
|
|
18
|
+
const session = await auth.api.getSession({
|
|
19
|
+
headers: request.headers,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (!session) {
|
|
23
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const body = await request.json();
|
|
27
|
+
const { defaultGoogleCalendarId, agendaVisibleGoogleCalendarIds, agendaGoogleEventColor } = body as {
|
|
28
|
+
defaultGoogleCalendarId?: string | null;
|
|
29
|
+
agendaVisibleGoogleCalendarIds?: string[];
|
|
30
|
+
agendaGoogleEventColor?: string | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const googleAccount = await getUserGoogleAccount(session.user.id);
|
|
34
|
+
|
|
35
|
+
const accessToken = await getValidAccessToken(
|
|
36
|
+
googleAccount.accessToken,
|
|
37
|
+
googleAccount.refreshToken,
|
|
38
|
+
googleAccount.tokenExpiresAt,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (accessToken !== googleAccount.accessToken) {
|
|
42
|
+
const tokenExpiresAt = new Date();
|
|
43
|
+
tokenExpiresAt.setSeconds(tokenExpiresAt.getSeconds() + 3600);
|
|
44
|
+
await prisma.userGoogleAccount.update({
|
|
45
|
+
where: { userId: session.user.id },
|
|
46
|
+
data: {
|
|
47
|
+
accessToken: encrypt(accessToken),
|
|
48
|
+
tokenExpiresAt,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const data: {
|
|
54
|
+
defaultGoogleCalendarId?: string | null;
|
|
55
|
+
agendaVisibleGoogleCalendarIds?: Prisma.InputJsonValue;
|
|
56
|
+
agendaGoogleEventColor?: string | null;
|
|
57
|
+
} = {};
|
|
58
|
+
|
|
59
|
+
if ('defaultGoogleCalendarId' in body) {
|
|
60
|
+
if (defaultGoogleCalendarId === null || defaultGoogleCalendarId === '') {
|
|
61
|
+
data.defaultGoogleCalendarId = null;
|
|
62
|
+
} else if (typeof defaultGoogleCalendarId === 'string') {
|
|
63
|
+
const cal = defaultGoogleCalendarId.trim();
|
|
64
|
+
await assertWritableGoogleCalendar(accessToken, cal);
|
|
65
|
+
data.defaultGoogleCalendarId = cal === 'primary' ? null : cal;
|
|
66
|
+
} else {
|
|
67
|
+
return NextResponse.json({ error: 'defaultGoogleCalendarId invalide' }, { status: 400 });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (agendaVisibleGoogleCalendarIds !== undefined) {
|
|
72
|
+
if (!Array.isArray(agendaVisibleGoogleCalendarIds)) {
|
|
73
|
+
return NextResponse.json(
|
|
74
|
+
{ error: 'agendaVisibleGoogleCalendarIds doit être un tableau' },
|
|
75
|
+
{ status: 400 },
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const ids = [...new Set(agendaVisibleGoogleCalendarIds.filter((x) => typeof x === 'string' && x.trim() !== '').map((x: string) => x.trim()))];
|
|
79
|
+
await Promise.all(ids.map((id) => assertKnownGoogleCalendar(accessToken, id)));
|
|
80
|
+
data.agendaVisibleGoogleCalendarIds = ids;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ('agendaGoogleEventColor' in body) {
|
|
84
|
+
if (agendaGoogleEventColor === null || agendaGoogleEventColor === '') {
|
|
85
|
+
data.agendaGoogleEventColor = null;
|
|
86
|
+
} else if (typeof agendaGoogleEventColor === 'string') {
|
|
87
|
+
const c = agendaGoogleEventColor.trim();
|
|
88
|
+
if (!/^#[0-9A-Fa-f]{6}$/.test(c)) {
|
|
89
|
+
return NextResponse.json(
|
|
90
|
+
{ error: 'agendaGoogleEventColor doit être au format #RRGGBB' },
|
|
91
|
+
{ status: 400 },
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
data.agendaGoogleEventColor = c;
|
|
95
|
+
} else {
|
|
96
|
+
return NextResponse.json({ error: 'agendaGoogleEventColor invalide' }, { status: 400 });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (Object.keys(data).length === 0) {
|
|
101
|
+
return NextResponse.json({ error: 'Aucun champ à mettre à jour' }, { status: 400 });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const updated = await prisma.userGoogleAccount.update({
|
|
105
|
+
where: { userId: session.user.id },
|
|
106
|
+
data,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return NextResponse.json({
|
|
110
|
+
success: true,
|
|
111
|
+
defaultGoogleCalendarId: updated.defaultGoogleCalendarId ?? null,
|
|
112
|
+
agendaVisibleGoogleCalendarIds: normalizeAgendaIds(updated.agendaVisibleGoogleCalendarIds),
|
|
113
|
+
agendaGoogleEventColor: updated.agendaGoogleEventColor ?? null,
|
|
114
|
+
});
|
|
115
|
+
} catch (error: any) {
|
|
116
|
+
console.error('PATCH google-calendar settings:', error);
|
|
117
|
+
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeAgendaIds(raw: unknown): string[] {
|
|
122
|
+
if (!raw || !Array.isArray(raw)) return [];
|
|
123
|
+
return raw.filter((x): x is string => typeof x === 'string' && x.trim() !== '');
|
|
124
|
+
}
|
|
@@ -159,6 +159,20 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
159
159
|
data: updateData,
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
+
await prisma.integrationImportLog.create({
|
|
163
|
+
data: {
|
|
164
|
+
integrationType: 'google_sheet',
|
|
165
|
+
configId: config.id,
|
|
166
|
+
configName: config.name,
|
|
167
|
+
action: 'updated',
|
|
168
|
+
actorId: session.user.id,
|
|
169
|
+
totalImported: 0,
|
|
170
|
+
totalDuplicates: 0,
|
|
171
|
+
totalUpdated: 0,
|
|
172
|
+
totalErrors: 0,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
162
176
|
return NextResponse.json({
|
|
163
177
|
success: true,
|
|
164
178
|
config: {
|
|
@@ -216,6 +230,20 @@ export async function DELETE(
|
|
|
216
230
|
where: { id },
|
|
217
231
|
});
|
|
218
232
|
|
|
233
|
+
await prisma.integrationImportLog.create({
|
|
234
|
+
data: {
|
|
235
|
+
integrationType: 'google_sheet',
|
|
236
|
+
configId: existingConfig.id,
|
|
237
|
+
configName: existingConfig.name,
|
|
238
|
+
action: 'deleted',
|
|
239
|
+
actorId: session.user.id,
|
|
240
|
+
totalImported: 0,
|
|
241
|
+
totalDuplicates: 0,
|
|
242
|
+
totalUpdated: 0,
|
|
243
|
+
totalErrors: 0,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
|
|
219
247
|
return NextResponse.json({
|
|
220
248
|
success: true,
|
|
221
249
|
message: 'Configuration Google Sheets supprimée avec succès.',
|
|
@@ -56,9 +56,7 @@ export async function POST(request: NextRequest) {
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
// Note: Cette fonction n'existe plus dans google-calendar, utiliser celle de google-drive
|
|
61
|
-
const { getAdminGoogleAccount } = await import('@/lib/google-drive');
|
|
59
|
+
const { getAdminGoogleAccount } = await import('@/lib/google-calendar');
|
|
62
60
|
let googleAccount;
|
|
63
61
|
try {
|
|
64
62
|
googleAccount = await getAdminGoogleAccount();
|
|
@@ -67,7 +65,8 @@ export async function POST(request: NextRequest) {
|
|
|
67
65
|
{
|
|
68
66
|
error:
|
|
69
67
|
error.message ||
|
|
70
|
-
'Aucun compte Google configuré. Veuillez demander à un administrateur de connecter son compte Google
|
|
68
|
+
'Aucun compte Google configuré. Veuillez demander à un administrateur de connecter son compte Google pour configurer l\'intégration Google Sheets.',
|
|
69
|
+
configLink: '/settings?section=integrations',
|
|
71
70
|
},
|
|
72
71
|
{ status: 400 },
|
|
73
72
|
);
|
|
@@ -125,6 +124,28 @@ export async function POST(request: NextRequest) {
|
|
|
125
124
|
emailColumn: ['email', 'e-mail', 'mail', 'courriel', 'mél', 'mel'],
|
|
126
125
|
cityColumn: ['ville', 'city', 'localité', 'locality'],
|
|
127
126
|
postalCodeColumn: ['code postal', 'postal code', 'cp', 'zip', 'zipcode', 'code_postal'],
|
|
127
|
+
companyNameColumn: [
|
|
128
|
+
'société',
|
|
129
|
+
'societe',
|
|
130
|
+
'company',
|
|
131
|
+
'company name',
|
|
132
|
+
'company_name',
|
|
133
|
+
'company-name',
|
|
134
|
+
'companyname',
|
|
135
|
+
'organisation',
|
|
136
|
+
'organization',
|
|
137
|
+
'organisme',
|
|
138
|
+
'org',
|
|
139
|
+
'org name',
|
|
140
|
+
'org_name',
|
|
141
|
+
'org-name',
|
|
142
|
+
'business',
|
|
143
|
+
'business name',
|
|
144
|
+
'business_name',
|
|
145
|
+
'entreprise',
|
|
146
|
+
'nom entreprise',
|
|
147
|
+
'nom société',
|
|
148
|
+
],
|
|
128
149
|
originColumn: [
|
|
129
150
|
'origine',
|
|
130
151
|
'origin',
|
|
@@ -133,6 +154,18 @@ export async function POST(request: NextRequest) {
|
|
|
133
154
|
'campaign',
|
|
134
155
|
'origine de la campagne',
|
|
135
156
|
],
|
|
157
|
+
websiteColumn: [
|
|
158
|
+
'site',
|
|
159
|
+
'site web',
|
|
160
|
+
'website',
|
|
161
|
+
'url',
|
|
162
|
+
'web',
|
|
163
|
+
'site internet',
|
|
164
|
+
],
|
|
165
|
+
linkedinColumn: ['linkedin', 'linkedin url', 'linkedin profile', 'linkedin profil'],
|
|
166
|
+
facebookColumn: ['facebook', 'facebook url', 'fb', 'facebook profile', 'facebook profil'],
|
|
167
|
+
twitterColumn: ['twitter', 'twitter url', 'x', 'x.com', 'twitter profile', 'twitter profil'],
|
|
168
|
+
instagramColumn: ['instagram', 'instagram url', 'insta', 'instagram profile', 'instagram profil'],
|
|
136
169
|
};
|
|
137
170
|
|
|
138
171
|
const mapping: Record<string, string> = {};
|
|
@@ -35,13 +35,19 @@ export async function POST(request: NextRequest) {
|
|
|
35
35
|
|
|
36
36
|
const spreadsheetId = extractSpreadsheetId(sheetUrl);
|
|
37
37
|
|
|
38
|
-
const { getAdminGoogleAccount } = await import('@/lib/google-
|
|
38
|
+
const { getAdminGoogleAccount } = await import('@/lib/google-calendar');
|
|
39
39
|
let googleAccount;
|
|
40
40
|
try {
|
|
41
41
|
googleAccount = await getAdminGoogleAccount();
|
|
42
42
|
} catch (error: unknown) {
|
|
43
|
-
const message =
|
|
44
|
-
|
|
43
|
+
const message =
|
|
44
|
+
error instanceof Error
|
|
45
|
+
? error.message
|
|
46
|
+
: 'Aucun compte Google configuré. Veuillez demander à un administrateur de connecter son compte Google pour configurer l\'intégration Google Sheets.';
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ error: message, configLink: '/settings?section=integrations' },
|
|
49
|
+
{ status: 400 },
|
|
50
|
+
);
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
const accessToken = await getValidAccessToken(
|
|
@@ -223,6 +223,20 @@ export async function POST(request: NextRequest) {
|
|
|
223
223
|
},
|
|
224
224
|
});
|
|
225
225
|
|
|
226
|
+
await prisma.integrationImportLog.create({
|
|
227
|
+
data: {
|
|
228
|
+
integrationType: 'google_sheet',
|
|
229
|
+
configId: config.id,
|
|
230
|
+
configName: config.name,
|
|
231
|
+
action: 'created',
|
|
232
|
+
actorId: session.user.id,
|
|
233
|
+
totalImported: 0,
|
|
234
|
+
totalDuplicates: 0,
|
|
235
|
+
totalUpdated: 0,
|
|
236
|
+
totalErrors: 0,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
226
240
|
return NextResponse.json({
|
|
227
241
|
success: true,
|
|
228
242
|
config: {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { prisma, Prisma } from '@/lib/prisma';
|
|
3
|
+
import { checkPermissions } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
5
|
+
|
|
6
|
+
const INTEGRATION_PERMISSIONS = [
|
|
7
|
+
'integrations.google_sheets.manage',
|
|
8
|
+
'integrations.meta.manage',
|
|
9
|
+
'integrations.google_ads.manage',
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
function hasAnyIntegrationPermission(perms: Record<string, boolean>): boolean {
|
|
13
|
+
return INTEGRATION_PERMISSIONS.some((p) => perms[p]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SYNC_ACTIONS = ['synced', 'synced_manual', 'synced_auto'] as const;
|
|
17
|
+
|
|
18
|
+
// GET /api/settings/integrations/logs - Liste paginée des logs d'intégration
|
|
19
|
+
export async function GET(request: NextRequest) {
|
|
20
|
+
try {
|
|
21
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
22
|
+
if (!session) {
|
|
23
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
24
|
+
}
|
|
25
|
+
const perms = await checkPermissions(...INTEGRATION_PERMISSIONS);
|
|
26
|
+
if (!hasAnyIntegrationPermission(perms)) {
|
|
27
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { searchParams } = new URL(request.url);
|
|
31
|
+
const integrationType = searchParams.get('integrationType') || undefined;
|
|
32
|
+
const configId = searchParams.get('configId') || undefined;
|
|
33
|
+
const page = Math.max(1, Number.parseInt(searchParams.get('page') || '1', 10));
|
|
34
|
+
const pageSize = Math.min(50, Math.max(10, Number.parseInt(searchParams.get('pageSize') || '20', 10)));
|
|
35
|
+
const since = searchParams.get('since') || undefined;
|
|
36
|
+
|
|
37
|
+
const where: Prisma.IntegrationImportLogWhereInput = {};
|
|
38
|
+
if (integrationType) where.integrationType = integrationType;
|
|
39
|
+
if (configId) where.configId = configId;
|
|
40
|
+
if (since) {
|
|
41
|
+
const sinceDate = new Date(since);
|
|
42
|
+
if (!Number.isNaN(sinceDate.getTime())) where.createdAt = { gte: sinceDate };
|
|
43
|
+
}
|
|
44
|
+
// Synchronisations : afficher si au moins un contact concerné OU une erreur (cohérent avec ce qui est persisté)
|
|
45
|
+
where.OR = [
|
|
46
|
+
{ action: { notIn: [...SYNC_ACTIONS] } },
|
|
47
|
+
{ action: { in: [...SYNC_ACTIONS] }, totalImported: { gt: 0 } },
|
|
48
|
+
{ action: { in: [...SYNC_ACTIONS] }, totalDuplicates: { gt: 0 } },
|
|
49
|
+
{ action: { in: [...SYNC_ACTIONS] }, totalUpdated: { gt: 0 } },
|
|
50
|
+
{ action: { in: [...SYNC_ACTIONS] }, totalErrors: { gt: 0 } },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const [logs, total] = await Promise.all([
|
|
54
|
+
prisma.integrationImportLog.findMany({
|
|
55
|
+
where,
|
|
56
|
+
include: {
|
|
57
|
+
actor: {
|
|
58
|
+
select: { id: true, name: true, email: true },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
orderBy: { createdAt: 'desc' },
|
|
62
|
+
skip: (page - 1) * pageSize,
|
|
63
|
+
take: pageSize,
|
|
64
|
+
}),
|
|
65
|
+
prisma.integrationImportLog.count({ where }),
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
return NextResponse.json({
|
|
69
|
+
logs: logs.map((log) => ({
|
|
70
|
+
id: log.id,
|
|
71
|
+
integrationType: log.integrationType,
|
|
72
|
+
configId: log.configId,
|
|
73
|
+
configName: log.configName,
|
|
74
|
+
action: log.action,
|
|
75
|
+
actorId: log.actorId,
|
|
76
|
+
actor: log.actor,
|
|
77
|
+
totalImported: log.totalImported,
|
|
78
|
+
totalDuplicates: log.totalDuplicates,
|
|
79
|
+
totalUpdated: log.totalUpdated,
|
|
80
|
+
totalErrors: log.totalErrors,
|
|
81
|
+
errorDetails: log.errorDetails,
|
|
82
|
+
createdAt: log.createdAt,
|
|
83
|
+
})),
|
|
84
|
+
total,
|
|
85
|
+
page,
|
|
86
|
+
pageSize,
|
|
87
|
+
totalPages: Math.ceil(total / pageSize),
|
|
88
|
+
});
|
|
89
|
+
} catch (error: any) {
|
|
90
|
+
console.error('Erreur lors de la récupération des logs d\'intégration:', error);
|
|
91
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { prisma } from '@/lib/prisma';
|
|
3
|
+
import { checkPermissions } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
5
|
+
|
|
6
|
+
const INTEGRATION_PERMISSIONS = [
|
|
7
|
+
'integrations.google_sheets.manage',
|
|
8
|
+
'integrations.meta.manage',
|
|
9
|
+
'integrations.google_ads.manage',
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
function hasAnyIntegrationPermission(perms: Record<string, boolean>): boolean {
|
|
13
|
+
return INTEGRATION_PERMISSIONS.some((p) => perms[p]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const NOTIFICATION_ACTIONS = ['synced', 'synced_manual', 'synced_auto', 'created'] as const;
|
|
17
|
+
|
|
18
|
+
// GET /api/settings/integrations/notifications?since=ISO_DATE
|
|
19
|
+
// Retourne les logs récents pour le polling (notifications admin)
|
|
20
|
+
export async function GET(request: NextRequest) {
|
|
21
|
+
try {
|
|
22
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
23
|
+
if (!session) {
|
|
24
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
25
|
+
}
|
|
26
|
+
const perms = await checkPermissions(...INTEGRATION_PERMISSIONS);
|
|
27
|
+
if (!hasAnyIntegrationPermission(perms)) {
|
|
28
|
+
return NextResponse.json({ notifications: [] });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { searchParams } = new URL(request.url);
|
|
32
|
+
const sinceParam = searchParams.get('since');
|
|
33
|
+
if (!sinceParam) {
|
|
34
|
+
return NextResponse.json({ notifications: [] });
|
|
35
|
+
}
|
|
36
|
+
const since = new Date(sinceParam);
|
|
37
|
+
if (Number.isNaN(since.getTime())) {
|
|
38
|
+
return NextResponse.json({ notifications: [] });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const logs = await prisma.integrationImportLog.findMany({
|
|
42
|
+
where: {
|
|
43
|
+
createdAt: { gt: since },
|
|
44
|
+
action: { in: [...NOTIFICATION_ACTIONS] },
|
|
45
|
+
},
|
|
46
|
+
orderBy: { createdAt: 'asc' },
|
|
47
|
+
take: 50,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const notifications = logs.map((log) => ({
|
|
51
|
+
id: log.id,
|
|
52
|
+
integrationType: log.integrationType,
|
|
53
|
+
configName: log.configName,
|
|
54
|
+
action: log.action,
|
|
55
|
+
totalImported: log.totalImported,
|
|
56
|
+
totalDuplicates: log.totalDuplicates,
|
|
57
|
+
totalUpdated: log.totalUpdated,
|
|
58
|
+
totalErrors: log.totalErrors,
|
|
59
|
+
createdAt: log.createdAt,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
return NextResponse.json({ notifications });
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
console.error('Erreur lors de la récupération des notifications d\'intégration:', error);
|
|
65
|
+
return NextResponse.json({ notifications: [] });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -67,7 +67,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
67
67
|
id: config.id,
|
|
68
68
|
name: config.name,
|
|
69
69
|
pageId: config.pageId,
|
|
70
|
-
verifyToken: config.verifyToken,
|
|
71
70
|
active: config.active,
|
|
72
71
|
defaultStatusId: config.defaultStatusId,
|
|
73
72
|
defaultAssignedUserId: config.defaultAssignedUserId,
|
|
@@ -33,7 +33,6 @@ export async function GET(request: NextRequest) {
|
|
|
33
33
|
id: config.id,
|
|
34
34
|
name: config.name,
|
|
35
35
|
pageId: config.pageId,
|
|
36
|
-
verifyToken: config.verifyToken,
|
|
37
36
|
active: config.active,
|
|
38
37
|
defaultStatusId: config.defaultStatusId,
|
|
39
38
|
defaultAssignedUserId: config.defaultAssignedUserId,
|
|
@@ -109,13 +108,26 @@ export async function POST(request: NextRequest) {
|
|
|
109
108
|
},
|
|
110
109
|
});
|
|
111
110
|
|
|
111
|
+
await prisma.integrationImportLog.create({
|
|
112
|
+
data: {
|
|
113
|
+
integrationType: 'meta_lead',
|
|
114
|
+
configId: config.id,
|
|
115
|
+
configName: config.name,
|
|
116
|
+
action: 'created',
|
|
117
|
+
actorId: session.user.id,
|
|
118
|
+
totalImported: 0,
|
|
119
|
+
totalDuplicates: 0,
|
|
120
|
+
totalUpdated: 0,
|
|
121
|
+
totalErrors: 0,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
112
125
|
return NextResponse.json({
|
|
113
126
|
success: true,
|
|
114
127
|
config: {
|
|
115
128
|
id: config.id,
|
|
116
129
|
name: config.name,
|
|
117
130
|
pageId: config.pageId,
|
|
118
|
-
verifyToken: config.verifyToken,
|
|
119
131
|
active: config.active,
|
|
120
132
|
defaultStatusId: config.defaultStatusId,
|
|
121
133
|
defaultAssignedUserId: config.defaultAssignedUserId,
|