create-crm-tmp 1.1.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/.prettierignore +2 -0
- package/template/README.md +1 -1
- package/template/components.json +22 -0
- package/template/exemple-contacts.csv +54 -0
- package/template/next.config.ts +27 -1
- package/template/package.json +51 -16
- package/template/prisma/schema.prisma +807 -58
- package/template/skills-lock.json +25 -0
- package/template/src/app/(auth)/invite/[token]/page.tsx +21 -24
- package/template/src/app/(auth)/reset-password/complete/page.tsx +12 -21
- package/template/src/app/(auth)/reset-password/page.tsx +12 -8
- package/template/src/app/(auth)/reset-password/verify/page.tsx +12 -8
- package/template/src/app/(auth)/signin/page.tsx +20 -17
- package/template/src/app/(dashboard)/agenda/page.tsx +2232 -2189
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +680 -323
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
- package/template/src/app/(dashboard)/automatisation/page.tsx +473 -180
- package/template/src/app/(dashboard)/closing/page.tsx +500 -468
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +5049 -4110
- package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1703 -0
- package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +3776 -2064
- package/template/src/app/(dashboard)/dashboard/page.tsx +37 -519
- package/template/src/app/(dashboard)/error.tsx +37 -0
- package/template/src/app/(dashboard)/layout.tsx +1 -1
- package/template/src/app/(dashboard)/loading.tsx +5 -0
- package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
- package/template/src/app/(dashboard)/settings/page.tsx +2685 -2489
- package/template/src/app/(dashboard)/templates/page.tsx +500 -300
- package/template/src/app/(dashboard)/users/list/page.tsx +356 -350
- package/template/src/app/(dashboard)/users/page.tsx +279 -310
- package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
- package/template/src/app/(dashboard)/users/roles/page.tsx +164 -137
- package/template/src/app/api/audit-logs/route.ts +1 -1
- package/template/src/app/api/auth/google/callback/route.ts +8 -5
- package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
- package/template/src/app/api/companies/[id]/activities/route.ts +131 -0
- package/template/src/app/api/companies/[id]/route.ts +195 -0
- package/template/src/app/api/companies/export/route.ts +206 -0
- package/template/src/app/api/companies/route.ts +166 -0
- package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
- package/template/src/app/api/contact-views/[id]/route.ts +197 -0
- package/template/src/app/api/contact-views/route.ts +146 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +77 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +7 -17
- package/template/src/app/api/contacts/[id]/files/route.ts +83 -44
- package/template/src/app/api/contacts/[id]/interactions/route.ts +37 -0
- package/template/src/app/api/contacts/[id]/kyc/route.ts +71 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +38 -29
- package/template/src/app/api/contacts/[id]/route.ts +111 -20
- package/template/src/app/api/contacts/[id]/send-email/route.ts +6 -0
- package/template/src/app/api/contacts/[id]/workflows/run/route.ts +61 -0
- package/template/src/app/api/contacts/export/route.ts +13 -18
- package/template/src/app/api/contacts/import/route.ts +22 -19
- package/template/src/app/api/contacts/import-preview/route.ts +139 -0
- package/template/src/app/api/contacts/route.ts +202 -49
- package/template/src/app/api/dashboard/stats/route.ts +9 -292
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +203 -185
- package/template/src/app/api/invite/complete/route.ts +20 -23
- package/template/src/app/api/reminders/route.ts +1 -0
- package/template/src/app/api/reset-password/complete/route.ts +11 -13
- package/template/src/app/api/send/route.ts +9 -43
- package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
- package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
- package/template/src/app/api/settings/company/route.ts +19 -26
- package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
- package/template/src/app/api/settings/google-ads/route.ts +20 -23
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +20 -23
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +23 -32
- package/template/src/app/api/settings/google-sheet/preview/route.ts +104 -0
- package/template/src/app/api/settings/google-sheet/route.ts +20 -23
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -23
- package/template/src/app/api/settings/meta-leads/route.ts +20 -23
- package/template/src/app/api/settings/statuses/[id]/route.ts +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 +14 -7
- package/template/src/app/api/tasks/[id]/route.ts +173 -137
- package/template/src/app/api/tasks/meet/route.ts +11 -8
- package/template/src/app/api/tasks/route.ts +155 -95
- package/template/src/app/api/templates/[id]/route.ts +22 -13
- package/template/src/app/api/templates/route.ts +22 -5
- package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
- package/template/src/app/api/users/[id]/route.ts +2 -2
- package/template/src/app/api/users/commercials/route.ts +38 -0
- package/template/src/app/api/users/for-agenda/route.ts +1 -2
- package/template/src/app/api/users/route.ts +89 -34
- package/template/src/app/api/webhooks/google-ads/route.ts +20 -1
- package/template/src/app/api/webhooks/meta-leads/route.ts +18 -1
- package/template/src/app/api/workflows/[id]/route.ts +33 -6
- package/template/src/app/api/workflows/process/route.ts +510 -146
- package/template/src/app/api/workflows/route.ts +46 -4
- package/template/src/app/globals.css +243 -101
- package/template/src/app/layout.tsx +19 -8
- package/template/src/app/page.tsx +37 -7
- package/template/src/components/address-autocomplete.tsx +232 -0
- package/template/src/components/contacts/filter-bar.tsx +181 -0
- package/template/src/components/contacts/filter-builder.tsx +589 -0
- package/template/src/components/contacts/save-view-dialog.tsx +160 -0
- package/template/src/components/contacts/views-tab-bar.tsx +440 -0
- package/template/src/components/dashboard/activity-chart.tsx +31 -39
- package/template/src/components/dashboard/dashboard-content.tsx +79 -0
- package/template/src/components/dashboard/stat-card.tsx +40 -42
- package/template/src/components/dashboard/tasks-pie-chart.tsx +34 -37
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +78 -72
- package/template/src/components/date-picker.tsx +396 -0
- package/template/src/components/editor.tsx +27 -13
- package/template/src/components/email-template.tsx +4 -2
- package/template/src/components/global-search.tsx +358 -0
- package/template/src/components/header.tsx +57 -62
- package/template/src/components/invitation-email-template.tsx +4 -2
- package/template/src/components/lazy-editor.tsx +11 -0
- package/template/src/components/meet-cancellation-email-template.tsx +11 -3
- package/template/src/components/meet-confirmation-email-template.tsx +10 -3
- package/template/src/components/meet-update-email-template.tsx +10 -3
- package/template/src/components/page-header.tsx +19 -15
- package/template/src/components/protected-page.tsx +94 -0
- package/template/src/components/reset-password-email-template.tsx +4 -2
- package/template/src/components/sidebar.tsx +92 -94
- package/template/src/components/skeleton.tsx +128 -42
- package/template/src/components/ui/accordion.tsx +64 -0
- package/template/src/components/ui/alert-dialog.tsx +139 -0
- package/template/src/components/ui/button.tsx +60 -0
- package/template/src/components/view-as-banner.tsx +1 -1
- package/template/src/components/view-as-modal.tsx +21 -16
- package/template/src/config/nav-pages.ts +108 -0
- package/template/src/contexts/app-toast-context.tsx +174 -0
- package/template/src/contexts/sidebar-context.tsx +16 -47
- package/template/src/contexts/task-reminder-context.tsx +6 -6
- package/template/src/contexts/view-as-context.tsx +11 -16
- package/template/src/hooks/use-alert.tsx +65 -0
- package/template/src/hooks/use-confirm.tsx +87 -0
- package/template/src/hooks/use-contact-views.ts +140 -0
- package/template/src/hooks/use-contacts.ts +69 -0
- package/template/src/hooks/use-fetch.ts +17 -0
- package/template/src/hooks/use-focus-trap.ts +73 -0
- package/template/src/hooks/use-statuses.ts +22 -0
- package/template/src/lib/address-api.ts +155 -0
- package/template/src/lib/cache.ts +73 -0
- package/template/src/lib/check-permission.ts +12 -177
- package/template/src/lib/contact-interactions.ts +3 -1
- package/template/src/lib/contact-view-filters.ts +341 -0
- package/template/src/lib/dashboard-stats.ts +224 -0
- package/template/src/lib/date-utils.ts +49 -0
- package/template/src/lib/get-auth-user.ts +25 -0
- package/template/src/lib/google-calendar.ts +54 -12
- package/template/src/lib/google-drive.ts +796 -75
- package/template/src/lib/google-fetch.ts +63 -0
- package/template/src/lib/local-storage.ts +34 -0
- package/template/src/lib/permissions.ts +245 -47
- package/template/src/lib/prisma.ts +11 -11
- package/template/src/lib/roles.ts +12 -15
- package/template/src/lib/template-variables.ts +67 -33
- package/template/src/lib/utils.ts +26 -11
- package/template/src/lib/workflow-executor.ts +445 -228
- package/template/src/proxy.ts +34 -73
- package/template/src/types/contact-views.ts +351 -0
- package/template/src/types/yousign.ts +52 -0
- package/template/vercel.json +12 -0
- package/template/WORKFLOWS_CRON.md +0 -185
- package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
- package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
- package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
- package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
- package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
- package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
- package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
- package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
- package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
- package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
- package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
- package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
- package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
- package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
- package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
- package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
- package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
- package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
- package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
- package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
- package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
- package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
- package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
- package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
- package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
- package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
- package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
- package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
- package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
- package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
- package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
- package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
- package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
- package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
- package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
- package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
- package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
- package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
- package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
- package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
- package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
- package/template/prisma/migrations/20260226093949_fix_cascade_on_user_delete/migration.sql +0 -69
- package/template/prisma/migrations/migration_lock.toml +0 -3
- package/template/src/app/(dashboard)/users/layout.tsx +0 -30
- package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
- package/template/src/app/api/dashboard/widgets/route.ts +0 -181
- package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
- package/template/src/components/dashboard/color-picker.tsx +0 -65
- package/template/src/components/dashboard/contacts-chart.tsx +0 -69
- package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
- package/template/src/components/dashboard/recent-activity.tsx +0 -157
- package/template/src/components/dashboard/status-distribution-chart.tsx +0 -82
- package/template/src/components/dashboard/top-contacts-list.tsx +0 -119
- package/template/src/components/dashboard/widget-wrapper.tsx +0 -39
- package/template/src/contexts/dashboard-theme-context.tsx +0 -58
- package/template/src/lib/dashboard-themes.ts +0 -140
- package/template/src/lib/default-widgets.ts +0 -14
- package/template/src/lib/widget-registry.ts +0 -177
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
-
import Link from 'next/link';
|
|
5
4
|
import { useRouter } from 'next/navigation';
|
|
6
5
|
import { PageHeader } from '@/components/page-header';
|
|
7
|
-
import {
|
|
6
|
+
import { PageLoader } from '@/components/skeleton';
|
|
7
|
+
import {
|
|
8
|
+
Plus,
|
|
9
|
+
Trash2,
|
|
10
|
+
Search,
|
|
11
|
+
ChevronDown,
|
|
12
|
+
ChevronUp,
|
|
13
|
+
Zap,
|
|
14
|
+
Mail,
|
|
15
|
+
MessageSquare,
|
|
16
|
+
Tag,
|
|
17
|
+
CheckSquare,
|
|
18
|
+
Clock,
|
|
19
|
+
UserPlus,
|
|
20
|
+
StickyNote,
|
|
21
|
+
Bell,
|
|
22
|
+
ArrowRightLeft,
|
|
23
|
+
ShoppingCart,
|
|
24
|
+
Users,
|
|
25
|
+
Filter,
|
|
26
|
+
Info,
|
|
27
|
+
} from 'lucide-react';
|
|
8
28
|
import { cn } from '@/lib/utils';
|
|
29
|
+
import { ProtectedPage } from '@/components/protected-page';
|
|
30
|
+
import { useConfirm } from '@/hooks/use-confirm';
|
|
31
|
+
import { useUserRole } from '@/hooks/use-user-role';
|
|
32
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
9
33
|
|
|
10
34
|
interface Status {
|
|
11
35
|
id: string;
|
|
@@ -18,7 +42,7 @@ interface Workflow {
|
|
|
18
42
|
name: string;
|
|
19
43
|
description: string | null;
|
|
20
44
|
active: boolean;
|
|
21
|
-
triggerType:
|
|
45
|
+
triggerType: string;
|
|
22
46
|
triggerFromStatusId: string | null;
|
|
23
47
|
triggerToStatusId: string | null;
|
|
24
48
|
triggerTimeDays: number | null;
|
|
@@ -30,12 +54,21 @@ interface Workflow {
|
|
|
30
54
|
|
|
31
55
|
export default function AutomatisationPage() {
|
|
32
56
|
const router = useRouter();
|
|
57
|
+
const { confirm, ConfirmDialog } = useConfirm();
|
|
58
|
+
const { hasPermission } = useUserRole();
|
|
59
|
+
const toast = useAppToast();
|
|
60
|
+
|
|
61
|
+
// Permissions
|
|
62
|
+
const canCreate = hasPermission('workflows.create');
|
|
63
|
+
const canDelete = hasPermission('workflows.delete');
|
|
64
|
+
|
|
33
65
|
const [workflows, setWorkflows] = useState<Workflow[]>([]);
|
|
34
66
|
const [statuses, setStatuses] = useState<Status[]>([]);
|
|
35
67
|
const [loading, setLoading] = useState(true);
|
|
36
68
|
const [error, setError] = useState('');
|
|
37
69
|
const [success, setSuccess] = useState('');
|
|
38
70
|
const [search, setSearch] = useState('');
|
|
71
|
+
const [showGuide, setShowGuide] = useState(false);
|
|
39
72
|
|
|
40
73
|
async function fetchWorkflows() {
|
|
41
74
|
try {
|
|
@@ -71,8 +104,28 @@ export default function AutomatisationPage() {
|
|
|
71
104
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
72
105
|
}, []);
|
|
73
106
|
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!success) return;
|
|
109
|
+
toast.success(success);
|
|
110
|
+
setSuccess('');
|
|
111
|
+
}, [success, toast]);
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (!error) return;
|
|
115
|
+
toast.error(error);
|
|
116
|
+
setError('');
|
|
117
|
+
}, [error, toast]);
|
|
118
|
+
|
|
74
119
|
const handleDelete = async (id: string) => {
|
|
75
|
-
|
|
120
|
+
const confirmed = await confirm({
|
|
121
|
+
title: 'Supprimer le workflow',
|
|
122
|
+
description: 'Voulez-vous vraiment supprimer ce workflow ? Cette action est irréversible.',
|
|
123
|
+
confirmText: 'Supprimer',
|
|
124
|
+
cancelText: 'Annuler',
|
|
125
|
+
variant: 'destructive',
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!confirmed) {
|
|
76
129
|
return;
|
|
77
130
|
}
|
|
78
131
|
|
|
@@ -117,7 +170,7 @@ export default function AutomatisationPage() {
|
|
|
117
170
|
setError(err.message || 'Erreur lors de la mise à jour');
|
|
118
171
|
}
|
|
119
172
|
};
|
|
120
|
-
const getTriggerLabel = (triggerType:
|
|
173
|
+
const getTriggerLabel = (triggerType: string) => {
|
|
121
174
|
switch (triggerType) {
|
|
122
175
|
case 'CONTACT_CREATED':
|
|
123
176
|
return 'Nouveau contact créé';
|
|
@@ -127,8 +180,16 @@ export default function AutomatisationPage() {
|
|
|
127
180
|
return 'Basé sur le temps';
|
|
128
181
|
case 'MANUAL':
|
|
129
182
|
return 'Déclencheur manuel';
|
|
183
|
+
case 'TASK_COMPLETED':
|
|
184
|
+
return 'Tâche complétée';
|
|
185
|
+
case 'TRANSACTION_CREATED':
|
|
186
|
+
return 'Transaction créée';
|
|
187
|
+
case 'TRANSACTION_STATUS_CHANGED':
|
|
188
|
+
return 'Changement statut transaction';
|
|
189
|
+
case 'CONTACT_ASSIGNMENT_CHANGED':
|
|
190
|
+
return "Changement d'assignation";
|
|
130
191
|
default:
|
|
131
|
-
return
|
|
192
|
+
return triggerType;
|
|
132
193
|
}
|
|
133
194
|
};
|
|
134
195
|
|
|
@@ -142,196 +203,428 @@ export default function AutomatisationPage() {
|
|
|
142
203
|
});
|
|
143
204
|
|
|
144
205
|
return (
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
<div className="mb-4 rounded-lg border border-green-200 bg-green-50 p-4">
|
|
163
|
-
<p className="text-sm font-medium text-green-800">{success}</p>
|
|
164
|
-
</div>
|
|
165
|
-
)}
|
|
206
|
+
<ProtectedPage requiredPermission="workflows.view">
|
|
207
|
+
<div className="h-full">
|
|
208
|
+
<PageHeader
|
|
209
|
+
title="Automatisation"
|
|
210
|
+
description="Créez et gérez vos workflows d'automatisation"
|
|
211
|
+
action={
|
|
212
|
+
canCreate ? (
|
|
213
|
+
<button
|
|
214
|
+
onClick={() => router.push('/automatisation/new')}
|
|
215
|
+
className="inline-flex cursor-pointer items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700 focus-visible:ring-2 focus-visible:ring-gray-400/30 focus-visible:ring-offset-2 focus-visible:outline-none"
|
|
216
|
+
>
|
|
217
|
+
<Plus className="h-4 w-4" />
|
|
218
|
+
Nouveau workflow
|
|
219
|
+
</button>
|
|
220
|
+
) : undefined
|
|
221
|
+
}
|
|
222
|
+
/>
|
|
166
223
|
|
|
167
|
-
|
|
168
|
-
<div className="mb-4 rounded-lg border border-red-200 bg-red-50 p-4">
|
|
169
|
-
<p className="text-sm font-medium text-red-800">{error}</p>
|
|
170
|
-
</div>
|
|
171
|
-
)}
|
|
224
|
+
<div className="p-4 sm:p-6 lg:p-8">
|
|
172
225
|
|
|
173
|
-
|
|
174
|
-
<div className="
|
|
175
|
-
<p className="text-sm text-gray-500">Chargement des workflows...</p>
|
|
176
|
-
</div>
|
|
177
|
-
) : workflows.length === 0 ? (
|
|
178
|
-
<div className="rounded-xl bg-white p-12 text-center shadow-sm">
|
|
179
|
-
<p className="text-gray-500">Aucun workflow créé pour le moment.</p>
|
|
226
|
+
{/* Guide d'utilisation */}
|
|
227
|
+
<div className="mb-6 rounded-2xl border border-blue-100 bg-white shadow-sm">
|
|
180
228
|
<button
|
|
181
|
-
|
|
182
|
-
|
|
229
|
+
type="button"
|
|
230
|
+
onClick={() => setShowGuide((prev) => !prev)}
|
|
231
|
+
className="flex w-full cursor-pointer items-center justify-between px-5 py-4"
|
|
183
232
|
>
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
</
|
|
196
|
-
</div>
|
|
197
|
-
<div className="relative w-full max-w-xs">
|
|
198
|
-
<Search className="pointer-events-none absolute top-2.5 left-3 h-4 w-4 text-gray-400" />
|
|
199
|
-
<input
|
|
200
|
-
type="text"
|
|
201
|
-
value={search}
|
|
202
|
-
onChange={(e) => setSearch(e.target.value)}
|
|
203
|
-
placeholder="Rechercher une automatisation..."
|
|
204
|
-
className="w-full rounded-full border border-gray-200 bg-gray-50 py-2 pr-3 pl-9 text-sm text-gray-900 placeholder:text-gray-400 focus:border-indigo-500 focus:bg-white focus:ring-2 focus:ring-indigo-500/30 focus:outline-none"
|
|
205
|
-
/>
|
|
233
|
+
<div className="flex items-center gap-3">
|
|
234
|
+
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-blue-50">
|
|
235
|
+
<Info className="h-5 w-5 text-blue-600" />
|
|
236
|
+
</div>
|
|
237
|
+
<div className="text-left">
|
|
238
|
+
<p className="text-sm font-semibold text-gray-900">
|
|
239
|
+
Comment fonctionnent les automatisations ?
|
|
240
|
+
</p>
|
|
241
|
+
<p className="mt-0.5 text-xs text-gray-500">
|
|
242
|
+
Déclencheurs, actions, conditions -- tout comprendre en un coup d'oeil
|
|
243
|
+
</p>
|
|
244
|
+
</div>
|
|
206
245
|
</div>
|
|
207
|
-
|
|
246
|
+
{showGuide ? (
|
|
247
|
+
<ChevronUp className="h-5 w-5 text-gray-400" />
|
|
248
|
+
) : (
|
|
249
|
+
<ChevronDown className="h-5 w-5 text-gray-400" />
|
|
250
|
+
)}
|
|
251
|
+
</button>
|
|
208
252
|
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
253
|
+
{showGuide && (
|
|
254
|
+
<div className="border-t border-blue-50 px-5 pt-4 pb-6">
|
|
255
|
+
{/* Principe */}
|
|
256
|
+
<p className="text-sm leading-relaxed text-gray-600">
|
|
257
|
+
Un workflow suit un schéma simple : un <strong>déclencheur</strong> lance la
|
|
258
|
+
séquence, puis une ou plusieurs <strong>actions</strong> s'exécutent dans
|
|
259
|
+
l'ordre. Chaque action peut avoir un <strong>délai</strong> (jours / heures)
|
|
260
|
+
et une <strong>condition</strong> optionnelle pour s'exécuter uniquement si
|
|
261
|
+
le contact remplit certains critères.
|
|
262
|
+
</p>
|
|
263
|
+
|
|
264
|
+
{/* Déclencheurs */}
|
|
265
|
+
<div className="mt-5">
|
|
266
|
+
<h3 className="flex items-center gap-2 text-xs font-semibold tracking-wide text-blue-600 uppercase">
|
|
267
|
+
<Zap className="h-4 w-4" />
|
|
268
|
+
Déclencheurs disponibles
|
|
269
|
+
</h3>
|
|
270
|
+
<div className="mt-3 grid gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
271
|
+
{[
|
|
272
|
+
{
|
|
273
|
+
icon: <UserPlus className="h-4 w-4 text-emerald-600" />,
|
|
274
|
+
title: 'Contact créé',
|
|
275
|
+
desc: "Se lance dès qu'un nouveau contact est ajouté au CRM.",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
icon: <Tag className="h-4 w-4 text-blue-600" />,
|
|
279
|
+
title: 'Changement de statut',
|
|
280
|
+
desc: 'Se lance quand le statut du contact change (ex. Nouveau \u2192 Qualifié). Filtrable par statut source/cible.',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
icon: <Clock className="h-4 w-4 text-blue-600" />,
|
|
284
|
+
title: 'Basé sur le temps',
|
|
285
|
+
desc: 'Se lance X jours/heures après un événement (création, dernier changement de statut, dernière interaction).',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
icon: <CheckSquare className="h-4 w-4 text-violet-600" />,
|
|
289
|
+
title: 'Tâche complétée',
|
|
290
|
+
desc: 'Se lance quand une tâche liée à un contact est terminée. Filtrable par type de tâche.',
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
icon: <ShoppingCart className="h-4 w-4 text-orange-600" />,
|
|
294
|
+
title: 'Transaction créée',
|
|
295
|
+
desc: 'Se lance quand une nouvelle transaction est créée pour un contact.',
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
icon: <ArrowRightLeft className="h-4 w-4 text-rose-600" />,
|
|
299
|
+
title: 'Statut transaction',
|
|
300
|
+
desc: 'Se lance quand le statut d\u2019une transaction change (ex. Signée, Complétée). Filtrable par statut.',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
icon: <Users className="h-4 w-4 text-cyan-600" />,
|
|
304
|
+
title: "Changement d'assignation",
|
|
305
|
+
desc: 'Se lance quand le commercial ou le télépro assigné au contact change.',
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
icon: <Zap className="h-4 w-4 text-purple-600" />,
|
|
309
|
+
title: 'Manuel',
|
|
310
|
+
desc: "Se lance manuellement depuis la fiche d'un contact (bouton \u00ab Auto \u00bb).",
|
|
311
|
+
},
|
|
312
|
+
].map((item) => (
|
|
313
|
+
<div
|
|
314
|
+
key={item.title}
|
|
315
|
+
className="rounded-xl border border-gray-100 bg-gray-50/50 p-3"
|
|
316
|
+
>
|
|
317
|
+
<div className="flex items-center gap-2">
|
|
318
|
+
{item.icon}
|
|
319
|
+
<p className="text-xs font-semibold text-gray-900">{item.title}</p>
|
|
248
320
|
</div>
|
|
249
|
-
|
|
250
|
-
|
|
321
|
+
<p className="mt-1.5 text-[11px] leading-relaxed text-gray-500">
|
|
322
|
+
{item.desc}
|
|
323
|
+
</p>
|
|
324
|
+
</div>
|
|
325
|
+
))}
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
{/* Actions */}
|
|
330
|
+
<div className="mt-5">
|
|
331
|
+
<h3 className="flex items-center gap-2 text-xs font-semibold tracking-wide text-blue-600 uppercase">
|
|
332
|
+
<CheckSquare className="h-4 w-4" />
|
|
333
|
+
Actions possibles
|
|
334
|
+
</h3>
|
|
335
|
+
<div className="mt-3 grid gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
336
|
+
{[
|
|
337
|
+
{
|
|
338
|
+
icon: <Mail className="h-4 w-4 text-blue-500" />,
|
|
339
|
+
title: 'Envoyer un email',
|
|
340
|
+
desc: 'Envoie un email au contact en utilisant un template prédéfini. Les variables (prénom, nom, etc.) sont remplacées automatiquement.',
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
icon: <MessageSquare className="h-4 w-4 text-blue-500" />,
|
|
344
|
+
title: 'Envoyer un SMS',
|
|
345
|
+
desc: 'Envoie un SMS au numéro du contact avec un message personnalisable (variables supportées).',
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
icon: <Tag className="h-4 w-4 text-blue-500" />,
|
|
349
|
+
title: 'Changer le statut',
|
|
350
|
+
desc: 'Modifie automatiquement le statut du contact vers un statut de votre choix.',
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
icon: <CheckSquare className="h-4 w-4 text-blue-500" />,
|
|
354
|
+
title: 'Créer une tâche',
|
|
355
|
+
desc: 'Crée une tâche (appel, rendez-vous, etc.) avec type, priorité et assignation personnalisables.',
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
icon: <UserPlus className="h-4 w-4 text-blue-500" />,
|
|
359
|
+
title: 'Assigner le contact',
|
|
360
|
+
desc: 'Assigne ou réassigne le contact à un commercial et/ou un télépro de votre choix.',
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
icon: <StickyNote className="h-4 w-4 text-blue-500" />,
|
|
364
|
+
title: 'Ajouter une note',
|
|
365
|
+
desc: "Crée une note dans l'historique du contact. Supporte les variables de template.",
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
icon: <Bell className="h-4 w-4 text-blue-500" />,
|
|
369
|
+
title: 'Notifier un utilisateur',
|
|
370
|
+
desc: "Crée une tâche pour un utilisateur spécifique (ex. alerter un manager qu'un contact est qualifié).",
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
icon: <Clock className="h-4 w-4 text-blue-500" />,
|
|
374
|
+
title: 'Attendre',
|
|
375
|
+
desc: "Insère un délai dans la séquence avant l'action suivante (en jours et/ou heures).",
|
|
376
|
+
},
|
|
377
|
+
].map((item) => (
|
|
378
|
+
<div
|
|
379
|
+
key={item.title}
|
|
380
|
+
className="rounded-xl border border-gray-100 bg-gray-50/50 p-3"
|
|
381
|
+
>
|
|
251
382
|
<div className="flex items-center gap-2">
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
</span>
|
|
255
|
-
{workflow.triggerType === 'STATUS_CHANGED' && (
|
|
256
|
-
<span className="text-xs text-gray-500">
|
|
257
|
-
{statuses.find((s) => s.id === workflow.triggerFromStatusId)?.name ??
|
|
258
|
-
'Tous'}{' '}
|
|
259
|
-
→{' '}
|
|
260
|
-
{statuses.find((s) => s.id === workflow.triggerToStatusId)?.name ??
|
|
261
|
-
'Tous'}
|
|
262
|
-
</span>
|
|
263
|
-
)}
|
|
383
|
+
{item.icon}
|
|
384
|
+
<p className="text-xs font-semibold text-gray-900">{item.title}</p>
|
|
264
385
|
</div>
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
386
|
+
<p className="mt-1.5 text-[11px] leading-relaxed text-gray-500">
|
|
387
|
+
{item.desc}
|
|
388
|
+
</p>
|
|
389
|
+
</div>
|
|
390
|
+
))}
|
|
391
|
+
</div>
|
|
392
|
+
</div>
|
|
393
|
+
|
|
394
|
+
{/* Conditions */}
|
|
395
|
+
<div className="mt-5">
|
|
396
|
+
<h3 className="flex items-center gap-2 text-xs font-semibold tracking-wide text-blue-600 uppercase">
|
|
397
|
+
<Filter className="h-4 w-4" />
|
|
398
|
+
Conditions sur les actions
|
|
399
|
+
</h3>
|
|
400
|
+
<p className="mt-2 text-xs leading-relaxed text-gray-600">
|
|
401
|
+
Chaque action peut être conditionnée pour ne s'exécuter que si le contact
|
|
402
|
+
remplit certains critères au moment du traitement :
|
|
403
|
+
</p>
|
|
404
|
+
<ul className="mt-2 space-y-1.5 text-xs text-gray-600">
|
|
405
|
+
<li className="flex items-start gap-2">
|
|
406
|
+
<span className="mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-blue-400" />
|
|
407
|
+
<span>
|
|
408
|
+
<strong>Statut du contact</strong> -- l'action s'exécute
|
|
409
|
+
uniquement si le statut du contact est (ou n'est pas) égal à un statut
|
|
410
|
+
donné.
|
|
411
|
+
</span>
|
|
412
|
+
</li>
|
|
413
|
+
<li className="flex items-start gap-2">
|
|
414
|
+
<span className="mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-blue-400" />
|
|
415
|
+
<span>
|
|
416
|
+
<strong>Origine du contact</strong> -- filtrer par origine (ex. « Site
|
|
417
|
+
web », « Facebook »).
|
|
418
|
+
</span>
|
|
419
|
+
</li>
|
|
420
|
+
<li className="flex items-start gap-2">
|
|
421
|
+
<span className="mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-blue-400" />
|
|
422
|
+
<span>
|
|
423
|
+
<strong>Société</strong> -- exécuter uniquement si le contact est (ou
|
|
424
|
+
n'est pas) rattaché à une société.
|
|
425
|
+
</span>
|
|
426
|
+
</li>
|
|
427
|
+
</ul>
|
|
428
|
+
</div>
|
|
429
|
+
|
|
430
|
+
{/* Exemples */}
|
|
431
|
+
<div className="mt-5 rounded-xl border border-dashed border-blue-200 bg-blue-50/30 p-4">
|
|
432
|
+
<h3 className="text-xs font-semibold text-blue-700">
|
|
433
|
+
Exemples de workflows courants
|
|
434
|
+
</h3>
|
|
435
|
+
<ul className="mt-2 space-y-2 text-xs leading-relaxed text-blue-900/70">
|
|
436
|
+
<li>
|
|
437
|
+
<strong>Séquence de bienvenue :</strong> Déclencheur « Contact créé
|
|
438
|
+
» → Envoyer un email de bienvenue → Attendre 2 jours →
|
|
439
|
+
Créer une tâche d'appel pour le commercial.
|
|
440
|
+
</li>
|
|
441
|
+
<li>
|
|
442
|
+
<strong>Relance automatique :</strong> Déclencheur « Basé sur le temps
|
|
443
|
+
» (7 jours sans interaction) → Envoyer un email de relance →
|
|
444
|
+
Ajouter une note dans l'historique.
|
|
445
|
+
</li>
|
|
446
|
+
<li>
|
|
447
|
+
<strong>Qualification post-signature :</strong> Déclencheur « Statut
|
|
448
|
+
transaction » vers « Signée » → Changer le statut du
|
|
449
|
+
contact en « Client » → Notifier le manager.
|
|
450
|
+
</li>
|
|
451
|
+
<li>
|
|
452
|
+
<strong>Réassignation intelligente :</strong> Déclencheur « Tâche
|
|
453
|
+
complétée » (type Appel) → Si statut ≠ « Qualifié »,
|
|
454
|
+
assigner à un autre commercial.
|
|
455
|
+
</li>
|
|
456
|
+
</ul>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
)}
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
{loading ? (
|
|
463
|
+
<PageLoader text="Chargement des workflows..." />
|
|
464
|
+
) : workflows.length === 0 ? (
|
|
465
|
+
<div className="rounded-xl bg-white p-12 text-center shadow-sm">
|
|
466
|
+
<p className="text-gray-500">Aucun workflow créé pour le moment.</p>
|
|
467
|
+
{canCreate && (
|
|
468
|
+
<button
|
|
469
|
+
onClick={() => router.push('/automatisation/new')}
|
|
470
|
+
className="mt-4 inline-flex cursor-pointer items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700"
|
|
471
|
+
>
|
|
472
|
+
<Plus className="h-4 w-4" />
|
|
473
|
+
Créer votre premier workflow
|
|
474
|
+
</button>
|
|
475
|
+
)}
|
|
476
|
+
</div>
|
|
477
|
+
) : (
|
|
478
|
+
<div className="rounded-xl border border-gray-200 bg-white shadow-sm">
|
|
479
|
+
<div className="flex flex-col gap-3 border-b border-gray-100 px-4 py-3 sm:flex-row sm:items-center sm:justify-between sm:px-6">
|
|
480
|
+
<div className="flex items-center gap-2 text-sm text-gray-500">
|
|
481
|
+
<span className="font-medium text-gray-900">Automatisations</span>
|
|
482
|
+
<span className="rounded-full bg-gray-100 px-2 py-0.5 text-xs">
|
|
483
|
+
{workflows.length}
|
|
484
|
+
</span>
|
|
485
|
+
</div>
|
|
486
|
+
<div className="relative w-full max-w-xs">
|
|
487
|
+
<Search className="pointer-events-none absolute top-2.5 left-3 h-4 w-4 text-gray-400" />
|
|
488
|
+
<input
|
|
489
|
+
type="text"
|
|
490
|
+
value={search}
|
|
491
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
492
|
+
placeholder="Rechercher une automatisation..."
|
|
493
|
+
className="w-full rounded-full border border-gray-200 bg-gray-50 py-2 pr-3 pl-9 text-sm text-gray-900 placeholder:text-gray-400 focus:bg-white focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
|
|
494
|
+
/>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
|
|
498
|
+
<div className="overflow-x-auto">
|
|
499
|
+
<table className="min-w-full divide-y divide-gray-100 text-sm">
|
|
500
|
+
<thead className="bg-gray-50/60">
|
|
501
|
+
<tr>
|
|
502
|
+
<th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
|
|
503
|
+
Nom
|
|
504
|
+
</th>
|
|
505
|
+
<th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
|
|
506
|
+
Déclencheur
|
|
507
|
+
</th>
|
|
508
|
+
<th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
|
|
509
|
+
Actions
|
|
510
|
+
</th>
|
|
511
|
+
<th className="px-6 py-3 text-left text-xs font-medium tracking-wide text-gray-500 uppercase">
|
|
512
|
+
Créé le
|
|
513
|
+
</th>
|
|
514
|
+
<th className="px-6 py-3 text-center text-xs font-medium tracking-wide text-gray-500 uppercase">
|
|
515
|
+
Statut
|
|
516
|
+
</th>
|
|
517
|
+
<th className="px-4 py-3 text-right text-xs font-medium tracking-wide text-gray-500 uppercase">
|
|
518
|
+
Actions
|
|
519
|
+
</th>
|
|
520
|
+
</tr>
|
|
521
|
+
</thead>
|
|
522
|
+
<tbody className="divide-y divide-gray-100">
|
|
523
|
+
{filteredWorkflows.map((workflow) => (
|
|
524
|
+
<tr
|
|
525
|
+
key={workflow.id}
|
|
526
|
+
className="group cursor-pointer transition-colors hover:bg-blue-50/40"
|
|
527
|
+
onClick={() => router.push(`/automatisation/${workflow.id}`)}
|
|
280
528
|
>
|
|
281
|
-
<
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
529
|
+
<td className="px-6 py-4 whitespace-nowrap">
|
|
530
|
+
<div className="flex flex-col">
|
|
531
|
+
<span className="font-medium text-gray-900">{workflow.name}</span>
|
|
532
|
+
{workflow.description && (
|
|
533
|
+
<span className="mt-0.5 line-clamp-1 text-xs text-gray-500">
|
|
534
|
+
{workflow.description}
|
|
535
|
+
</span>
|
|
536
|
+
)}
|
|
537
|
+
</div>
|
|
538
|
+
</td>
|
|
539
|
+
<td className="px-6 py-4 whitespace-nowrap text-gray-700">
|
|
540
|
+
<div className="flex items-center gap-2">
|
|
541
|
+
<span className="rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-700">
|
|
542
|
+
{getTriggerLabel(workflow.triggerType)}
|
|
543
|
+
</span>
|
|
544
|
+
{workflow.triggerType === 'STATUS_CHANGED' && (
|
|
545
|
+
<span className="text-xs text-gray-500">
|
|
546
|
+
{statuses.find((s) => s.id === workflow.triggerFromStatusId)
|
|
547
|
+
?.name ?? 'Tous'}{' '}
|
|
548
|
+
→{' '}
|
|
549
|
+
{statuses.find((s) => s.id === workflow.triggerToStatusId)?.name ??
|
|
550
|
+
'Tous'}
|
|
551
|
+
</span>
|
|
293
552
|
)}
|
|
294
|
-
>
|
|
553
|
+
</div>
|
|
554
|
+
</td>
|
|
555
|
+
<td className="px-6 py-4 whitespace-nowrap text-gray-700">
|
|
556
|
+
<span className="inline-flex items-center rounded-full bg-blue-50 px-2.5 py-0.5 text-xs font-medium text-blue-700">
|
|
557
|
+
{workflow.actions.length} action(s)
|
|
558
|
+
</span>
|
|
559
|
+
</td>
|
|
560
|
+
<td className="px-6 py-4 text-xs whitespace-nowrap text-gray-500">
|
|
561
|
+
{new Date(workflow.createdAt).toLocaleDateString()}
|
|
562
|
+
</td>
|
|
563
|
+
<td
|
|
564
|
+
className="px-6 py-4 text-center whitespace-nowrap"
|
|
565
|
+
onClick={(e) => {
|
|
566
|
+
// Empêche la navigation vers la page de détail
|
|
567
|
+
e.stopPropagation();
|
|
568
|
+
}}
|
|
569
|
+
>
|
|
570
|
+
<div className="flex items-center justify-center gap-2">
|
|
571
|
+
<button
|
|
572
|
+
type="button"
|
|
573
|
+
aria-label={
|
|
574
|
+
workflow.active ? 'Désactiver le workflow' : 'Activer le workflow'
|
|
575
|
+
}
|
|
576
|
+
onClick={() => handleToggleActive(workflow)}
|
|
577
|
+
className={cn(
|
|
578
|
+
'relative inline-flex h-5 w-9 cursor-pointer items-center rounded-full border transition-colors',
|
|
579
|
+
workflow.active
|
|
580
|
+
? 'border-green-500 bg-green-500'
|
|
581
|
+
: 'border-gray-300 bg-gray-200',
|
|
582
|
+
)}
|
|
583
|
+
>
|
|
584
|
+
<span
|
|
585
|
+
className={cn(
|
|
586
|
+
'inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform',
|
|
587
|
+
workflow.active ? 'translate-x-4' : 'translate-x-1',
|
|
588
|
+
)}
|
|
589
|
+
/>
|
|
590
|
+
</button>
|
|
295
591
|
<span
|
|
296
592
|
className={cn(
|
|
297
|
-
'
|
|
298
|
-
workflow.active ? '
|
|
593
|
+
'text-xs font-medium',
|
|
594
|
+
workflow.active ? 'text-green-600' : 'text-gray-500',
|
|
299
595
|
)}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
596
|
+
>
|
|
597
|
+
{workflow.active ? 'Actif' : 'Inactif'}
|
|
598
|
+
</span>
|
|
599
|
+
</div>
|
|
600
|
+
</td>
|
|
601
|
+
<td
|
|
602
|
+
className="px-4 py-4 text-right whitespace-nowrap"
|
|
603
|
+
onClick={(e) => e.stopPropagation()}
|
|
604
|
+
>
|
|
605
|
+
<div className="flex items-center justify-end gap-1">
|
|
606
|
+
{canDelete && (
|
|
607
|
+
<button
|
|
608
|
+
type="button"
|
|
609
|
+
onClick={() => handleDelete(workflow.id)}
|
|
610
|
+
className="inline-flex cursor-pointer items-center rounded-lg px-2 py-1 text-xs text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600"
|
|
611
|
+
>
|
|
612
|
+
<Trash2 className="mr-1 h-4 w-4" />
|
|
613
|
+
Supprimer
|
|
614
|
+
</button>
|
|
306
615
|
)}
|
|
307
|
-
>
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
onClick={(e) => e.stopPropagation()}
|
|
315
|
-
>
|
|
316
|
-
<div className="flex items-center justify-end gap-1">
|
|
317
|
-
<button
|
|
318
|
-
type="button"
|
|
319
|
-
onClick={() => handleDelete(workflow.id)}
|
|
320
|
-
className="inline-flex cursor-pointer items-center rounded-lg px-2 py-1 text-xs text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600"
|
|
321
|
-
>
|
|
322
|
-
<Trash2 className="mr-1 h-4 w-4" />
|
|
323
|
-
Supprimer
|
|
324
|
-
</button>
|
|
325
|
-
</div>
|
|
326
|
-
</td>
|
|
327
|
-
</tr>
|
|
328
|
-
))}
|
|
329
|
-
</tbody>
|
|
330
|
-
</table>
|
|
616
|
+
</div>
|
|
617
|
+
</td>
|
|
618
|
+
</tr>
|
|
619
|
+
))}
|
|
620
|
+
</tbody>
|
|
621
|
+
</table>
|
|
622
|
+
</div>
|
|
331
623
|
</div>
|
|
332
|
-
|
|
333
|
-
|
|
624
|
+
)}
|
|
625
|
+
</div>
|
|
334
626
|
</div>
|
|
335
|
-
|
|
627
|
+
<ConfirmDialog />
|
|
628
|
+
</ProtectedPage>
|
|
336
629
|
);
|
|
337
630
|
}
|