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
package/template/src/proxy.ts
CHANGED
|
@@ -1,91 +1,53 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import type { NextRequest } from 'next/server';
|
|
3
|
-
import { auth } from './lib/auth';
|
|
4
|
-
import { Role } from './lib/roles';
|
|
5
|
-
import { prisma } from './lib/prisma';
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
const protectedRoutes = [
|
|
9
|
-
'/dashboard',
|
|
10
|
-
'/contacts',
|
|
11
|
-
'/settings',
|
|
12
|
-
'/users',
|
|
13
|
-
'/agenda',
|
|
14
|
-
'/automatisation',
|
|
15
|
-
'/templates',
|
|
16
|
-
'/closing',
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
// Routes réservées aux admins
|
|
20
|
-
const adminRoutes = ['/users'];
|
|
3
|
+
const SESSION_COOKIE = 'better-auth.session_token';
|
|
21
4
|
|
|
22
|
-
|
|
23
|
-
const authRoutes = ['/signin'];
|
|
24
|
-
|
|
25
|
-
export async function proxy(request: NextRequest) {
|
|
26
|
-
const { pathname } = request.nextUrl;
|
|
5
|
+
const PUBLIC_PAGES = new Set(['/', '/signin', '/invite', '/reset-password']);
|
|
27
6
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (session && session.user?.id) {
|
|
39
|
-
const dbUser = await prisma.user.findUnique({
|
|
40
|
-
where: { id: session.user.id },
|
|
41
|
-
select: { role: true, active: true },
|
|
42
|
-
});
|
|
7
|
+
const PUBLIC_API_PREFIXES = [
|
|
8
|
+
'/api/auth',
|
|
9
|
+
'/api/webhooks',
|
|
10
|
+
'/api/cron',
|
|
11
|
+
'/api/jobs',
|
|
12
|
+
'/api/invite',
|
|
13
|
+
'/api/reset-password',
|
|
14
|
+
'/api/email/track',
|
|
15
|
+
'/api/workflows/process',
|
|
16
|
+
];
|
|
43
17
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
18
|
+
function isPublicPage(pathname: string): boolean {
|
|
19
|
+
if (PUBLIC_PAGES.has(pathname)) return true;
|
|
20
|
+
for (const path of PUBLIC_PAGES) {
|
|
21
|
+
if (path !== '/' && pathname.startsWith(path + '/')) return true;
|
|
49
22
|
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
50
25
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Vérifier si la route est réservée aux admins
|
|
55
|
-
const isAdminRoute = adminRoutes.some((route) => pathname.startsWith(route));
|
|
26
|
+
function isPublicApi(pathname: string): boolean {
|
|
27
|
+
return PUBLIC_API_PREFIXES.some((prefix) => pathname.startsWith(prefix));
|
|
28
|
+
}
|
|
56
29
|
|
|
57
|
-
|
|
58
|
-
const
|
|
30
|
+
export function proxy(request: NextRequest) {
|
|
31
|
+
const { pathname } = request.nextUrl;
|
|
59
32
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const signInUrl = new URL('/signin', request.url);
|
|
63
|
-
signInUrl.searchParams.set('callbackUrl', pathname);
|
|
64
|
-
return NextResponse.redirect(signInUrl);
|
|
33
|
+
if (isPublicPage(pathname) || isPublicApi(pathname)) {
|
|
34
|
+
return NextResponse.next();
|
|
65
35
|
}
|
|
66
36
|
|
|
67
|
-
|
|
68
|
-
if (isAuthenticated && isAdminRoute && userRole !== Role.ADMIN) {
|
|
69
|
-
return NextResponse.redirect(new URL('/dashboard', request.url));
|
|
70
|
-
}
|
|
37
|
+
const hasSession = request.cookies.has(SESSION_COOKIE);
|
|
71
38
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
39
|
+
if (!hasSession) {
|
|
40
|
+
if (pathname.startsWith('/api/')) {
|
|
41
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
42
|
+
}
|
|
43
|
+
const signinUrl = new URL('/signin', request.url);
|
|
44
|
+
signinUrl.searchParams.set('callbackUrl', pathname);
|
|
45
|
+
return NextResponse.redirect(signinUrl);
|
|
75
46
|
}
|
|
76
47
|
|
|
77
48
|
return NextResponse.next();
|
|
78
49
|
}
|
|
79
50
|
|
|
80
51
|
export const config = {
|
|
81
|
-
matcher: [
|
|
82
|
-
/*
|
|
83
|
-
* Match all request paths except for the ones starting with:
|
|
84
|
-
* - api (API routes)
|
|
85
|
-
* - _next/static (static files)
|
|
86
|
-
* - _next/image (image optimization files)
|
|
87
|
-
* - favicon.ico (favicon file)
|
|
88
|
-
*/
|
|
89
|
-
'/((?!api|_next/static|_next/image|favicon.ico).*)',
|
|
90
|
-
],
|
|
52
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|.*..*).*)'],
|
|
91
53
|
};
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
export type FilterOperator =
|
|
2
|
+
| 'equals'
|
|
3
|
+
| 'not_equals'
|
|
4
|
+
| 'contains'
|
|
5
|
+
| 'not_contains'
|
|
6
|
+
| 'starts_with'
|
|
7
|
+
| 'ends_with'
|
|
8
|
+
| 'is_any_of'
|
|
9
|
+
| 'is_none_of'
|
|
10
|
+
| 'gt'
|
|
11
|
+
| 'gte'
|
|
12
|
+
| 'lt'
|
|
13
|
+
| 'lte'
|
|
14
|
+
| 'between'
|
|
15
|
+
| 'is_known'
|
|
16
|
+
| 'is_unknown'
|
|
17
|
+
| 'date_preset';
|
|
18
|
+
|
|
19
|
+
export type DatePreset =
|
|
20
|
+
| 'today'
|
|
21
|
+
| 'yesterday'
|
|
22
|
+
| 'tomorrow'
|
|
23
|
+
| 'this_week'
|
|
24
|
+
| 'this_week_so_far'
|
|
25
|
+
| 'last_week'
|
|
26
|
+
| 'next_week'
|
|
27
|
+
| 'this_month'
|
|
28
|
+
| 'this_month_so_far'
|
|
29
|
+
| 'last_month'
|
|
30
|
+
| 'next_month'
|
|
31
|
+
| 'this_quarter'
|
|
32
|
+
| 'this_quarter_so_far'
|
|
33
|
+
| 'last_quarter'
|
|
34
|
+
| 'this_year'
|
|
35
|
+
| 'this_year_so_far'
|
|
36
|
+
| 'last_year'
|
|
37
|
+
| 'last_7_days'
|
|
38
|
+
| 'last_14_days'
|
|
39
|
+
| 'last_30_days'
|
|
40
|
+
| 'last_60_days'
|
|
41
|
+
| 'last_90_days'
|
|
42
|
+
| 'last_180_days'
|
|
43
|
+
| 'last_365_days';
|
|
44
|
+
|
|
45
|
+
export interface ViewFilter {
|
|
46
|
+
field: string;
|
|
47
|
+
operator: FilterOperator;
|
|
48
|
+
value: string | string[] | null;
|
|
49
|
+
preset?: DatePreset;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ViewColumn {
|
|
53
|
+
id: string;
|
|
54
|
+
visible: boolean;
|
|
55
|
+
order: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ViewSortConfig {
|
|
59
|
+
field: string;
|
|
60
|
+
direction: 'asc' | 'desc';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type FilterFieldType = 'text' | 'select' | 'date' | 'boolean' | 'select_or_text';
|
|
64
|
+
|
|
65
|
+
export interface FilterFieldDefinition {
|
|
66
|
+
field: string;
|
|
67
|
+
label: string;
|
|
68
|
+
type: FilterFieldType;
|
|
69
|
+
group: string;
|
|
70
|
+
operators: FilterOperator[];
|
|
71
|
+
optionsEndpoint?: string;
|
|
72
|
+
staticOptions?: { value: string; label: string }[];
|
|
73
|
+
clientSideOnly?: boolean;
|
|
74
|
+
entityTypes?: ('contacts' | 'companies')[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const DATE_PRESET_LABELS: Record<DatePreset, string> = {
|
|
78
|
+
today: "Aujourd'hui",
|
|
79
|
+
yesterday: 'Hier',
|
|
80
|
+
tomorrow: 'Demain',
|
|
81
|
+
this_week: 'Cette semaine',
|
|
82
|
+
this_week_so_far: "Cette semaine jusqu'à aujourd'hui",
|
|
83
|
+
last_week: 'La semaine dernière',
|
|
84
|
+
next_week: 'La semaine prochaine',
|
|
85
|
+
this_month: 'Ce mois-ci',
|
|
86
|
+
this_month_so_far: "Ce mois jusqu'à aujourd'hui",
|
|
87
|
+
last_month: 'Le mois dernier',
|
|
88
|
+
next_month: 'Le mois prochain',
|
|
89
|
+
this_quarter: 'Ce trimestre',
|
|
90
|
+
this_quarter_so_far: "Ce trimestre jusqu'à aujourd'hui",
|
|
91
|
+
last_quarter: 'Le trimestre dernier',
|
|
92
|
+
this_year: 'Cette année',
|
|
93
|
+
this_year_so_far: "Cette année jusqu'à aujourd'hui",
|
|
94
|
+
last_year: "L'année dernière",
|
|
95
|
+
last_7_days: 'Les 7 derniers jours',
|
|
96
|
+
last_14_days: 'Les 14 derniers jours',
|
|
97
|
+
last_30_days: 'Les 30 derniers jours',
|
|
98
|
+
last_60_days: 'Les 60 derniers jours',
|
|
99
|
+
last_90_days: 'Les 90 derniers jours',
|
|
100
|
+
last_180_days: 'Les 180 derniers jours',
|
|
101
|
+
last_365_days: 'Les 365 derniers jours',
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const OPERATOR_LABELS: Record<FilterOperator, string> = {
|
|
105
|
+
equals: 'est égal à',
|
|
106
|
+
not_equals: "n'est pas égal à",
|
|
107
|
+
contains: 'contient',
|
|
108
|
+
not_contains: 'ne contient pas',
|
|
109
|
+
starts_with: 'commence par',
|
|
110
|
+
ends_with: 'se termine par',
|
|
111
|
+
is_any_of: 'est parmi',
|
|
112
|
+
is_none_of: "n'est pas parmi",
|
|
113
|
+
gt: 'est supérieur à',
|
|
114
|
+
gte: 'est supérieur ou égal à',
|
|
115
|
+
lt: 'est inférieur à',
|
|
116
|
+
lte: 'est inférieur ou égal à',
|
|
117
|
+
between: 'est entre',
|
|
118
|
+
is_known: 'est connu',
|
|
119
|
+
is_unknown: 'est inconnu',
|
|
120
|
+
date_preset: 'est',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const FILTER_FIELD_DEFINITIONS: FilterFieldDefinition[] = [
|
|
124
|
+
{
|
|
125
|
+
field: 'firstName',
|
|
126
|
+
label: 'Prénom',
|
|
127
|
+
type: 'text',
|
|
128
|
+
group: 'Informations du contact',
|
|
129
|
+
operators: [
|
|
130
|
+
'equals',
|
|
131
|
+
'not_equals',
|
|
132
|
+
'contains',
|
|
133
|
+
'not_contains',
|
|
134
|
+
'starts_with',
|
|
135
|
+
'ends_with',
|
|
136
|
+
'is_known',
|
|
137
|
+
'is_unknown',
|
|
138
|
+
],
|
|
139
|
+
entityTypes: ['contacts'],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
field: 'lastName',
|
|
143
|
+
label: 'Nom',
|
|
144
|
+
type: 'text',
|
|
145
|
+
group: 'Informations du contact',
|
|
146
|
+
operators: [
|
|
147
|
+
'equals',
|
|
148
|
+
'not_equals',
|
|
149
|
+
'contains',
|
|
150
|
+
'not_contains',
|
|
151
|
+
'starts_with',
|
|
152
|
+
'ends_with',
|
|
153
|
+
'is_known',
|
|
154
|
+
'is_unknown',
|
|
155
|
+
],
|
|
156
|
+
entityTypes: ['contacts'],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
field: 'phone',
|
|
160
|
+
label: 'Téléphone',
|
|
161
|
+
type: 'text',
|
|
162
|
+
group: 'Informations du contact',
|
|
163
|
+
operators: ['equals', 'contains', 'starts_with', 'is_known', 'is_unknown'],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
field: 'email',
|
|
167
|
+
label: 'Email',
|
|
168
|
+
type: 'text',
|
|
169
|
+
group: 'Informations du contact',
|
|
170
|
+
operators: [
|
|
171
|
+
'equals',
|
|
172
|
+
'not_equals',
|
|
173
|
+
'contains',
|
|
174
|
+
'not_contains',
|
|
175
|
+
'starts_with',
|
|
176
|
+
'ends_with',
|
|
177
|
+
'is_known',
|
|
178
|
+
'is_unknown',
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
field: 'address',
|
|
183
|
+
label: 'Adresse',
|
|
184
|
+
type: 'text',
|
|
185
|
+
group: 'Informations du contact',
|
|
186
|
+
operators: ['contains', 'is_known', 'is_unknown'],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
field: 'city',
|
|
190
|
+
label: 'Ville',
|
|
191
|
+
type: 'text',
|
|
192
|
+
group: 'Informations du contact',
|
|
193
|
+
operators: ['equals', 'not_equals', 'contains', 'starts_with', 'is_known', 'is_unknown'],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
field: 'postalCode',
|
|
197
|
+
label: 'Code postal',
|
|
198
|
+
type: 'text',
|
|
199
|
+
group: 'Informations du contact',
|
|
200
|
+
operators: ['equals', 'starts_with', 'is_known', 'is_unknown'],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
field: 'civility',
|
|
204
|
+
label: 'Civilité',
|
|
205
|
+
type: 'select',
|
|
206
|
+
group: 'Informations du contact',
|
|
207
|
+
operators: ['is_any_of', 'is_none_of'],
|
|
208
|
+
staticOptions: [
|
|
209
|
+
{ value: 'M', label: 'M.' },
|
|
210
|
+
{ value: 'MME', label: 'Mme' },
|
|
211
|
+
{ value: 'MLLE', label: 'Mlle' },
|
|
212
|
+
],
|
|
213
|
+
entityTypes: ['contacts'],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
field: 'statusId',
|
|
217
|
+
label: 'Statut',
|
|
218
|
+
type: 'select',
|
|
219
|
+
group: 'Statut & Assignation',
|
|
220
|
+
operators: ['is_any_of', 'is_none_of', 'is_known', 'is_unknown'],
|
|
221
|
+
optionsEndpoint: '/api/statuses',
|
|
222
|
+
entityTypes: ['contacts'],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
field: 'origin',
|
|
226
|
+
label: 'Origine',
|
|
227
|
+
type: 'select_or_text',
|
|
228
|
+
group: 'Statut & Assignation',
|
|
229
|
+
operators: [
|
|
230
|
+
'equals',
|
|
231
|
+
'not_equals',
|
|
232
|
+
'is_any_of',
|
|
233
|
+
'is_none_of',
|
|
234
|
+
'contains',
|
|
235
|
+
'is_known',
|
|
236
|
+
'is_unknown',
|
|
237
|
+
],
|
|
238
|
+
entityTypes: ['contacts'],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
field: 'assignedCommercialId',
|
|
242
|
+
label: 'Commercial',
|
|
243
|
+
type: 'select',
|
|
244
|
+
group: 'Statut & Assignation',
|
|
245
|
+
operators: ['is_any_of', 'is_none_of', 'is_known', 'is_unknown'],
|
|
246
|
+
optionsEndpoint: '/api/users/list',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
field: 'assignedTeleproId',
|
|
250
|
+
label: 'Télépro',
|
|
251
|
+
type: 'select',
|
|
252
|
+
group: 'Statut & Assignation',
|
|
253
|
+
operators: ['is_any_of', 'is_none_of', 'is_known', 'is_unknown'],
|
|
254
|
+
optionsEndpoint: '/api/users/list',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
field: 'createdById',
|
|
258
|
+
label: 'Créé par',
|
|
259
|
+
type: 'select',
|
|
260
|
+
group: 'Statut & Assignation',
|
|
261
|
+
operators: ['is_any_of', 'is_none_of'],
|
|
262
|
+
optionsEndpoint: '/api/users/list',
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
field: 'createdAt',
|
|
266
|
+
label: 'Date de création',
|
|
267
|
+
type: 'date',
|
|
268
|
+
group: 'Dates',
|
|
269
|
+
operators: ['date_preset', 'equals', 'gt', 'gte', 'lt', 'lte', 'between'],
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
field: 'updatedAt',
|
|
273
|
+
label: 'Date de modification',
|
|
274
|
+
type: 'date',
|
|
275
|
+
group: 'Dates',
|
|
276
|
+
operators: ['date_preset', 'equals', 'gt', 'gte', 'lt', 'lte', 'between'],
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
field: 'region',
|
|
280
|
+
label: 'Région',
|
|
281
|
+
type: 'select',
|
|
282
|
+
group: 'Géographie',
|
|
283
|
+
operators: ['is_any_of', 'is_none_of'],
|
|
284
|
+
entityTypes: ['contacts'],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
field: 'department',
|
|
288
|
+
label: 'Département',
|
|
289
|
+
type: 'select',
|
|
290
|
+
group: 'Géographie',
|
|
291
|
+
operators: ['is_any_of', 'is_none_of'],
|
|
292
|
+
entityTypes: ['contacts'],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
field: 'name',
|
|
296
|
+
label: 'Nom',
|
|
297
|
+
type: 'text',
|
|
298
|
+
group: "Informations de l'entreprise",
|
|
299
|
+
operators: [
|
|
300
|
+
'equals',
|
|
301
|
+
'not_equals',
|
|
302
|
+
'contains',
|
|
303
|
+
'not_contains',
|
|
304
|
+
'starts_with',
|
|
305
|
+
'ends_with',
|
|
306
|
+
'is_known',
|
|
307
|
+
'is_unknown',
|
|
308
|
+
],
|
|
309
|
+
entityTypes: ['companies'],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
field: 'siret',
|
|
313
|
+
label: 'SIRET',
|
|
314
|
+
type: 'text',
|
|
315
|
+
group: "Informations de l'entreprise",
|
|
316
|
+
operators: ['equals', 'contains', 'starts_with', 'is_known', 'is_unknown'],
|
|
317
|
+
entityTypes: ['companies'],
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
field: 'industry',
|
|
321
|
+
label: 'Secteur',
|
|
322
|
+
type: 'text',
|
|
323
|
+
group: "Informations de l'entreprise",
|
|
324
|
+
operators: ['equals', 'not_equals', 'contains', 'is_known', 'is_unknown'],
|
|
325
|
+
entityTypes: ['companies'],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
field: 'website',
|
|
329
|
+
label: 'Site web',
|
|
330
|
+
type: 'text',
|
|
331
|
+
group: "Informations de l'entreprise",
|
|
332
|
+
operators: ['contains', 'is_known', 'is_unknown'],
|
|
333
|
+
entityTypes: ['companies'],
|
|
334
|
+
},
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
export interface ContactViewData {
|
|
338
|
+
id: string;
|
|
339
|
+
name: string;
|
|
340
|
+
userId: string;
|
|
341
|
+
user?: { id: string; name: string };
|
|
342
|
+
isPublic: boolean;
|
|
343
|
+
isDefault: boolean;
|
|
344
|
+
entityType: 'contacts' | 'companies';
|
|
345
|
+
filters: ViewFilter[];
|
|
346
|
+
columns: ViewColumn[] | null;
|
|
347
|
+
sortConfig: ViewSortConfig | null;
|
|
348
|
+
pinOrder: number | null;
|
|
349
|
+
createdAt: string;
|
|
350
|
+
updatedAt: string;
|
|
351
|
+
}
|
package/template/vercel.json
CHANGED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
# Configuration des Workflows avec Vercel Cron
|
|
2
|
-
|
|
3
|
-
Ce document explique comment fonctionne le système de planification des actions
|
|
4
|
-
de workflow avec Vercel Cron.
|
|
5
|
-
|
|
6
|
-
## Fonctionnement
|
|
7
|
-
|
|
8
|
-
Le système utilise une table `ScheduledWorkflowAction` dans la base de données
|
|
9
|
-
pour stocker les actions à exécuter plus tard. Un cron job Vercel appelle
|
|
10
|
-
l'endpoint `/api/workflows/process` toutes les minutes pour traiter les actions
|
|
11
|
-
dont la date d'exécution est arrivée.
|
|
12
|
-
|
|
13
|
-
## Configuration
|
|
14
|
-
|
|
15
|
-
### 1. Variables d'environnement
|
|
16
|
-
|
|
17
|
-
Ajoutez cette variable dans votre fichier `.env` et dans les variables
|
|
18
|
-
d'environnement Vercel :
|
|
19
|
-
|
|
20
|
-
```env
|
|
21
|
-
CRON_SECRET=votre_secret_aleatoire_ici
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
**Important** : Utilisez un secret fort et aléatoire pour sécuriser l'endpoint
|
|
25
|
-
cron. Vous pouvez générer un secret avec :
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
openssl rand -base64 32
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### 2. Configuration du service de cron (si plan Hobby)
|
|
32
|
-
|
|
33
|
-
Si vous êtes sur le plan Hobby de Vercel, vous devez utiliser un service externe
|
|
34
|
-
comme cron-job.org.
|
|
35
|
-
|
|
36
|
-
#### Configuration cron-job.org
|
|
37
|
-
|
|
38
|
-
1. Créez un compte sur [cron-job.org](https://cron-job.org/)
|
|
39
|
-
2. Créez un nouveau cron job avec ces paramètres :
|
|
40
|
-
- **URL** :
|
|
41
|
-
`https://votre-domaine.vercel.app/api/workflows/process?secret=VOTRE_CRON_SECRET`
|
|
42
|
-
- **Méthode** : `GET`
|
|
43
|
-
- **Schedule** : `Every minute` ou `*/1 * * * *`
|
|
44
|
-
- **Title** : "Workflow Actions Processor"
|
|
45
|
-
- **Active** : ✓
|
|
46
|
-
|
|
47
|
-
**Note** : Remplacez `VOTRE_CRON_SECRET` par la valeur de votre variable
|
|
48
|
-
d'environnement `CRON_SECRET`.
|
|
49
|
-
|
|
50
|
-
#### Autres services compatibles
|
|
51
|
-
|
|
52
|
-
- **EasyCron** : Même configuration que cron-job.org
|
|
53
|
-
- **UptimeRobot** : Utilisez l'URL avec le paramètre `secret`
|
|
54
|
-
- **GitHub Actions** : Voir la section GitHub Actions ci-dessous
|
|
55
|
-
|
|
56
|
-
### 3. Migration Prisma
|
|
57
|
-
|
|
58
|
-
Après avoir ajouté le modèle `ScheduledWorkflowAction` dans `schema.prisma`,
|
|
59
|
-
créez et exécutez la migration :
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
pnpm prisma migrate dev --name add_scheduled_workflow_actions
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 4. Configuration Vercel (optionnel - plans Pro/Enterprise uniquement)
|
|
66
|
-
|
|
67
|
-
Le fichier `vercel.json` est configuré pour exécuter le cron toutes les minutes
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
```json
|
|
71
|
-
{
|
|
72
|
-
"crons": [
|
|
73
|
-
{
|
|
74
|
-
"path": "/api/workflows/process",
|
|
75
|
-
"schedule": "* * * * *"
|
|
76
|
-
}
|
|
77
|
-
]
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**Note** : Vercel Cron est disponible uniquement sur les plans Pro et
|
|
82
|
-
Enterprise. Sur le plan Hobby, utilisez un service externe comme cron-job.org
|
|
83
|
-
(voir section 2).
|
|
84
|
-
|
|
85
|
-
### 5. Déploiement
|
|
86
|
-
|
|
87
|
-
1. Poussez vos changements sur votre repository
|
|
88
|
-
2. Si vous êtes sur plan Pro/Enterprise : Vercel détectera automatiquement le
|
|
89
|
-
fichier `vercel.json` et configurera le cron
|
|
90
|
-
3. Si vous êtes sur plan Hobby : Configurez cron-job.org ou un autre service
|
|
91
|
-
externe (voir section 2)
|
|
92
|
-
4. Vérifiez que `CRON_SECRET` est bien configuré dans les variables
|
|
93
|
-
d'environnement Vercel
|
|
94
|
-
|
|
95
|
-
## Fonctionnement détaillé
|
|
96
|
-
|
|
97
|
-
### Planification d'une action
|
|
98
|
-
|
|
99
|
-
Quand un workflow est déclenché et qu'une action a un délai (jours/heures), le
|
|
100
|
-
système :
|
|
101
|
-
|
|
102
|
-
1. Calcule la date d'exécution : `Date.now() + délai`
|
|
103
|
-
2. Sauvegarde l'action dans `ScheduledWorkflowAction` avec :
|
|
104
|
-
- Le type d'action (SEND_EMAIL, SEND_SMS, CHANGE_STATUS, CREATE_TASK)
|
|
105
|
-
- Les données nécessaires pour l'exécution (template, message, statut, etc.)
|
|
106
|
-
- La date d'exécution
|
|
107
|
-
- Le contact et le workflow concernés
|
|
108
|
-
|
|
109
|
-
### Exécution des actions
|
|
110
|
-
|
|
111
|
-
Toutes les minutes, Vercel Cron appelle `/api/workflows/process` qui :
|
|
112
|
-
|
|
113
|
-
1. Récupère toutes les actions non exécutées dont `executeAt <= maintenant`
|
|
114
|
-
2. Pour chaque action :
|
|
115
|
-
- Exécute l'action selon son type
|
|
116
|
-
- Marque l'action comme exécutée (`executed: true`)
|
|
117
|
-
- Enregistre la date d'exécution réelle
|
|
118
|
-
- En cas d'erreur, enregistre le message d'erreur
|
|
119
|
-
|
|
120
|
-
### Types d'actions supportées
|
|
121
|
-
|
|
122
|
-
- **SEND_EMAIL** : Envoie un email via SMTP avec template
|
|
123
|
-
- **SEND_SMS** : Envoie un SMS (nécessite une API SMS)
|
|
124
|
-
- **CHANGE_STATUS** : Change le statut du contact
|
|
125
|
-
- **CREATE_TASK** : Crée une tâche pour le contact
|
|
126
|
-
- **WAIT** : Action d'attente (gérée par le délai)
|
|
127
|
-
|
|
128
|
-
## Monitoring
|
|
129
|
-
|
|
130
|
-
Vous pouvez surveiller les actions planifiées via la base de données :
|
|
131
|
-
|
|
132
|
-
```sql
|
|
133
|
-
-- Actions en attente
|
|
134
|
-
SELECT * FROM scheduled_workflow_action
|
|
135
|
-
WHERE executed = false
|
|
136
|
-
ORDER BY executeAt ASC;
|
|
137
|
-
|
|
138
|
-
-- Actions récemment exécutées
|
|
139
|
-
SELECT * FROM scheduled_workflow_action
|
|
140
|
-
WHERE executed = true
|
|
141
|
-
ORDER BY executedAt DESC
|
|
142
|
-
LIMIT 50;
|
|
143
|
-
|
|
144
|
-
-- Actions en erreur
|
|
145
|
-
SELECT * FROM scheduled_workflow_action
|
|
146
|
-
WHERE executed = true AND error IS NOT NULL;
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## Limitations
|
|
150
|
-
|
|
151
|
-
- **Précision** : Les actions sont exécutées avec une précision d'environ 1
|
|
152
|
-
minute (selon la fréquence du cron)
|
|
153
|
-
- **Limite par exécution** : Maximum 50 actions traitées par exécution du cron
|
|
154
|
-
(pour éviter les timeouts)
|
|
155
|
-
- **Retry** : Les actions échouées ne sont pas automatiquement réessayées (elles
|
|
156
|
-
sont marquées comme exécutées avec une erreur)
|
|
157
|
-
|
|
158
|
-
## Dépannage
|
|
159
|
-
|
|
160
|
-
### Le cron ne s'exécute pas
|
|
161
|
-
|
|
162
|
-
1. **Si vous utilisez Vercel Cron** :
|
|
163
|
-
- Vérifiez que vous êtes sur un plan Vercel Pro ou Enterprise
|
|
164
|
-
- Vérifiez dans le dashboard Vercel que le cron est bien configuré (Settings
|
|
165
|
-
→ Cron Jobs)
|
|
166
|
-
- Vérifiez les logs Vercel pour voir les erreurs éventuelles
|
|
167
|
-
|
|
168
|
-
2. **Si vous utilisez un service externe (cron-job.org, etc.)** :
|
|
169
|
-
- Vérifiez que le job est actif dans votre service
|
|
170
|
-
- Vérifiez l'URL utilisée (doit inclure `?secret=VOTRE_CRON_SECRET`)
|
|
171
|
-
- Vérifiez les logs de votre service de cron
|
|
172
|
-
- Testez manuellement l'URL dans votre navigateur (avec le secret) pour voir
|
|
173
|
-
si elle répond
|
|
174
|
-
|
|
175
|
-
### Les actions ne s'exécutent pas
|
|
176
|
-
|
|
177
|
-
1. Vérifiez que `CRON_SECRET` est bien configuré dans Vercel
|
|
178
|
-
2. Vérifiez les logs de l'endpoint `/api/workflows/process`
|
|
179
|
-
3. Vérifiez dans la base de données que les actions sont bien créées
|
|
180
|
-
4. Vérifiez que `executeAt` est bien dans le passé
|
|
181
|
-
|
|
182
|
-
### Erreurs d'exécution
|
|
183
|
-
|
|
184
|
-
Les erreurs sont enregistrées dans le champ `error` de
|
|
185
|
-
`ScheduledWorkflowAction`. Consultez ce champ pour diagnostiquer les problèmes.
|