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,14 +1,15 @@
|
|
|
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
|
import {
|
|
5
6
|
getValidAccessToken,
|
|
6
7
|
createGoogleCalendarEvent,
|
|
7
8
|
extractMeetLink,
|
|
9
|
+
assertWritableGoogleCalendar,
|
|
8
10
|
} from '@/lib/google-calendar';
|
|
9
|
-
import { createInteraction } from '@/lib/contact-interactions';
|
|
10
11
|
import nodemailer from 'nodemailer';
|
|
11
|
-
import { decrypt } from '@/lib/encryption';
|
|
12
|
+
import { decrypt, encrypt } from '@/lib/encryption';
|
|
12
13
|
import { render } from '@react-email/render';
|
|
13
14
|
import { MeetConfirmationEmailTemplate } from '@/components/meet-confirmation-email-template';
|
|
14
15
|
import React from 'react';
|
|
@@ -37,6 +38,16 @@ export async function POST(request: NextRequest) {
|
|
|
37
38
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
const [canCreate, canEditOwn, canEditAll] = await Promise.all([
|
|
42
|
+
checkPermission('tasks.create'),
|
|
43
|
+
checkPermission('tasks.edit_own'),
|
|
44
|
+
checkPermission('tasks.edit_all'),
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
if (!canCreate && !canEditOwn && !canEditAll) {
|
|
48
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
const body = await request.json();
|
|
41
52
|
const {
|
|
42
53
|
title,
|
|
@@ -48,6 +59,7 @@ export async function POST(request: NextRequest) {
|
|
|
48
59
|
internalNote,
|
|
49
60
|
contactId,
|
|
50
61
|
addToGoogleCalendar = true,
|
|
62
|
+
googleCalendarId: bodyGoogleCalendarId,
|
|
51
63
|
} = body;
|
|
52
64
|
|
|
53
65
|
// Validation
|
|
@@ -58,13 +70,17 @@ export async function POST(request: NextRequest) {
|
|
|
58
70
|
// Vérifier que l'utilisateur a un compte Google connecté seulement si on veut ajouter à Google Calendar
|
|
59
71
|
let googleAccount = null;
|
|
60
72
|
if (addToGoogleCalendar) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!googleAccount) {
|
|
73
|
+
try {
|
|
74
|
+
const { getUserGoogleAccount } = await import('@/lib/google-calendar');
|
|
75
|
+
googleAccount = await getUserGoogleAccount(session.user.id);
|
|
76
|
+
} catch (error: any) {
|
|
66
77
|
return NextResponse.json(
|
|
67
|
-
{
|
|
78
|
+
{
|
|
79
|
+
error:
|
|
80
|
+
error.message ||
|
|
81
|
+
'Veuillez connecter votre compte Google dans les paramètres pour créer une visioconférence.',
|
|
82
|
+
configLink: '/settings?section=integrations',
|
|
83
|
+
},
|
|
68
84
|
{ status: 400 },
|
|
69
85
|
);
|
|
70
86
|
}
|
|
@@ -81,6 +97,7 @@ export async function POST(request: NextRequest) {
|
|
|
81
97
|
|
|
82
98
|
let googleEventId: string | null = null;
|
|
83
99
|
let meetLink: string | null = null;
|
|
100
|
+
let storedGoogleCalendarId: string | null = null;
|
|
84
101
|
|
|
85
102
|
// Créer l'évènement Google Calendar avec Meet seulement si demandé
|
|
86
103
|
if (addToGoogleCalendar && googleAccount) {
|
|
@@ -99,14 +116,21 @@ export async function POST(request: NextRequest) {
|
|
|
99
116
|
await prisma.userGoogleAccount.update({
|
|
100
117
|
where: { userId: session.user.id },
|
|
101
118
|
data: {
|
|
102
|
-
accessToken,
|
|
119
|
+
accessToken: encrypt(accessToken),
|
|
103
120
|
tokenExpiresAt,
|
|
104
121
|
},
|
|
105
122
|
});
|
|
106
123
|
}
|
|
107
124
|
|
|
125
|
+
const targetCalendarId =
|
|
126
|
+
typeof bodyGoogleCalendarId === 'string' && bodyGoogleCalendarId.trim() !== ''
|
|
127
|
+
? bodyGoogleCalendarId.trim()
|
|
128
|
+
: googleAccount.defaultGoogleCalendarId?.trim() || 'primary';
|
|
129
|
+
|
|
130
|
+
await assertWritableGoogleCalendar(accessToken, targetCalendarId);
|
|
131
|
+
|
|
108
132
|
// Créer l'évènement Google Calendar avec Meet
|
|
109
|
-
const googleEvent = await createGoogleCalendarEvent(accessToken, {
|
|
133
|
+
const googleEvent = await createGoogleCalendarEvent(accessToken, targetCalendarId, {
|
|
110
134
|
summary: title,
|
|
111
135
|
description: description || '',
|
|
112
136
|
start: {
|
|
@@ -131,6 +155,7 @@ export async function POST(request: NextRequest) {
|
|
|
131
155
|
|
|
132
156
|
googleEventId = googleEvent.id;
|
|
133
157
|
meetLink = extractMeetLink(googleEvent);
|
|
158
|
+
storedGoogleCalendarId = targetCalendarId === 'primary' ? null : targetCalendarId;
|
|
134
159
|
} catch (googleError: any) {
|
|
135
160
|
console.error("Erreur lors de la création de l'évènement Google Calendar:", googleError);
|
|
136
161
|
// On continue quand même la création de la tâche
|
|
@@ -149,6 +174,7 @@ export async function POST(request: NextRequest) {
|
|
|
149
174
|
createdById: session.user.id,
|
|
150
175
|
contactId: contactId || null,
|
|
151
176
|
googleEventId: googleEventId,
|
|
177
|
+
googleCalendarId: storedGoogleCalendarId,
|
|
152
178
|
googleMeetLink: meetLink,
|
|
153
179
|
durationMinutes,
|
|
154
180
|
internalNote: internalNote || null,
|
|
@@ -249,22 +275,7 @@ export async function POST(request: NextRequest) {
|
|
|
249
275
|
}
|
|
250
276
|
}
|
|
251
277
|
|
|
252
|
-
//
|
|
253
|
-
if (contactId) {
|
|
254
|
-
try {
|
|
255
|
-
await createInteraction({
|
|
256
|
-
contactId,
|
|
257
|
-
type: 'APPOINTMENT_CREATED' as any,
|
|
258
|
-
title: title || null,
|
|
259
|
-
content: description || '',
|
|
260
|
-
userId: session.user.id,
|
|
261
|
-
date: startDate,
|
|
262
|
-
});
|
|
263
|
-
} catch (error) {
|
|
264
|
-
// Ne pas faire échouer la création du Meet si l'interaction échoue
|
|
265
|
-
console.error("Erreur lors de la création de l'interaction:", error);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
278
|
+
// Pas d'interaction pour un Google Meet : affichage via la tâche tant qu'elle existe en base.
|
|
268
279
|
|
|
269
280
|
return NextResponse.json(task, { status: 201 });
|
|
270
281
|
} catch (error: any) {
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { auth } from '@/lib/auth';
|
|
3
2
|
import { prisma } from '@/lib/prisma';
|
|
3
|
+
import { getAuthUser } from '@/lib/get-auth-user';
|
|
4
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
5
|
import { logAppointmentCreated, createInteraction } from '@/lib/contact-interactions';
|
|
5
6
|
import nodemailer from 'nodemailer';
|
|
6
|
-
import { decrypt } from '@/lib/encryption';
|
|
7
|
-
import { render } from '@react-email/render';
|
|
7
|
+
import { decrypt, encrypt } from '@/lib/encryption';
|
|
8
8
|
import { MeetConfirmationEmailTemplate } from '@/components/meet-confirmation-email-template';
|
|
9
9
|
import React from 'react';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
appendGoogleCalendarContactFooter,
|
|
12
|
+
assertWritableGoogleCalendar,
|
|
13
|
+
createGoogleCalendarEvent,
|
|
14
|
+
getValidAccessToken,
|
|
15
|
+
} from '@/lib/google-calendar';
|
|
11
16
|
|
|
12
17
|
function htmlToText(html: string): string {
|
|
13
18
|
if (!html) return '';
|
|
@@ -22,62 +27,81 @@ function htmlToText(html: string): string {
|
|
|
22
27
|
// GET /api/tasks - Récupérer les tâches de l'utilisateur
|
|
23
28
|
export async function GET(request: NextRequest) {
|
|
24
29
|
try {
|
|
25
|
-
const
|
|
26
|
-
headers: request.headers,
|
|
27
|
-
});
|
|
30
|
+
const authUser = await getAuthUser();
|
|
28
31
|
|
|
29
|
-
if (!
|
|
32
|
+
if (!authUser) {
|
|
30
33
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
const { session, permissions } = authUser;
|
|
37
|
+
const canViewAll = permissions.includes('tasks.view_all');
|
|
38
|
+
const canViewOwn = permissions.includes('tasks.view_own');
|
|
39
|
+
|
|
40
|
+
if (!canViewAll && !canViewOwn) {
|
|
41
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
const { searchParams } = new URL(request.url);
|
|
34
45
|
const startDate = searchParams.get('startDate');
|
|
35
46
|
const endDate = searchParams.get('endDate');
|
|
36
|
-
const assignedTo = searchParams.get('assignedTo');
|
|
37
|
-
const contactId = searchParams.get('contactId');
|
|
47
|
+
const assignedTo = searchParams.get('assignedTo');
|
|
48
|
+
const contactId = searchParams.get('contactId');
|
|
38
49
|
const showOtherUsers = searchParams.get('showOtherUsers') === 'true';
|
|
39
50
|
|
|
40
|
-
// Construire les filtres
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
// Construire les filtres de base
|
|
52
|
+
// Pour les événements multi-jours, on récupère les tâches avec une marge
|
|
53
|
+
// Les tâches qui commencent jusqu'à 30 jours avant le début de la période
|
|
54
|
+
// peuvent potentiellement être affichées (événements longs)
|
|
55
|
+
const baseFilters: any = {};
|
|
56
|
+
|
|
57
|
+
if (startDate) {
|
|
58
|
+
const startDateObj = new Date(startDate);
|
|
59
|
+
// Marge de 30 jours avant pour capturer les événements longs
|
|
60
|
+
const marginStart = new Date(startDateObj);
|
|
61
|
+
marginStart.setDate(marginStart.getDate() - 30);
|
|
62
|
+
|
|
63
|
+
baseFilters.scheduledAt = {
|
|
64
|
+
gte: marginStart,
|
|
44
65
|
lte: endDate ? new Date(endDate) : undefined,
|
|
45
|
-
}
|
|
46
|
-
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (!endDate) {
|
|
69
|
+
delete baseFilters.scheduledAt.lte;
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
baseFilters.scheduledAt = {
|
|
73
|
+
gte: new Date(),
|
|
74
|
+
lte: endDate ? new Date(endDate) : undefined,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (!endDate) {
|
|
78
|
+
delete baseFilters.scheduledAt.lte;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
47
81
|
|
|
48
82
|
// Filtrer par contact si fourni
|
|
49
83
|
if (contactId) {
|
|
50
|
-
|
|
84
|
+
baseFilters.contactId = contactId;
|
|
51
85
|
}
|
|
52
86
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// Pas de permission, voir uniquement ses propres tâches
|
|
62
|
-
where.assignedUserId = session.user.id;
|
|
63
|
-
}
|
|
64
|
-
} else if (assignedTo && assignedTo !== session.user.id) {
|
|
65
|
-
const { checkPermission } = await import('@/lib/check-permission');
|
|
66
|
-
const canViewOthers = await checkPermission('tasks.view_other_users_events');
|
|
67
|
-
if (canViewOthers) {
|
|
68
|
-
where.assignedUserId = assignedTo;
|
|
69
|
-
} else {
|
|
70
|
-
// Pas de permission, voir uniquement ses propres tâches
|
|
71
|
-
where.assignedUserId = session.user.id;
|
|
87
|
+
// Construire les filtres d'utilisateur basés sur les permissions
|
|
88
|
+
let userFilters: any = {};
|
|
89
|
+
|
|
90
|
+
if (showOtherUsers && permissions.includes('tasks.view_other_users_events')) {
|
|
91
|
+
userFilters = {};
|
|
92
|
+
} else if (canViewAll) {
|
|
93
|
+
if (assignedTo) {
|
|
94
|
+
userFilters = { assignedUserId: assignedTo };
|
|
72
95
|
}
|
|
73
96
|
} else {
|
|
74
|
-
|
|
75
|
-
where.assignedUserId = session.user.id;
|
|
97
|
+
userFilters = { assignedUserId: session.user.id };
|
|
76
98
|
}
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
// Combiner tous les filtres
|
|
101
|
+
const where: any = {
|
|
102
|
+
...baseFilters,
|
|
103
|
+
...userFilters,
|
|
104
|
+
};
|
|
81
105
|
|
|
82
106
|
const tasks = await prisma.task.findMany({
|
|
83
107
|
where,
|
|
@@ -96,7 +120,6 @@ export async function GET(request: NextRequest) {
|
|
|
96
120
|
id: true,
|
|
97
121
|
name: true,
|
|
98
122
|
email: true,
|
|
99
|
-
eventColor: true,
|
|
100
123
|
},
|
|
101
124
|
},
|
|
102
125
|
createdBy: {
|
|
@@ -115,22 +138,32 @@ export async function GET(request: NextRequest) {
|
|
|
115
138
|
return NextResponse.json(tasks);
|
|
116
139
|
} catch (error: any) {
|
|
117
140
|
console.error('Erreur lors de la récupération des tâches:', error);
|
|
118
|
-
return NextResponse.json(
|
|
141
|
+
return NextResponse.json(
|
|
142
|
+
{
|
|
143
|
+
error:
|
|
144
|
+
process.env.NODE_ENV === 'development'
|
|
145
|
+
? error.message || 'Erreur serveur'
|
|
146
|
+
: 'Erreur serveur',
|
|
147
|
+
},
|
|
148
|
+
{ status: 500 },
|
|
149
|
+
);
|
|
119
150
|
}
|
|
120
151
|
}
|
|
121
152
|
|
|
122
153
|
// POST /api/tasks - Créer une nouvelle tâche
|
|
123
154
|
export async function POST(request: NextRequest) {
|
|
124
155
|
try {
|
|
125
|
-
const
|
|
126
|
-
headers: request.headers,
|
|
127
|
-
});
|
|
156
|
+
const [authUser, body] = await Promise.all([getAuthUser(), request.json()]);
|
|
128
157
|
|
|
129
|
-
if (!
|
|
158
|
+
if (!authUser) {
|
|
130
159
|
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
131
160
|
}
|
|
132
161
|
|
|
133
|
-
const
|
|
162
|
+
const { session } = authUser;
|
|
163
|
+
|
|
164
|
+
if (!authUser.permissions.includes('tasks.create')) {
|
|
165
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
166
|
+
}
|
|
134
167
|
const {
|
|
135
168
|
type,
|
|
136
169
|
title,
|
|
@@ -143,6 +176,13 @@ export async function POST(request: NextRequest) {
|
|
|
143
176
|
internalNote,
|
|
144
177
|
attendees = [],
|
|
145
178
|
addToGoogleCalendar = true,
|
|
179
|
+
googleCalendarId: bodyGoogleCalendarId,
|
|
180
|
+
// Champs pour les rendez-vous physiques
|
|
181
|
+
location,
|
|
182
|
+
locationAddress,
|
|
183
|
+
locationCity,
|
|
184
|
+
locationPostalCode,
|
|
185
|
+
isAtHome = false,
|
|
146
186
|
} = body;
|
|
147
187
|
|
|
148
188
|
// Validation
|
|
@@ -153,20 +193,30 @@ export async function POST(request: NextRequest) {
|
|
|
153
193
|
);
|
|
154
194
|
}
|
|
155
195
|
|
|
156
|
-
// Déterminer l'utilisateur assigné
|
|
196
|
+
// Déterminer l'utilisateur assigné (selon permissions d'assignation)
|
|
197
|
+
const canAssignFull = await checkPermission('tasks.assign');
|
|
198
|
+
const canAssignToSales = await checkPermission('tasks.assign_to_sales');
|
|
199
|
+
|
|
157
200
|
let finalAssignedUserId: string;
|
|
158
|
-
if (assignedUserId &&
|
|
159
|
-
|
|
160
|
-
const canAssignOthers = await checkPerm('tasks.view_other_users_events');
|
|
161
|
-
if (canAssignOthers) {
|
|
162
|
-
// Utilisateur avec permission peut assigner à n'importe qui
|
|
201
|
+
if (assignedUserId && (canAssignFull || canAssignToSales)) {
|
|
202
|
+
if (canAssignFull) {
|
|
163
203
|
finalAssignedUserId = assignedUserId;
|
|
164
204
|
} else {
|
|
165
|
-
//
|
|
166
|
-
|
|
205
|
+
// assign_to_sales : uniquement COMMERCIAL ou TELEPRO
|
|
206
|
+
const target = await prisma.user.findUnique({
|
|
207
|
+
where: { id: assignedUserId },
|
|
208
|
+
select: { role: true },
|
|
209
|
+
});
|
|
210
|
+
if (!target || (target.role !== 'COMMERCIAL' && target.role !== 'TELEPRO')) {
|
|
211
|
+
return NextResponse.json(
|
|
212
|
+
{ error: "Vous ne pouvez assigner une tâche qu'à un commercial ou un télépro" },
|
|
213
|
+
{ status: 403 },
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
finalAssignedUserId = assignedUserId;
|
|
167
217
|
}
|
|
168
218
|
} else {
|
|
169
|
-
finalAssignedUserId =
|
|
219
|
+
finalAssignedUserId = session.user.id;
|
|
170
220
|
}
|
|
171
221
|
|
|
172
222
|
// Vérifier que le contact existe si fourni
|
|
@@ -193,6 +243,12 @@ export async function POST(request: NextRequest) {
|
|
|
193
243
|
typeof reminderMinutesBefore === 'number' ? reminderMinutesBefore : null,
|
|
194
244
|
notifyContact: notifyContact === true,
|
|
195
245
|
internalNote: internalNote || null,
|
|
246
|
+
// Champs d'adresse pour les rendez-vous physiques
|
|
247
|
+
location: location || null,
|
|
248
|
+
locationAddress: locationAddress || null,
|
|
249
|
+
locationCity: locationCity || null,
|
|
250
|
+
locationPostalCode: locationPostalCode || null,
|
|
251
|
+
isAtHome: isAtHome === true,
|
|
196
252
|
},
|
|
197
253
|
include: {
|
|
198
254
|
contact: {
|
|
@@ -209,7 +265,6 @@ export async function POST(request: NextRequest) {
|
|
|
209
265
|
id: true,
|
|
210
266
|
name: true,
|
|
211
267
|
email: true,
|
|
212
|
-
eventColor: true,
|
|
213
268
|
},
|
|
214
269
|
},
|
|
215
270
|
createdBy: {
|
|
@@ -241,44 +296,79 @@ export async function POST(request: NextRequest) {
|
|
|
241
296
|
// Si c'est un rendez-vous (physique) ou une tâche et que l'utilisateur veut l'ajouter à Google Calendar
|
|
242
297
|
if ((type === 'MEETING' || type === 'TASK') && addToGoogleCalendar) {
|
|
243
298
|
try {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
description: htmlToText(description),
|
|
263
|
-
start: {
|
|
264
|
-
dateTime: startDate.toISOString(),
|
|
265
|
-
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
266
|
-
},
|
|
267
|
-
end: {
|
|
268
|
-
dateTime: endDate.toISOString(),
|
|
269
|
-
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
299
|
+
const { getUserGoogleAccount } = await import('@/lib/google-calendar');
|
|
300
|
+
const googleAccount = await getUserGoogleAccount(session.user.id);
|
|
301
|
+
|
|
302
|
+
const accessToken = await getValidAccessToken(
|
|
303
|
+
googleAccount.accessToken,
|
|
304
|
+
googleAccount.refreshToken,
|
|
305
|
+
googleAccount.tokenExpiresAt,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Mettre à jour le token si nécessaire
|
|
309
|
+
if (accessToken !== googleAccount.accessToken) {
|
|
310
|
+
const tokenExpiresAt = new Date();
|
|
311
|
+
tokenExpiresAt.setSeconds(tokenExpiresAt.getSeconds() + 3600);
|
|
312
|
+
await prisma.userGoogleAccount.update({
|
|
313
|
+
where: { userId: session.user.id },
|
|
314
|
+
data: {
|
|
315
|
+
accessToken: encrypt(accessToken),
|
|
316
|
+
tokenExpiresAt,
|
|
270
317
|
},
|
|
271
|
-
attendees: allAttendees.length > 0 ? allAttendees : undefined,
|
|
272
318
|
});
|
|
319
|
+
}
|
|
273
320
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
321
|
+
// Durée par défaut de 60 minutes pour les rendez-vous physiques, 30 minutes pour les tâches
|
|
322
|
+
const startDate = new Date(scheduledAt);
|
|
323
|
+
const duration = type === 'MEETING' ? 60 : 30;
|
|
324
|
+
const endDate = new Date(startDate.getTime() + duration * 60 * 1000);
|
|
325
|
+
|
|
326
|
+
const targetCalendarId =
|
|
327
|
+
typeof bodyGoogleCalendarId === 'string' && bodyGoogleCalendarId.trim() !== ''
|
|
328
|
+
? bodyGoogleCalendarId.trim()
|
|
329
|
+
: googleAccount.defaultGoogleCalendarId?.trim() || 'primary';
|
|
330
|
+
|
|
331
|
+
await assertWritableGoogleCalendar(accessToken, targetCalendarId);
|
|
332
|
+
|
|
333
|
+
const googleAttendees =
|
|
334
|
+
type === 'TASK' && task.contact?.email
|
|
335
|
+
? allAttendees.filter(
|
|
336
|
+
(a) => a.email.toLowerCase() !== task.contact!.email!.toLowerCase(),
|
|
337
|
+
)
|
|
338
|
+
: allAttendees;
|
|
339
|
+
|
|
340
|
+
let googleDescription = htmlToText(description);
|
|
341
|
+
if (type === 'TASK' && task.contact?.email) {
|
|
342
|
+
googleDescription = appendGoogleCalendarContactFooter(googleDescription, {
|
|
343
|
+
firstName: task.contact.firstName,
|
|
344
|
+
lastName: task.contact.lastName,
|
|
345
|
+
email: task.contact.email,
|
|
280
346
|
});
|
|
281
347
|
}
|
|
348
|
+
|
|
349
|
+
const googleEvent = await createGoogleCalendarEvent(accessToken, targetCalendarId, {
|
|
350
|
+
summary: title || (type === 'MEETING' ? 'Rendez-vous' : 'Tâche'),
|
|
351
|
+
description: googleDescription,
|
|
352
|
+
start: {
|
|
353
|
+
dateTime: startDate.toISOString(),
|
|
354
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
355
|
+
},
|
|
356
|
+
end: {
|
|
357
|
+
dateTime: endDate.toISOString(),
|
|
358
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
359
|
+
},
|
|
360
|
+
attendees: googleAttendees.length > 0 ? googleAttendees : undefined,
|
|
361
|
+
location: location || undefined, // Ajouter l'adresse du rendez-vous
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Sauvegarder l'ID de l'évènement pour synchroniser les modifications/suppressions
|
|
365
|
+
await prisma.task.update({
|
|
366
|
+
where: { id: task.id },
|
|
367
|
+
data: {
|
|
368
|
+
googleEventId: googleEvent.id,
|
|
369
|
+
googleCalendarId: targetCalendarId === 'primary' ? null : targetCalendarId,
|
|
370
|
+
},
|
|
371
|
+
});
|
|
282
372
|
} catch (googleError: any) {
|
|
283
373
|
console.error('Erreur lors de la création de lévènement Google Calendar:', googleError);
|
|
284
374
|
// On ne bloque pas la création de la tâche si Google Calendar échoue
|
|
@@ -299,7 +389,8 @@ export async function POST(request: NextRequest) {
|
|
|
299
389
|
);
|
|
300
390
|
|
|
301
391
|
// Envoyer un email de notification si demandé (contact ou invités)
|
|
302
|
-
|
|
392
|
+
// Vérifier explicitement que notifyContact est true
|
|
393
|
+
if (notifyContact === true) {
|
|
303
394
|
try {
|
|
304
395
|
// Récupérer la configuration SMTP
|
|
305
396
|
const smtpConfig = await prisma.smtpConfig.findUnique({
|
|
@@ -312,6 +403,7 @@ export async function POST(request: NextRequest) {
|
|
|
312
403
|
try {
|
|
313
404
|
password = decrypt(smtpConfig.password);
|
|
314
405
|
} catch (error) {
|
|
406
|
+
console.error(error);
|
|
315
407
|
password = smtpConfig.password;
|
|
316
408
|
}
|
|
317
409
|
|
|
@@ -358,6 +450,7 @@ export async function POST(request: NextRequest) {
|
|
|
358
450
|
// Envoyer un email individuel à chaque destinataire
|
|
359
451
|
for (const recipient of recipients) {
|
|
360
452
|
try {
|
|
453
|
+
const { render } = await import('@react-email/render');
|
|
361
454
|
// Générer le contenu HTML de l'email avec le composant React
|
|
362
455
|
const emailComponent = React.createElement(MeetConfirmationEmailTemplate, {
|
|
363
456
|
contactName: recipient.name,
|
|
@@ -378,7 +471,7 @@ export async function POST(request: NextRequest) {
|
|
|
378
471
|
? `"${smtpConfig.fromName}" <${smtpConfig.fromEmail}>`
|
|
379
472
|
: smtpConfig.fromEmail,
|
|
380
473
|
to: recipient.email,
|
|
381
|
-
subject: `Confirmation de rendez-vous${title ?
|
|
474
|
+
subject: `Confirmation de rendez-vous ${title ? `: ${title}` : ''}`,
|
|
382
475
|
text: emailText,
|
|
383
476
|
html: emailHtml,
|
|
384
477
|
});
|
|
@@ -396,8 +489,8 @@ export async function POST(request: NextRequest) {
|
|
|
396
489
|
console.error("Erreur lors de l'envoi de l'email de notification:", emailError);
|
|
397
490
|
}
|
|
398
491
|
}
|
|
399
|
-
} else {
|
|
400
|
-
//
|
|
492
|
+
} else if (type !== 'VIDEO_CONFERENCE') {
|
|
493
|
+
// Pas d'interaction pour la visio (annulable) : le fil d'activité lit la tâche en base.
|
|
401
494
|
await createInteraction({
|
|
402
495
|
contactId,
|
|
403
496
|
type: 'TASK' as any,
|
|
@@ -405,6 +498,10 @@ export async function POST(request: NextRequest) {
|
|
|
405
498
|
content: description,
|
|
406
499
|
userId: session.user.id,
|
|
407
500
|
date: new Date(scheduledAt),
|
|
501
|
+
metadata: {
|
|
502
|
+
taskId: task.id,
|
|
503
|
+
scheduledAt: new Date(scheduledAt).toISOString(),
|
|
504
|
+
},
|
|
408
505
|
});
|
|
409
506
|
}
|
|
410
507
|
} catch (error) {
|
|
@@ -416,6 +513,14 @@ export async function POST(request: NextRequest) {
|
|
|
416
513
|
return NextResponse.json(task, { status: 201 });
|
|
417
514
|
} catch (error: any) {
|
|
418
515
|
console.error('Erreur lors de la création de la tâche:', error);
|
|
419
|
-
return NextResponse.json(
|
|
516
|
+
return NextResponse.json(
|
|
517
|
+
{
|
|
518
|
+
error:
|
|
519
|
+
process.env.NODE_ENV === 'development'
|
|
520
|
+
? error.message || 'Erreur serveur'
|
|
521
|
+
: 'Erreur serveur',
|
|
522
|
+
},
|
|
523
|
+
{ status: 500 },
|
|
524
|
+
);
|
|
420
525
|
}
|
|
421
526
|
}
|