create-crm-tmp 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/.prettierignore +2 -0
- package/template/README.md +53 -67
- package/template/components.json +22 -0
- package/template/exemple-contacts.csv +54 -0
- package/template/next.config.ts +27 -1
- package/template/package.json +64 -27
- package/template/prisma/schema.prisma +821 -72
- package/template/skills-lock.json +25 -0
- package/template/src/app/(auth)/invite/[token]/page.tsx +21 -24
- package/template/src/app/(auth)/reset-password/complete/page.tsx +12 -21
- package/template/src/app/(auth)/reset-password/page.tsx +12 -8
- package/template/src/app/(auth)/reset-password/verify/page.tsx +12 -8
- package/template/src/app/(auth)/signin/page.tsx +20 -17
- package/template/src/app/(dashboard)/agenda/page.tsx +2231 -2188
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +680 -323
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
- package/template/src/app/(dashboard)/automatisation/page.tsx +473 -180
- package/template/src/app/(dashboard)/closing/page.tsx +500 -468
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +5035 -4126
- package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1703 -0
- package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +3776 -2064
- package/template/src/app/(dashboard)/dashboard/page.tsx +37 -519
- package/template/src/app/(dashboard)/error.tsx +37 -0
- package/template/src/app/(dashboard)/layout.tsx +1 -1
- 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 +2685 -2489
- package/template/src/app/(dashboard)/templates/page.tsx +500 -300
- package/template/src/app/(dashboard)/users/list/page.tsx +356 -350
- 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 +164 -137
- package/template/src/app/api/audit-logs/route.ts +1 -1
- 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/companies/[id]/activities/route.ts +131 -0
- package/template/src/app/api/companies/[id]/route.ts +195 -0
- package/template/src/app/api/companies/export/route.ts +206 -0
- package/template/src/app/api/companies/route.ts +166 -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 +77 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +7 -17
- package/template/src/app/api/contacts/[id]/files/route.ts +83 -44
- package/template/src/app/api/contacts/[id]/interactions/route.ts +37 -0
- package/template/src/app/api/contacts/[id]/kyc/route.ts +71 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +38 -29
- package/template/src/app/api/contacts/[id]/route.ts +111 -20
- package/template/src/app/api/contacts/[id]/send-email/route.ts +6 -0
- package/template/src/app/api/contacts/[id]/workflows/run/route.ts +61 -0
- package/template/src/app/api/contacts/export/route.ts +12 -17
- package/template/src/app/api/contacts/import/route.ts +22 -19
- package/template/src/app/api/contacts/import-preview/route.ts +139 -0
- package/template/src/app/api/contacts/route.ts +202 -49
- package/template/src/app/api/dashboard/stats/route.ts +9 -292
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +203 -185
- package/template/src/app/api/invite/complete/route.ts +20 -23
- package/template/src/app/api/reminders/route.ts +1 -0
- package/template/src/app/api/reset-password/complete/route.ts +11 -13
- package/template/src/app/api/send/route.ts +9 -85
- 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 +20 -23
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +20 -23
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +23 -32
- package/template/src/app/api/settings/google-sheet/preview/route.ts +104 -0
- package/template/src/app/api/settings/google-sheet/route.ts +20 -23
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -23
- package/template/src/app/api/settings/meta-leads/route.ts +20 -23
- package/template/src/app/api/settings/statuses/[id]/route.ts +33 -23
- 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 +14 -7
- package/template/src/app/api/tasks/[id]/route.ts +161 -137
- package/template/src/app/api/tasks/meet/route.ts +11 -8
- package/template/src/app/api/tasks/route.ts +155 -95
- 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 +16 -1
- 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/route.ts +94 -55
- package/template/src/app/api/webhooks/google-ads/route.ts +20 -1
- package/template/src/app/api/webhooks/meta-leads/route.ts +18 -1
- package/template/src/app/api/workflows/[id]/route.ts +33 -6
- package/template/src/app/api/workflows/process/route.ts +509 -146
- package/template/src/app/api/workflows/route.ts +46 -4
- package/template/src/app/globals.css +210 -101
- package/template/src/app/layout.tsx +19 -8
- package/template/src/app/page.tsx +37 -7
- package/template/src/components/address-autocomplete.tsx +232 -0
- package/template/src/components/contacts/filter-bar.tsx +181 -0
- package/template/src/components/contacts/filter-builder.tsx +589 -0
- package/template/src/components/contacts/save-view-dialog.tsx +160 -0
- package/template/src/components/contacts/views-tab-bar.tsx +440 -0
- package/template/src/components/dashboard/activity-chart.tsx +31 -39
- package/template/src/components/dashboard/dashboard-content.tsx +79 -0
- package/template/src/components/dashboard/stat-card.tsx +40 -42
- package/template/src/components/dashboard/tasks-pie-chart.tsx +34 -37
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +78 -72
- package/template/src/components/date-picker.tsx +396 -0
- package/template/src/components/editor.tsx +27 -13
- package/template/src/components/email-template.tsx +4 -2
- package/template/src/components/global-search.tsx +358 -0
- package/template/src/components/header.tsx +57 -62
- 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/sidebar.tsx +92 -94
- package/template/src/components/skeleton.tsx +128 -42
- 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 +60 -0
- package/template/src/components/view-as-banner.tsx +1 -1
- package/template/src/components/view-as-modal.tsx +21 -16
- package/template/src/config/nav-pages.ts +108 -0
- package/template/src/contexts/app-toast-context.tsx +174 -0
- package/template/src/contexts/sidebar-context.tsx +16 -47
- package/template/src/contexts/task-reminder-context.tsx +6 -6
- package/template/src/contexts/view-as-context.tsx +11 -16
- 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/lib/address-api.ts +155 -0
- package/template/src/lib/cache.ts +73 -0
- package/template/src/lib/check-permission.ts +12 -177
- package/template/src/lib/contact-interactions.ts +3 -1
- package/template/src/lib/contact-view-filters.ts +341 -0
- package/template/src/lib/dashboard-stats.ts +224 -0
- package/template/src/lib/date-utils.ts +49 -0
- package/template/src/lib/get-auth-user.ts +25 -0
- package/template/src/lib/google-calendar.ts +54 -12
- package/template/src/lib/google-drive.ts +796 -75
- package/template/src/lib/google-fetch.ts +63 -0
- package/template/src/lib/local-storage.ts +34 -0
- package/template/src/lib/permissions.ts +245 -47
- package/template/src/lib/prisma.ts +11 -11
- package/template/src/lib/roles.ts +14 -39
- package/template/src/lib/template-variables.ts +67 -33
- package/template/src/lib/utils.ts +26 -2
- package/template/src/lib/workflow-executor.ts +445 -229
- package/template/src/proxy.ts +34 -73
- package/template/src/types/contact-views.ts +351 -0
- package/template/src/types/yousign.ts +52 -0
- package/template/vercel.json +12 -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/migration_lock.toml +0 -3
- package/template/src/app/(dashboard)/users/layout.tsx +0 -30
- package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
- package/template/src/app/api/dashboard/widgets/route.ts +0 -181
- package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
- package/template/src/components/dashboard/color-picker.tsx +0 -65
- package/template/src/components/dashboard/contacts-chart.tsx +0 -69
- package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
- package/template/src/components/dashboard/recent-activity.tsx +0 -157
- package/template/src/components/dashboard/sales-analytics-chart.tsx +0 -77
- package/template/src/components/dashboard/status-distribution-chart.tsx +0 -82
- package/template/src/components/dashboard/top-contacts-list.tsx +0 -119
- package/template/src/components/dashboard/widget-wrapper.tsx +0 -39
- package/template/src/contexts/dashboard-theme-context.tsx +0 -58
- package/template/src/lib/dashboard-themes.ts +0 -140
- package/template/src/lib/default-widgets.ts +0 -14
- package/template/src/lib/widget-registry.ts +0 -177
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
4
|
import { getValidAccessToken } from '@/lib/google-calendar';
|
|
5
|
+
import { indexToColumn } from '@/lib/utils';
|
|
6
|
+
import { googleFetch } from '@/lib/google-fetch';
|
|
5
7
|
|
|
6
8
|
function extractSpreadsheetId(sheetUrlOrId: string): string {
|
|
7
9
|
if (!sheetUrlOrId) return sheetUrlOrId;
|
|
8
10
|
|
|
9
|
-
// Si c'est déjà un ID simple, on le renvoie
|
|
10
11
|
if (!sheetUrlOrId.includes('https://')) {
|
|
11
12
|
return sheetUrlOrId;
|
|
12
13
|
}
|
|
@@ -19,21 +20,18 @@ function extractSpreadsheetId(sheetUrlOrId: string): string {
|
|
|
19
20
|
return sheetUrlOrId;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function indexToColumn(index: number): string {
|
|
23
|
-
let col = '';
|
|
24
|
-
let n = index + 1;
|
|
25
|
-
while (n > 0) {
|
|
26
|
-
const rem = (n - 1) % 26;
|
|
27
|
-
col = String.fromCharCode(65 + rem) + col;
|
|
28
|
-
n = Math.floor((n - 1) / 26);
|
|
29
|
-
}
|
|
30
|
-
return col;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
23
|
// POST /api/settings/google-sheet/auto-map - Proposer un mapping automatique des colonnes
|
|
34
24
|
export async function POST(request: NextRequest) {
|
|
35
25
|
try {
|
|
36
|
-
const session = await
|
|
26
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
27
|
+
if (!session) {
|
|
28
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const hasPermission = await checkPermission('integrations.google_sheets.manage');
|
|
32
|
+
if (!hasPermission) {
|
|
33
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
34
|
+
}
|
|
37
35
|
|
|
38
36
|
const body = await request.json();
|
|
39
37
|
const { sheetUrl, sheetName, headerRow } = body || {};
|
|
@@ -58,16 +56,18 @@ export async function POST(request: NextRequest) {
|
|
|
58
56
|
);
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
// Récupérer le compte Google de l'
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
// Récupérer le compte Google de l'admin pour Google Sheets (utilise Drive)
|
|
60
|
+
// Note: Cette fonction n'existe plus dans google-calendar, utiliser celle de google-drive
|
|
61
|
+
const { getAdminGoogleAccount } = await import('@/lib/google-drive');
|
|
62
|
+
let googleAccount;
|
|
63
|
+
try {
|
|
64
|
+
googleAccount = await getAdminGoogleAccount();
|
|
65
|
+
} catch (error: any) {
|
|
67
66
|
return NextResponse.json(
|
|
68
67
|
{
|
|
69
68
|
error:
|
|
70
|
-
|
|
69
|
+
error.message ||
|
|
70
|
+
'Aucun compte Google configuré. Veuillez demander à un administrateur de connecter son compte Google dans les paramètres.',
|
|
71
71
|
},
|
|
72
72
|
{ status: 400 },
|
|
73
73
|
);
|
|
@@ -80,7 +80,7 @@ export async function POST(request: NextRequest) {
|
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
const range = encodeURIComponent(sheetName);
|
|
83
|
-
const response = await
|
|
83
|
+
const response = await googleFetch(
|
|
84
84
|
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`,
|
|
85
85
|
{
|
|
86
86
|
headers: {
|
|
@@ -184,15 +184,6 @@ export async function POST(request: NextRequest) {
|
|
|
184
184
|
});
|
|
185
185
|
} catch (error: any) {
|
|
186
186
|
console.error('Erreur lors du mapping automatique Google Sheets:', error);
|
|
187
|
-
|
|
188
|
-
if (error.message === 'Non authentifié') {
|
|
189
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
193
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
194
|
-
}
|
|
195
|
-
|
|
196
187
|
return NextResponse.json(
|
|
197
188
|
{ error: error.message || 'Erreur serveur lors du mapping automatique' },
|
|
198
189
|
{ status: 500 },
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { getValidAccessToken } from '@/lib/google-calendar';
|
|
5
|
+
import { googleFetch } from '@/lib/google-fetch';
|
|
6
|
+
|
|
7
|
+
function extractSpreadsheetId(sheetUrlOrId: string): string {
|
|
8
|
+
if (!sheetUrlOrId) return sheetUrlOrId;
|
|
9
|
+
if (!sheetUrlOrId.includes('https://')) return sheetUrlOrId;
|
|
10
|
+
const match = sheetUrlOrId.match(/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);
|
|
11
|
+
return match?.[1] ?? sheetUrlOrId;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(request: NextRequest) {
|
|
15
|
+
try {
|
|
16
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
17
|
+
if (!session) {
|
|
18
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hasPermission = await checkPermission('integrations.google_sheets.manage');
|
|
22
|
+
if (!hasPermission) {
|
|
23
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const body = await request.json();
|
|
27
|
+
const { sheetUrl, sheetName } = body || {};
|
|
28
|
+
|
|
29
|
+
if (!sheetUrl) {
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
{ error: 'Le lien du Google Sheet est obligatoire.' },
|
|
32
|
+
{ status: 400 },
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const spreadsheetId = extractSpreadsheetId(sheetUrl);
|
|
37
|
+
|
|
38
|
+
const { getAdminGoogleAccount } = await import('@/lib/google-drive');
|
|
39
|
+
let googleAccount;
|
|
40
|
+
try {
|
|
41
|
+
googleAccount = await getAdminGoogleAccount();
|
|
42
|
+
} catch (error: unknown) {
|
|
43
|
+
const message = error instanceof Error ? error.message : 'Aucun compte Google configuré.';
|
|
44
|
+
return NextResponse.json({ error: message }, { status: 400 });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const accessToken = await getValidAccessToken(
|
|
48
|
+
googleAccount.accessToken,
|
|
49
|
+
googleAccount.refreshToken,
|
|
50
|
+
googleAccount.tokenExpiresAt,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Fetch spreadsheet metadata to get all sheet names
|
|
54
|
+
const metaResponse = await googleFetch(
|
|
55
|
+
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}?fields=sheets.properties.title`,
|
|
56
|
+
{ headers: { Authorization: `Bearer ${accessToken}` } },
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (!metaResponse.ok) {
|
|
60
|
+
return NextResponse.json(
|
|
61
|
+
{ error: 'Impossible de lire les métadonnées du Google Sheet.' },
|
|
62
|
+
{ status: 400 },
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const metaData = await metaResponse.json();
|
|
67
|
+
const sheetNames: string[] = (metaData.sheets || []).map(
|
|
68
|
+
(s: { properties: { title: string } }) => s.properties.title,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (sheetNames.length === 0) {
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: 'Le Google Sheet ne contient aucune feuille.' },
|
|
74
|
+
{ status: 400 },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const targetSheet = sheetName && sheetNames.includes(sheetName) ? sheetName : sheetNames[0];
|
|
79
|
+
|
|
80
|
+
const range = encodeURIComponent(targetSheet);
|
|
81
|
+
const dataResponse = await googleFetch(
|
|
82
|
+
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`,
|
|
83
|
+
{ headers: { Authorization: `Bearer ${accessToken}` } },
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (!dataResponse.ok) {
|
|
87
|
+
return NextResponse.json(
|
|
88
|
+
{ error: 'Impossible de lire les données de la feuille.' },
|
|
89
|
+
{ status: 400 },
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const data = await dataResponse.json();
|
|
94
|
+
const values: string[][] = data.values || [];
|
|
95
|
+
|
|
96
|
+
const rawRows = values.slice(0, 20).map((row) => row.map((cell) => String(cell ?? '')));
|
|
97
|
+
|
|
98
|
+
return NextResponse.json({ sheetNames, rawRows });
|
|
99
|
+
} catch (error: unknown) {
|
|
100
|
+
console.error('Erreur google-sheet preview:', error);
|
|
101
|
+
const message = error instanceof Error ? error.message : 'Erreur serveur';
|
|
102
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { prisma } from '@/lib/prisma';
|
|
3
|
-
import {
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
4
5
|
|
|
5
6
|
function extractSpreadsheetId(sheetUrlOrId: string): string {
|
|
6
7
|
if (!sheetUrlOrId) return sheetUrlOrId;
|
|
@@ -18,10 +19,17 @@ function extractSpreadsheetId(sheetUrlOrId: string): string {
|
|
|
18
19
|
return sheetUrlOrId;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
// GET /api/settings/google-sheet - Récupérer toutes les configurations Google Sheets
|
|
22
|
+
// GET /api/settings/google-sheet - Récupérer toutes les configurations Google Sheets
|
|
22
23
|
export async function GET(request: NextRequest) {
|
|
23
24
|
try {
|
|
24
|
-
await
|
|
25
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
26
|
+
if (!session) {
|
|
27
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
28
|
+
}
|
|
29
|
+
const hasPermission = await checkPermission('integrations.google_sheets.manage');
|
|
30
|
+
if (!hasPermission) {
|
|
31
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
32
|
+
}
|
|
25
33
|
|
|
26
34
|
const client = prisma as any;
|
|
27
35
|
|
|
@@ -76,23 +84,21 @@ export async function GET(request: NextRequest) {
|
|
|
76
84
|
);
|
|
77
85
|
} catch (error: any) {
|
|
78
86
|
console.error('Erreur lors de la récupération des configurations Google Sheets:', error);
|
|
79
|
-
|
|
80
|
-
if (error.message === 'Non authentifié') {
|
|
81
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
85
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
87
|
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
// POST /api/settings/google-sheet - Créer une nouvelle configuration
|
|
91
|
+
// POST /api/settings/google-sheet - Créer une nouvelle configuration
|
|
93
92
|
export async function POST(request: NextRequest) {
|
|
94
93
|
try {
|
|
95
|
-
const session = await
|
|
94
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
95
|
+
if (!session) {
|
|
96
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
97
|
+
}
|
|
98
|
+
const hasPermission = await checkPermission('integrations.google_sheets.manage');
|
|
99
|
+
if (!hasPermission) {
|
|
100
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
101
|
+
}
|
|
96
102
|
|
|
97
103
|
const body = await request.json();
|
|
98
104
|
const {
|
|
@@ -240,15 +246,6 @@ export async function POST(request: NextRequest) {
|
|
|
240
246
|
});
|
|
241
247
|
} catch (error: any) {
|
|
242
248
|
console.error('Erreur lors de la création de la configuration Google Sheets:', error);
|
|
243
|
-
|
|
244
|
-
if (error.message === 'Non authentifié') {
|
|
245
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
249
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
250
|
-
}
|
|
251
|
-
|
|
252
249
|
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
253
250
|
}
|
|
254
251
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { prisma } from '@/lib/prisma';
|
|
3
|
-
import {
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
4
5
|
import { encrypt } from '@/lib/encryption';
|
|
5
6
|
|
|
6
|
-
// PUT /api/settings/meta-leads/[id] - Mettre à jour une configuration
|
|
7
|
+
// PUT /api/settings/meta-leads/[id] - Mettre à jour une configuration
|
|
7
8
|
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
8
9
|
try {
|
|
9
|
-
await
|
|
10
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
11
|
+
if (!session) {
|
|
12
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
13
|
+
}
|
|
14
|
+
const hasPermission = await checkPermission('integrations.meta.manage');
|
|
15
|
+
if (!hasPermission) {
|
|
16
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
17
|
+
}
|
|
10
18
|
|
|
11
19
|
const { id } = await params;
|
|
12
20
|
const body = await request.json();
|
|
@@ -68,26 +76,24 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
68
76
|
});
|
|
69
77
|
} catch (error: any) {
|
|
70
78
|
console.error('Erreur lors de la mise à jour de la configuration Meta Lead Ads:', error);
|
|
71
|
-
|
|
72
|
-
if (error.message === 'Non authentifié') {
|
|
73
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
77
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
79
|
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
// DELETE /api/settings/meta-leads/[id] - Supprimer une configuration
|
|
83
|
+
// DELETE /api/settings/meta-leads/[id] - Supprimer une configuration
|
|
85
84
|
export async function DELETE(
|
|
86
85
|
request: NextRequest,
|
|
87
86
|
{ params }: { params: Promise<{ id: string }> },
|
|
88
87
|
) {
|
|
89
88
|
try {
|
|
90
|
-
await
|
|
89
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
90
|
+
if (!session) {
|
|
91
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
92
|
+
}
|
|
93
|
+
const hasPermission = await checkPermission('integrations.meta.manage');
|
|
94
|
+
if (!hasPermission) {
|
|
95
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
96
|
+
}
|
|
91
97
|
|
|
92
98
|
const { id } = await params;
|
|
93
99
|
|
|
@@ -109,15 +115,6 @@ export async function DELETE(
|
|
|
109
115
|
});
|
|
110
116
|
} catch (error: any) {
|
|
111
117
|
console.error('Erreur lors de la suppression de la configuration Meta Lead Ads:', error);
|
|
112
|
-
|
|
113
|
-
if (error.message === 'Non authentifié') {
|
|
114
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
118
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
118
|
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
122
119
|
}
|
|
123
120
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { prisma } from '@/lib/prisma';
|
|
3
|
-
import {
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
4
5
|
import { encrypt } from '@/lib/encryption';
|
|
5
6
|
|
|
6
|
-
// GET /api/settings/meta-leads - Récupérer toutes les configurations Meta Lead Ads
|
|
7
|
+
// GET /api/settings/meta-leads - Récupérer toutes les configurations Meta Lead Ads
|
|
7
8
|
export async function GET(request: NextRequest) {
|
|
8
9
|
try {
|
|
9
|
-
await
|
|
10
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
11
|
+
if (!session) {
|
|
12
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
13
|
+
}
|
|
14
|
+
const hasPermission = await checkPermission('integrations.meta.manage');
|
|
15
|
+
if (!hasPermission) {
|
|
16
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
17
|
+
}
|
|
10
18
|
|
|
11
19
|
const configs = await prisma.metaLeadConfig.findMany({
|
|
12
20
|
include: {
|
|
@@ -41,23 +49,21 @@ export async function GET(request: NextRequest) {
|
|
|
41
49
|
);
|
|
42
50
|
} catch (error: any) {
|
|
43
51
|
console.error('Erreur lors de la récupération des configurations Meta Lead Ads:', error);
|
|
44
|
-
|
|
45
|
-
if (error.message === 'Non authentifié') {
|
|
46
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
50
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
52
|
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
// POST /api/settings/meta-leads - Créer une nouvelle configuration
|
|
56
|
+
// POST /api/settings/meta-leads - Créer une nouvelle configuration
|
|
58
57
|
export async function POST(request: NextRequest) {
|
|
59
58
|
try {
|
|
60
|
-
await
|
|
59
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
60
|
+
if (!session) {
|
|
61
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
62
|
+
}
|
|
63
|
+
const hasPermission = await checkPermission('integrations.meta.manage');
|
|
64
|
+
if (!hasPermission) {
|
|
65
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
66
|
+
}
|
|
61
67
|
|
|
62
68
|
const body = await request.json();
|
|
63
69
|
const {
|
|
@@ -118,15 +124,6 @@ export async function POST(request: NextRequest) {
|
|
|
118
124
|
});
|
|
119
125
|
} catch (error: any) {
|
|
120
126
|
console.error('Erreur lors de la création de la configuration Meta Lead Ads:', error);
|
|
121
|
-
|
|
122
|
-
if (error.message === 'Non authentifié') {
|
|
123
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
127
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
128
|
-
}
|
|
129
|
-
|
|
130
127
|
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
131
128
|
}
|
|
132
129
|
}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { prisma } from '@/lib/prisma';
|
|
3
|
-
import {
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
5
|
+
import { invalidateCachePrefix } from '@/lib/cache';
|
|
4
6
|
|
|
5
7
|
// PUT /api/settings/statuses/[id] - Mettre à jour un statut
|
|
6
8
|
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
7
9
|
try {
|
|
8
|
-
await
|
|
10
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
11
|
+
if (!session) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
12
|
+
const canManage = await checkPermission('settings.status.manage');
|
|
13
|
+
if (!canManage) return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
14
|
+
|
|
9
15
|
const { id } = await params;
|
|
10
16
|
|
|
11
17
|
const body = await request.json();
|
|
12
|
-
const { name, color, order } = body;
|
|
18
|
+
const { name, color, order, requiresClosingReason } = body;
|
|
13
19
|
|
|
14
20
|
// Validation
|
|
15
21
|
if (!name || !color) {
|
|
@@ -25,6 +31,13 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
25
31
|
return NextResponse.json({ error: 'Statut non trouvé' }, { status: 404 });
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
if (existing.isSystem && name !== existing.name) {
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
{ error: 'Ce statut est utilisé par le système et ne peut pas être renommé' },
|
|
37
|
+
{ status: 403 },
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
// Vérifier si le nom existe déjà pour un autre statut
|
|
29
42
|
const nameConflict = await prisma.status.findFirst({
|
|
30
43
|
where: {
|
|
@@ -43,21 +56,15 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
43
56
|
name,
|
|
44
57
|
color,
|
|
45
58
|
...(order !== undefined && { order }),
|
|
59
|
+
...(requiresClosingReason !== undefined && { requiresClosingReason }),
|
|
46
60
|
},
|
|
47
61
|
});
|
|
48
62
|
|
|
63
|
+
invalidateCachePrefix('status');
|
|
64
|
+
|
|
49
65
|
return NextResponse.json(updatedStatus);
|
|
50
66
|
} catch (error: any) {
|
|
51
67
|
console.error('Erreur lors de la mise à jour du statut:', error);
|
|
52
|
-
|
|
53
|
-
if (error.message === 'Non authentifié') {
|
|
54
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
58
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
68
|
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
62
69
|
}
|
|
63
70
|
}
|
|
@@ -68,10 +75,13 @@ export async function DELETE(
|
|
|
68
75
|
{ params }: { params: Promise<{ id: string }> },
|
|
69
76
|
) {
|
|
70
77
|
try {
|
|
71
|
-
await
|
|
78
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
79
|
+
if (!session) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
80
|
+
const canManage = await checkPermission('settings.status.manage');
|
|
81
|
+
if (!canManage) return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
82
|
+
|
|
72
83
|
const { id } = await params;
|
|
73
84
|
|
|
74
|
-
// Vérifier si le statut existe
|
|
75
85
|
const existing = await prisma.status.findUnique({
|
|
76
86
|
where: { id },
|
|
77
87
|
});
|
|
@@ -80,22 +90,22 @@ export async function DELETE(
|
|
|
80
90
|
return NextResponse.json({ error: 'Statut non trouvé' }, { status: 404 });
|
|
81
91
|
}
|
|
82
92
|
|
|
93
|
+
if (existing.isSystem) {
|
|
94
|
+
return NextResponse.json(
|
|
95
|
+
{ error: 'Ce statut est utilisé par le système et ne peut pas être supprimé' },
|
|
96
|
+
{ status: 403 },
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
83
100
|
await prisma.status.delete({
|
|
84
101
|
where: { id },
|
|
85
102
|
});
|
|
86
103
|
|
|
104
|
+
invalidateCachePrefix('status');
|
|
105
|
+
|
|
87
106
|
return NextResponse.json({ success: true, message: 'Statut supprimé avec succès' });
|
|
88
107
|
} catch (error: any) {
|
|
89
108
|
console.error('Erreur lors de la suppression du statut:', error);
|
|
90
|
-
|
|
91
|
-
if (error.message === 'Non authentifié') {
|
|
92
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
96
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
109
|
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
100
110
|
}
|
|
101
111
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { prisma } from '@/lib/prisma';
|
|
3
|
-
import {
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
5
|
+
import { invalidateCachePrefix } from '@/lib/cache';
|
|
4
6
|
|
|
5
7
|
// GET /api/settings/statuses - Récupérer tous les statuts
|
|
6
8
|
export async function GET(request: NextRequest) {
|
|
7
9
|
try {
|
|
8
|
-
await
|
|
10
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
11
|
+
if (!session) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
12
|
+
const canManage = await checkPermission('settings.status.manage');
|
|
13
|
+
if (!canManage) return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
9
14
|
|
|
10
15
|
const statuses = await prisma.status.findMany({
|
|
11
16
|
orderBy: { order: 'asc' },
|
|
@@ -14,15 +19,6 @@ export async function GET(request: NextRequest) {
|
|
|
14
19
|
return NextResponse.json(statuses);
|
|
15
20
|
} catch (error: any) {
|
|
16
21
|
console.error('Erreur lors de la récupération des statuts:', error);
|
|
17
|
-
|
|
18
|
-
if (error.message === 'Non authentifié') {
|
|
19
|
-
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (error.message === 'Permissions insuffisantes') {
|
|
23
|
-
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
22
|
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
27
23
|
}
|
|
28
24
|
}
|
|
@@ -30,10 +26,13 @@ export async function GET(request: NextRequest) {
|
|
|
30
26
|
// POST /api/settings/statuses - Créer un nouveau statut
|
|
31
27
|
export async function POST(request: NextRequest) {
|
|
32
28
|
try {
|
|
33
|
-
await
|
|
29
|
+
const session = await auth.api.getSession({ headers: request.headers });
|
|
30
|
+
if (!session) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
31
|
+
const canManage = await checkPermission('settings.status.manage');
|
|
32
|
+
if (!canManage) return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
34
33
|
|
|
35
34
|
const body = await request.json();
|
|
36
|
-
const { name, color, order } = body;
|
|
35
|
+
const { name, color, order, requiresClosingReason } = body;
|
|
37
36
|
|
|
38
37
|
// Validation
|
|
39
38
|
if (!name || !color) {
|
|
@@ -63,21 +62,24 @@ export async function POST(request: NextRequest) {
|
|
|
63
62
|
name,
|
|
64
63
|
color,
|
|
65
64
|
order: statusOrder,
|
|
65
|
+
...(requiresClosingReason !== undefined && { requiresClosingReason }),
|
|
66
66
|
},
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
invalidateCachePrefix('status');
|
|
70
|
+
|
|
69
71
|
return NextResponse.json(status, { status: 201 });
|
|
70
72
|
} catch (error: any) {
|
|
71
73
|
console.error('Erreur lors de la création du statut:', error);
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
return NextResponse.json(
|
|
76
|
+
{
|
|
77
|
+
error:
|
|
78
|
+
process.env.NODE_ENV === 'development'
|
|
79
|
+
? error.message || 'Erreur serveur'
|
|
80
|
+
: 'Erreur serveur',
|
|
81
|
+
},
|
|
82
|
+
{ status: 500 },
|
|
83
|
+
);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { auth } from '@/lib/auth';
|
|
3
|
-
import {
|
|
3
|
+
import { getCachedStatuses } from '@/lib/cache';
|
|
4
4
|
|
|
5
|
-
// GET /api/statuses - Récupérer tous les statuts (accessible à tous les utilisateurs authentifiés)
|
|
6
5
|
export async function GET(request: NextRequest) {
|
|
7
6
|
try {
|
|
8
7
|
const session = await auth.api.getSession({
|
|
@@ -13,9 +12,7 @@ export async function GET(request: NextRequest) {
|
|
|
13
12
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
const statuses = await
|
|
17
|
-
orderBy: { order: 'asc' },
|
|
18
|
-
});
|
|
15
|
+
const statuses = await getCachedStatuses();
|
|
19
16
|
|
|
20
17
|
return NextResponse.json(statuses);
|
|
21
18
|
} catch (error: any) {
|
|
@@ -45,13 +45,20 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
|
|
|
45
45
|
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// Récupérer le compte Google
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return NextResponse.json(
|
|
48
|
+
// Récupérer le compte Google de l'utilisateur courant
|
|
49
|
+
const { getUserGoogleAccount } = await import('@/lib/google-calendar');
|
|
50
|
+
let googleAccount;
|
|
51
|
+
try {
|
|
52
|
+
googleAccount = await getUserGoogleAccount(session.user.id);
|
|
53
|
+
} catch (error: any) {
|
|
54
|
+
return NextResponse.json(
|
|
55
|
+
{
|
|
56
|
+
error:
|
|
57
|
+
error.message ||
|
|
58
|
+
'Veuillez connecter votre compte Google dans les paramètres pour utiliser Google Calendar.',
|
|
59
|
+
},
|
|
60
|
+
{ status: 400 },
|
|
61
|
+
);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
// Obtenir un token valide
|