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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { auth } from '@/lib/auth';
|
|
3
3
|
import { prisma } from '@/lib/prisma';
|
|
4
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
5
|
|
|
5
|
-
// GET /api/templates/[id] - Récupérer un template spécifique
|
|
6
|
+
// GET /api/templates/[id] - Récupérer un template spécifique (commun à tous les utilisateurs)
|
|
6
7
|
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
7
8
|
try {
|
|
8
9
|
const session = await auth.api.getSession({
|
|
@@ -13,12 +14,16 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
|
|
|
13
14
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
const canView = await checkPermission('templates.view');
|
|
18
|
+
if (!canView) {
|
|
19
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
const { id } = await params;
|
|
17
23
|
|
|
18
|
-
const template = await prisma.template.
|
|
24
|
+
const template = await prisma.template.findUnique({
|
|
19
25
|
where: {
|
|
20
26
|
id,
|
|
21
|
-
userId: session.user.id,
|
|
22
27
|
},
|
|
23
28
|
});
|
|
24
29
|
|
|
@@ -33,7 +38,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
|
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
// PUT /api/templates/[id] - Mettre à jour un template
|
|
41
|
+
// PUT /api/templates/[id] - Mettre à jour un template (commun à tous les utilisateurs)
|
|
37
42
|
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
38
43
|
try {
|
|
39
44
|
const session = await auth.api.getSession({
|
|
@@ -44,15 +49,18 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
44
49
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
const canEdit = await checkPermission('templates.edit');
|
|
53
|
+
if (!canEdit) {
|
|
54
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
const { id } = await params;
|
|
48
58
|
const body = await request.json();
|
|
49
59
|
const { name, type, subject, content } = body;
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
const existing = await prisma.template.findFirst({
|
|
61
|
+
const existing = await prisma.template.findUnique({
|
|
53
62
|
where: {
|
|
54
63
|
id,
|
|
55
|
-
userId: session.user.id,
|
|
56
64
|
},
|
|
57
65
|
});
|
|
58
66
|
|
|
@@ -60,7 +68,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
60
68
|
return NextResponse.json({ error: 'Template non trouvé' }, { status: 404 });
|
|
61
69
|
}
|
|
62
70
|
|
|
63
|
-
// Validation
|
|
64
71
|
if (!name || !type || !content) {
|
|
65
72
|
return NextResponse.json(
|
|
66
73
|
{ error: 'Le nom, le type et le contenu sont requis' },
|
|
@@ -75,7 +82,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
75
82
|
);
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
// Pour EMAIL, le sujet est requis
|
|
79
85
|
if (type === 'EMAIL' && !subject) {
|
|
80
86
|
return NextResponse.json(
|
|
81
87
|
{ error: 'Le sujet est requis pour les templates EMAIL' },
|
|
@@ -100,7 +106,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
|
|
103
|
-
// DELETE /api/templates/[id] - Supprimer un template
|
|
109
|
+
// DELETE /api/templates/[id] - Supprimer un template (commun à tous les utilisateurs)
|
|
104
110
|
export async function DELETE(
|
|
105
111
|
request: NextRequest,
|
|
106
112
|
{ params }: { params: Promise<{ id: string }> },
|
|
@@ -114,13 +120,16 @@ export async function DELETE(
|
|
|
114
120
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
115
121
|
}
|
|
116
122
|
|
|
123
|
+
const canDelete = await checkPermission('templates.delete');
|
|
124
|
+
if (!canDelete) {
|
|
125
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
126
|
+
}
|
|
127
|
+
|
|
117
128
|
const { id } = await params;
|
|
118
129
|
|
|
119
|
-
|
|
120
|
-
const existing = await prisma.template.findFirst({
|
|
130
|
+
const existing = await prisma.template.findUnique({
|
|
121
131
|
where: {
|
|
122
132
|
id,
|
|
123
|
-
userId: session.user.id,
|
|
124
133
|
},
|
|
125
134
|
});
|
|
126
135
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { auth } from '@/lib/auth';
|
|
3
3
|
import { prisma } from '@/lib/prisma';
|
|
4
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
5
|
|
|
5
|
-
// GET /api/templates - Récupérer tous les templates
|
|
6
|
+
// GET /api/templates - Récupérer tous les templates (communs à tous les utilisateurs)
|
|
6
7
|
export async function GET(request: NextRequest) {
|
|
7
8
|
try {
|
|
8
9
|
const session = await auth.api.getSession({
|
|
@@ -13,12 +14,15 @@ export async function GET(request: NextRequest) {
|
|
|
13
14
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
const canView = await checkPermission('templates.view');
|
|
18
|
+
if (!canView) {
|
|
19
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
const { searchParams } = new URL(request.url);
|
|
17
23
|
const type = searchParams.get('type'); // EMAIL, SMS, NOTE
|
|
18
24
|
|
|
19
|
-
const where: any = {
|
|
20
|
-
userId: session.user.id,
|
|
21
|
-
};
|
|
25
|
+
const where: any = {};
|
|
22
26
|
|
|
23
27
|
if (type) {
|
|
24
28
|
where.type = type;
|
|
@@ -47,6 +51,11 @@ export async function POST(request: NextRequest) {
|
|
|
47
51
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
const canCreate = await checkPermission('templates.create');
|
|
55
|
+
if (!canCreate) {
|
|
56
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
const body = await request.json();
|
|
51
60
|
const { name, type, subject, content } = body;
|
|
52
61
|
|
|
@@ -86,6 +95,14 @@ export async function POST(request: NextRequest) {
|
|
|
86
95
|
return NextResponse.json(template, { status: 201 });
|
|
87
96
|
} catch (error: any) {
|
|
88
97
|
console.error('Erreur lors de la création du template:', error);
|
|
89
|
-
return NextResponse.json(
|
|
98
|
+
return NextResponse.json(
|
|
99
|
+
{
|
|
100
|
+
error:
|
|
101
|
+
process.env.NODE_ENV === 'development'
|
|
102
|
+
? error.message || 'Erreur serveur'
|
|
103
|
+
: 'Erreur serveur',
|
|
104
|
+
},
|
|
105
|
+
{ status: 500 },
|
|
106
|
+
);
|
|
90
107
|
}
|
|
91
108
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { prisma } from '@/lib/prisma';
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
5
|
+
|
|
6
|
+
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
7
|
+
try {
|
|
8
|
+
const [session, { id }] = await Promise.all([
|
|
9
|
+
auth.api.getSession({ headers: request.headers }),
|
|
10
|
+
params,
|
|
11
|
+
]);
|
|
12
|
+
if (!session) {
|
|
13
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const hasPermission = await checkPermission('users.create');
|
|
17
|
+
if (!hasPermission) {
|
|
18
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const user = await prisma.user.findUnique({
|
|
22
|
+
where: { id },
|
|
23
|
+
include: {
|
|
24
|
+
accounts: {
|
|
25
|
+
where: { providerId: 'credential' },
|
|
26
|
+
select: { id: true },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!user) {
|
|
32
|
+
return NextResponse.json({ error: 'Utilisateur non trouvé' }, { status: 404 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (user.accounts.length > 0 || user.emailVerified) {
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ error: 'Cet utilisateur a déjà complété son inscription' },
|
|
38
|
+
{ status: 400 },
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const token = crypto.randomUUID();
|
|
43
|
+
const expiresAt = new Date();
|
|
44
|
+
expiresAt.setDate(expiresAt.getDate() + 1);
|
|
45
|
+
|
|
46
|
+
await prisma.$transaction([
|
|
47
|
+
prisma.verification.deleteMany({
|
|
48
|
+
where: { identifier: user.email },
|
|
49
|
+
}),
|
|
50
|
+
prisma.verification.create({
|
|
51
|
+
data: {
|
|
52
|
+
id: crypto.randomUUID(),
|
|
53
|
+
identifier: user.email,
|
|
54
|
+
value: token,
|
|
55
|
+
expiresAt,
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const baseUrl = process.env.BETTER_AUTH_URL || 'http://localhost:3000';
|
|
61
|
+
const invitationUrl = `${baseUrl}/invite/${token}`;
|
|
62
|
+
|
|
63
|
+
const cookieHeader = request.headers.get('cookie') || '';
|
|
64
|
+
const emailResponse = await fetch(`${baseUrl}/api/send`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
Cookie: cookieHeader,
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
to: user.email,
|
|
72
|
+
subject: 'Invitation à rejoindre le CRM',
|
|
73
|
+
template: 'invitation',
|
|
74
|
+
invitationUrl,
|
|
75
|
+
name: user.name,
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!emailResponse.ok) {
|
|
80
|
+
const errorData = await emailResponse.json().catch(() => ({}));
|
|
81
|
+
console.error("Erreur lors de l'envoi de l'email de réinvitation:", errorData);
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{ error: errorData.error || "Erreur lors de l'envoi de l'email" },
|
|
84
|
+
{ status: 500 },
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return NextResponse.json({
|
|
89
|
+
message: "Email d'invitation renvoyé avec succès",
|
|
90
|
+
});
|
|
91
|
+
} catch (error: any) {
|
|
92
|
+
console.error("Erreur lors du renvoi de l'invitation:", error);
|
|
93
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -95,28 +95,34 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
|
|
95
95
|
where: { id: customRoleId },
|
|
96
96
|
select: { name: true },
|
|
97
97
|
});
|
|
98
|
-
resolvedRole = resolveRoleFromCustomRoleName(customRole?.name
|
|
98
|
+
resolvedRole = resolveRoleFromCustomRoleName(customRole?.name);
|
|
99
99
|
} else {
|
|
100
|
-
resolvedRole =
|
|
100
|
+
resolvedRole = 'USER';
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const updatedUser = await prisma
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
104
|
+
const updatedUser = await prisma.$transaction(async (tx) => {
|
|
105
|
+
const userRow = await tx.user.update({
|
|
106
|
+
where: { id },
|
|
107
|
+
data: {
|
|
108
|
+
...(name && { name }),
|
|
109
|
+
...(customRoleId !== undefined && { customRoleId: customRoleId || null }),
|
|
110
|
+
...(resolvedRole && { role: resolvedRole as any }),
|
|
111
|
+
...(typeof active === 'boolean' && { active }),
|
|
112
|
+
},
|
|
113
|
+
include: {
|
|
114
|
+
customRole: {
|
|
115
|
+
select: {
|
|
116
|
+
id: true,
|
|
117
|
+
name: true,
|
|
118
|
+
},
|
|
117
119
|
},
|
|
118
120
|
},
|
|
119
|
-
}
|
|
121
|
+
});
|
|
122
|
+
if (typeof active === 'boolean' && active === false && existingUser.active) {
|
|
123
|
+
await tx.session.deleteMany({ where: { userId: id } });
|
|
124
|
+
}
|
|
125
|
+
return userRow;
|
|
120
126
|
});
|
|
121
127
|
|
|
122
128
|
// Récupérer les noms des profils pour les métadonnées
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma } from '@/lib/prisma';
|
|
4
|
+
|
|
5
|
+
// GET /api/users/commercials - Récupérer la liste des commerciaux (accessible à tous les utilisateurs authentifiés)
|
|
6
|
+
export async function GET(request: NextRequest) {
|
|
7
|
+
try {
|
|
8
|
+
const session = await auth.api.getSession({
|
|
9
|
+
headers: request.headers,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!session) {
|
|
13
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Récupérer tous les utilisateurs avec le rôle COMMERCIAL, ADMIN ou MANAGER
|
|
17
|
+
const users = await prisma.user.findMany({
|
|
18
|
+
where: {
|
|
19
|
+
active: true,
|
|
20
|
+
role: {
|
|
21
|
+
in: ['COMMERCIAL', 'ADMIN', 'MANAGER'],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
select: {
|
|
25
|
+
id: true,
|
|
26
|
+
name: true,
|
|
27
|
+
email: true,
|
|
28
|
+
role: true,
|
|
29
|
+
},
|
|
30
|
+
orderBy: { name: 'asc' },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return NextResponse.json(users);
|
|
34
|
+
} catch (error: any) {
|
|
35
|
+
console.error('Erreur lors de la récupération des commerciaux:', error);
|
|
36
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -3,7 +3,7 @@ import { auth } from '@/lib/auth';
|
|
|
3
3
|
import { prisma } from '@/lib/prisma';
|
|
4
4
|
import { checkPermission } from '@/lib/check-permission';
|
|
5
5
|
|
|
6
|
-
// GET /api/users/for-agenda - Liste les utilisateurs
|
|
6
|
+
// GET /api/users/for-agenda - Liste les utilisateurs pour le filtre de l'agenda
|
|
7
7
|
export async function GET(request: NextRequest) {
|
|
8
8
|
try {
|
|
9
9
|
const session = await auth.api.getSession({
|
|
@@ -25,7 +25,6 @@ export async function GET(request: NextRequest) {
|
|
|
25
25
|
id: true,
|
|
26
26
|
name: true,
|
|
27
27
|
email: true,
|
|
28
|
-
eventColor: true,
|
|
29
28
|
},
|
|
30
29
|
orderBy: {
|
|
31
30
|
name: 'asc',
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { auth } from '@/lib/auth';
|
|
3
|
-
import { prisma } from '@/lib/prisma';
|
|
3
|
+
import { prisma, type Role } from '@/lib/prisma';
|
|
4
4
|
import { checkPermission } from '@/lib/check-permission';
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// Tous les rôles assignables (permission contacts.assign)
|
|
7
|
+
const ASSIGNABLE_ROLES: Role[] = ['ADMIN', 'MANAGER', 'COMMERCIAL', 'TELEPRO'];
|
|
8
|
+
// Uniquement commercial et télépro (permission contacts.assign_to_sales)
|
|
9
|
+
const SALES_ROLES: Role[] = ['COMMERCIAL', 'TELEPRO'];
|
|
10
|
+
|
|
11
|
+
// GET /api/users/list - Liste des utilisateurs (admins: tous ; édition contacts: liste pour attribution)
|
|
7
12
|
export async function GET(request: NextRequest) {
|
|
8
13
|
try {
|
|
9
14
|
const session = await auth.api.getSession({
|
|
@@ -14,28 +19,61 @@ export async function GET(request: NextRequest) {
|
|
|
14
19
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
const [canManageRoles, canAssignContactsFull, canAssignContactsToSales, canAssignTasksFull, canAssignTasksToSales, canEditOwn, canEditAll] = await Promise.all([
|
|
23
|
+
checkPermission('users.manage_roles'),
|
|
24
|
+
checkPermission('contacts.assign'),
|
|
25
|
+
checkPermission('contacts.assign_to_sales'),
|
|
26
|
+
checkPermission('tasks.assign'),
|
|
27
|
+
checkPermission('tasks.assign_to_sales'),
|
|
28
|
+
checkPermission('contacts.edit_own'),
|
|
29
|
+
checkPermission('contacts.edit_all'),
|
|
30
|
+
]);
|
|
31
|
+
const canEditContacts = canEditOwn || canEditAll;
|
|
32
|
+
|
|
33
|
+
const canAssignFull = canAssignContactsFull || canAssignTasksFull;
|
|
34
|
+
const canAssignToSales = canAssignContactsToSales || canAssignTasksToSales;
|
|
35
|
+
|
|
36
|
+
const canAccessList =
|
|
37
|
+
canManageRoles ||
|
|
38
|
+
canAssignFull ||
|
|
39
|
+
canAssignToSales ||
|
|
40
|
+
canEditContacts;
|
|
41
|
+
if (!canAccessList) {
|
|
20
42
|
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
21
43
|
}
|
|
22
44
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
permissions: true,
|
|
34
|
-
},
|
|
45
|
+
const select = {
|
|
46
|
+
id: true,
|
|
47
|
+
name: true,
|
|
48
|
+
email: true,
|
|
49
|
+
role: true,
|
|
50
|
+
customRole: {
|
|
51
|
+
select: {
|
|
52
|
+
id: true,
|
|
53
|
+
name: true,
|
|
54
|
+
permissions: true,
|
|
35
55
|
},
|
|
36
56
|
},
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Déterminer les rôles à inclure : admins = tous ; assign (contacts ou tâches) full = ASSIGNABLE_ROLES ; sinon SALES_ROLES
|
|
60
|
+
const assignableRoles = canManageRoles
|
|
61
|
+
? undefined
|
|
62
|
+
: canAssignFull
|
|
63
|
+
? ASSIGNABLE_ROLES
|
|
64
|
+
: SALES_ROLES;
|
|
65
|
+
|
|
66
|
+
const users =
|
|
67
|
+
assignableRoles === undefined
|
|
68
|
+
? await prisma.user.findMany({
|
|
69
|
+
select,
|
|
70
|
+
orderBy: { name: 'asc' },
|
|
71
|
+
})
|
|
72
|
+
: await prisma.user.findMany({
|
|
73
|
+
where: { role: { in: assignableRoles }, active: true },
|
|
74
|
+
select,
|
|
75
|
+
orderBy: { name: 'asc' },
|
|
76
|
+
});
|
|
39
77
|
|
|
40
78
|
return NextResponse.json(users);
|
|
41
79
|
} catch (error: any) {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
import { prisma } from '@/lib/prisma';
|
|
3
4
|
import { checkPermission } from '@/lib/check-permission';
|
|
4
5
|
import { auth } from '@/lib/auth';
|
|
5
6
|
import { logAudit } from '@/lib/audit-log';
|
|
6
7
|
import { resolveRoleFromCustomRoleName } from '@/lib/roles';
|
|
7
8
|
|
|
9
|
+
const createUserSchema = z.object({
|
|
10
|
+
name: z.string().trim().min(1, 'Le nom est requis'),
|
|
11
|
+
email: z.string().trim().email('Email invalide'),
|
|
12
|
+
customRoleId: z.string().trim().min(1, 'Le profil est requis'),
|
|
13
|
+
});
|
|
14
|
+
|
|
8
15
|
// GET /api/users - Liste tous les utilisateurs (admin seulement)
|
|
9
16
|
export async function GET(request: NextRequest) {
|
|
10
17
|
try {
|
|
@@ -30,27 +37,68 @@ export async function GET(request: NextRequest) {
|
|
|
30
37
|
name: true,
|
|
31
38
|
},
|
|
32
39
|
},
|
|
40
|
+
accounts: {
|
|
41
|
+
where: { providerId: 'credential' },
|
|
42
|
+
select: { id: true },
|
|
43
|
+
},
|
|
33
44
|
},
|
|
34
45
|
orderBy: {
|
|
35
46
|
createdAt: 'desc',
|
|
36
47
|
},
|
|
37
48
|
});
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const emails: string[] = [];
|
|
51
|
+
for (const u of users) {
|
|
52
|
+
if (!u.emailVerified && u.accounts.length === 0) {
|
|
53
|
+
emails.push(u.email);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const verifications =
|
|
58
|
+
emails.length > 0
|
|
59
|
+
? await prisma.verification.findMany({
|
|
60
|
+
where: { identifier: { in: emails } },
|
|
61
|
+
select: { identifier: true, expiresAt: true },
|
|
62
|
+
orderBy: { expiresAt: 'desc' },
|
|
63
|
+
})
|
|
64
|
+
: [];
|
|
65
|
+
|
|
66
|
+
const latestTokenByEmail = new Map<string, Date>();
|
|
67
|
+
for (const v of verifications) {
|
|
68
|
+
if (!latestTokenByEmail.has(v.identifier)) {
|
|
69
|
+
latestTokenByEmail.set(v.identifier, v.expiresAt);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const now = new Date();
|
|
74
|
+
const usersWithRole = users.map((user: any) => {
|
|
75
|
+
let invitationStatus: 'completed' | 'pending' | 'expired' | null = null;
|
|
76
|
+
if (user.emailVerified || user.accounts.length > 0) {
|
|
77
|
+
invitationStatus = 'completed';
|
|
78
|
+
} else {
|
|
79
|
+
const expiresAt = latestTokenByEmail.get(user.email);
|
|
80
|
+
if (!expiresAt) {
|
|
81
|
+
invitationStatus = 'expired';
|
|
82
|
+
} else {
|
|
83
|
+
invitationStatus = expiresAt > now ? 'pending' : 'expired';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
id: user.id,
|
|
89
|
+
name: user.name,
|
|
90
|
+
email: user.email,
|
|
91
|
+
role: user.role || 'USER',
|
|
92
|
+
customRoleId: user.customRoleId,
|
|
93
|
+
customRole: user.customRole,
|
|
94
|
+
emailVerified: user.emailVerified,
|
|
95
|
+
active: user.active,
|
|
96
|
+
createdAt: user.createdAt,
|
|
97
|
+
updatedAt: user.updatedAt,
|
|
98
|
+
image: user.image,
|
|
99
|
+
invitationStatus,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
54
102
|
|
|
55
103
|
return NextResponse.json(usersWithRole);
|
|
56
104
|
} catch (error: any) {
|
|
@@ -76,17 +124,20 @@ export async function POST(request: NextRequest) {
|
|
|
76
124
|
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
77
125
|
}
|
|
78
126
|
|
|
79
|
-
const
|
|
80
|
-
const
|
|
127
|
+
const json = await request.json();
|
|
128
|
+
const parseResult = createUserSchema.safeParse(json);
|
|
81
129
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
130
|
+
if (!parseResult.success) {
|
|
131
|
+
return NextResponse.json(
|
|
132
|
+
{
|
|
133
|
+
error: 'Données invalides',
|
|
134
|
+
details: parseResult.error.flatten(),
|
|
135
|
+
},
|
|
136
|
+
{ status: 400 },
|
|
137
|
+
);
|
|
85
138
|
}
|
|
86
139
|
|
|
87
|
-
|
|
88
|
-
return NextResponse.json({ error: 'Le profil est requis' }, { status: 400 });
|
|
89
|
-
}
|
|
140
|
+
const { name, email, customRoleId } = parseResult.data;
|
|
90
141
|
|
|
91
142
|
// Vérifier si l'email existe déjà
|
|
92
143
|
const existingUser = await prisma.user.findUnique({
|
|
@@ -106,13 +157,11 @@ export async function POST(request: NextRequest) {
|
|
|
106
157
|
// Si l'utilisateur existe mais sans compte, on peut régénérer un token
|
|
107
158
|
}
|
|
108
159
|
|
|
109
|
-
// Résoudre le rôle enum à partir du profil attribué
|
|
110
160
|
const customRole = await prisma.customRole.findUnique({
|
|
111
161
|
where: { id: customRoleId },
|
|
112
162
|
select: { name: true },
|
|
113
163
|
});
|
|
114
|
-
const
|
|
115
|
-
const resolvedRole = resolveRoleFromCustomRoleName(customRoleName);
|
|
164
|
+
const resolvedRole = resolveRoleFromCustomRoleName(customRole?.name);
|
|
116
165
|
|
|
117
166
|
let user;
|
|
118
167
|
if (existingUser && existingUser.accounts.length === 0) {
|
|
@@ -126,12 +175,6 @@ export async function POST(request: NextRequest) {
|
|
|
126
175
|
},
|
|
127
176
|
});
|
|
128
177
|
} else {
|
|
129
|
-
const colors = [
|
|
130
|
-
'#EF4444', '#3B82F6', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899',
|
|
131
|
-
'#06B6D4', '#84CC16', '#F97316', '#6366F1', '#14B8A6', '#A855F7',
|
|
132
|
-
];
|
|
133
|
-
const randomColor = colors[Math.floor(Math.random() * colors.length)];
|
|
134
|
-
|
|
135
178
|
user = await prisma.user.create({
|
|
136
179
|
data: {
|
|
137
180
|
id: crypto.randomUUID(),
|
|
@@ -141,11 +184,12 @@ export async function POST(request: NextRequest) {
|
|
|
141
184
|
customRoleId,
|
|
142
185
|
emailVerified: false,
|
|
143
186
|
active: true,
|
|
144
|
-
eventColor: randomColor,
|
|
145
187
|
},
|
|
146
188
|
});
|
|
147
189
|
}
|
|
148
190
|
|
|
191
|
+
const customRoleName = customRole?.name || null;
|
|
192
|
+
|
|
149
193
|
// Log d'audit : création ou réactivation d'utilisateur
|
|
150
194
|
await logAudit({
|
|
151
195
|
actorId: session.user.id,
|
|
@@ -213,6 +257,9 @@ export async function POST(request: NextRequest) {
|
|
|
213
257
|
statusText: emailResponse.statusText,
|
|
214
258
|
error: errorData,
|
|
215
259
|
});
|
|
260
|
+
} else {
|
|
261
|
+
const successData = await emailResponse.json().catch(() => ({}));
|
|
262
|
+
console.log("✅ Email d'invitation envoyé avec succès:", successData);
|
|
216
263
|
}
|
|
217
264
|
} catch (emailError: any) {
|
|
218
265
|
console.error("❌ Erreur lors de l'envoi de l'email:", emailError);
|
|
@@ -248,6 +295,14 @@ export async function POST(request: NextRequest) {
|
|
|
248
295
|
return NextResponse.json({ error: 'Cet email est déjà utilisé' }, { status: 400 });
|
|
249
296
|
}
|
|
250
297
|
|
|
251
|
-
return NextResponse.json(
|
|
298
|
+
return NextResponse.json(
|
|
299
|
+
{
|
|
300
|
+
error:
|
|
301
|
+
process.env.NODE_ENV === 'development'
|
|
302
|
+
? error.message || 'Erreur serveur'
|
|
303
|
+
: 'Erreur serveur',
|
|
304
|
+
},
|
|
305
|
+
{ status: 500 },
|
|
306
|
+
);
|
|
252
307
|
}
|
|
253
308
|
}
|