create-crm-tmp 2.0.0 → 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/README.md +230 -115
- package/template/eslint.config.mjs +13 -0
- package/template/next.config.ts +14 -0
- package/template/package.json +15 -2
- package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
- package/template/prisma/migrations/migration_lock.toml +3 -0
- package/template/prisma/schema.prisma +132 -637
- package/template/src/app/(auth)/invite/[token]/page.tsx +10 -8
- package/template/src/app/(auth)/layout.tsx +1 -1
- package/template/src/app/(auth)/reset-password/complete/page.tsx +11 -8
- package/template/src/app/(auth)/reset-password/page.tsx +4 -4
- package/template/src/app/(auth)/reset-password/verify/page.tsx +4 -4
- package/template/src/app/(auth)/signin/page.tsx +14 -6
- package/template/src/app/(dashboard)/agenda/page.tsx +2243 -988
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +18 -104
- package/template/src/app/(dashboard)/automatisation/page.tsx +10 -26
- package/template/src/app/(dashboard)/closing/page.tsx +78 -62
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +2082 -1080
- package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +46 -47
- package/template/src/app/(dashboard)/contacts/page.tsx +1062 -780
- package/template/src/app/(dashboard)/dashboard/page.tsx +533 -37
- package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
- package/template/src/app/(dashboard)/layout.tsx +6 -2
- package/template/src/app/(dashboard)/settings/page.tsx +797 -2582
- package/template/src/app/(dashboard)/templates/page.tsx +55 -54
- package/template/src/app/(dashboard)/users/list/page.tsx +51 -48
- package/template/src/app/(dashboard)/users/page.tsx +1 -1
- package/template/src/app/(dashboard)/users/permissions/page.tsx +2 -2
- package/template/src/app/(dashboard)/users/roles/page.tsx +7 -5
- package/template/src/app/api/agenda/google-events/route.ts +92 -0
- package/template/src/app/api/auth/check-active/route.ts +3 -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 +1 -3
- package/template/src/app/api/companies/[id]/route.ts +1 -2
- package/template/src/app/api/companies/route.ts +42 -12
- package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +9 -31
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +14 -32
- package/template/src/app/api/contacts/[id]/files/route.ts +112 -212
- package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
- package/template/src/app/api/contacts/[id]/interactions/route.ts +16 -16
- package/template/src/app/api/contacts/[id]/kyc/route.ts +21 -11
- package/template/src/app/api/contacts/[id]/meet/route.ts +19 -2
- package/template/src/app/api/contacts/[id]/route.ts +106 -34
- package/template/src/app/api/contacts/[id]/send-email/route.ts +27 -11
- package/template/src/app/api/contacts/[id]/workflows/run/route.ts +6 -0
- package/template/src/app/api/contacts/export/route.ts +9 -13
- package/template/src/app/api/contacts/import/route.ts +55 -25
- package/template/src/app/api/contacts/import-preview/route.ts +1 -1
- package/template/src/app/api/contacts/origins/route.ts +63 -0
- package/template/src/app/api/contacts/route.ts +153 -41
- package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
- package/template/src/app/api/dashboard/widgets/[id]/route.ts +44 -0
- package/template/src/app/api/dashboard/widgets/route.ts +181 -0
- 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 +24 -556
- 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 +164 -39
- package/template/src/app/api/reminders/state/route.ts +164 -0
- 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 +16 -4
- package/template/src/app/api/settings/google-ads/route.ts +14 -0
- 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 +28 -0
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +37 -4
- package/template/src/app/api/settings/google-sheet/preview/route.ts +9 -3
- package/template/src/app/api/settings/google-sheet/route.ts +14 -0
- 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 +0 -1
- package/template/src/app/api/settings/meta-leads/route.ts +14 -2
- package/template/src/app/api/settings/smtp/route.ts +53 -6
- package/template/src/app/api/tasks/[id]/attendees/route.ts +24 -8
- package/template/src/app/api/tasks/[id]/route.ts +234 -58
- package/template/src/app/api/tasks/meet/route.ts +27 -19
- package/template/src/app/api/tasks/route.ts +62 -17
- package/template/src/app/api/users/[id]/route.ts +20 -14
- package/template/src/app/api/users/list/route.ts +57 -19
- package/template/src/app/api/webhooks/google-ads/route.ts +34 -14
- package/template/src/app/api/webhooks/meta-leads/route.ts +32 -12
- package/template/src/app/api/workflows/[id]/route.ts +0 -4
- package/template/src/app/api/workflows/process/route.ts +22 -51
- package/template/src/app/api/workflows/route.ts +0 -4
- package/template/src/app/globals.css +342 -4
- package/template/src/app/layout.tsx +11 -3
- package/template/src/app/page.tsx +1 -1
- package/template/src/components/address-autocomplete.tsx +7 -6
- package/template/src/components/config-error-alert.tsx +46 -0
- package/template/src/components/contacts/filter-bar.tsx +12 -3
- package/template/src/components/contacts/filter-builder.tsx +28 -43
- package/template/src/components/contacts/save-view-dialog.tsx +1 -1
- package/template/src/components/contacts/views-tab-bar.tsx +15 -6
- package/template/src/components/dashboard/activity-chart.tsx +41 -28
- package/template/src/components/dashboard/add-widget-dialog.tsx +157 -0
- package/template/src/components/dashboard/color-picker.tsx +64 -0
- package/template/src/components/dashboard/contacts-chart.tsx +69 -0
- package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
- package/template/src/components/dashboard/recent-activity.tsx +154 -0
- package/template/src/components/dashboard/stat-card.tsx +40 -40
- package/template/src/components/dashboard/status-distribution-chart.tsx +81 -0
- package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
- package/template/src/components/dashboard/top-contacts-list.tsx +113 -0
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +72 -81
- package/template/src/components/dashboard/widget-wrapper.tsx +36 -0
- package/template/src/components/date-picker.tsx +9 -6
- package/template/src/components/editor/upload-editor-image.ts +42 -0
- package/template/src/components/editor.tsx +161 -22
- package/template/src/components/email-template.tsx +2 -2
- package/template/src/components/global-search.tsx +30 -28
- package/template/src/components/header.tsx +178 -80
- 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 +2 -2
- package/template/src/components/meet-cancellation-email-template.tsx +3 -3
- package/template/src/components/meet-confirmation-email-template.tsx +3 -3
- package/template/src/components/meet-update-email-template.tsx +3 -3
- package/template/src/components/page-header.tsx +5 -5
- package/template/src/components/protected-page.tsx +1 -1
- package/template/src/components/reset-password-email-template.tsx +2 -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 +45 -26
- package/template/src/components/skeleton.tsx +40 -43
- package/template/src/components/ui/accordion.tsx +2 -2
- package/template/src/components/ui/alert-dialog.tsx +1 -1
- package/template/src/components/ui/button.tsx +20 -9
- 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-modal.tsx +13 -7
- package/template/src/contexts/app-toast-context.tsx +245 -57
- package/template/src/contexts/dashboard-theme-context.tsx +53 -0
- package/template/src/contexts/sidebar-context.tsx +22 -17
- package/template/src/contexts/task-reminder-context.tsx +134 -160
- package/template/src/contexts/view-as-context.tsx +33 -6
- package/template/src/hooks/use-focus-trap.ts +2 -2
- package/template/src/hooks/useIntegrationNotifications.ts +49 -0
- package/template/src/lib/auth.ts +8 -1
- 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 +21 -21
- package/template/src/lib/contact-view-filters.ts +24 -64
- package/template/src/lib/contacts-list-url.ts +190 -0
- package/template/src/lib/dashboard-stats.ts +65 -7
- package/template/src/lib/dashboard-themes.ts +135 -0
- package/template/src/lib/date-utils.ts +127 -0
- package/template/src/lib/default-widgets.ts +12 -0
- 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/google-calendar-agenda.ts +201 -0
- package/template/src/lib/google-calendar.ts +255 -5
- 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/permissions.ts +40 -10
- package/template/src/lib/prisma.ts +4 -1
- 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/supabase-storage.ts +113 -0
- package/template/src/lib/template-variables.ts +164 -23
- package/template/src/lib/utils.ts +45 -0
- package/template/src/lib/widget-registry.ts +173 -0
- package/template/src/lib/workflow-executor.ts +16 -70
- package/template/src/proxy.ts +1 -0
- package/template/vercel.json +3 -10
- package/template/skills-lock.json +0 -25
- package/template/src/components/dashboard/dashboard-content.tsx +0 -79
- package/template/src/lib/google-drive.ts +0 -1101
- package/template/src/types/yousign.ts +0 -52
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
4
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
5
|
+
import { useConfirm } from '@/hooks/use-confirm';
|
|
6
|
+
import { StatusSelect } from '@/components/ui/status-select';
|
|
7
|
+
import { devToast } from '@/lib/utils';
|
|
8
|
+
|
|
9
|
+
interface MetaLeadConfig {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
pageId: string;
|
|
13
|
+
verifyToken: string;
|
|
14
|
+
active: boolean;
|
|
15
|
+
defaultStatusId: string | null;
|
|
16
|
+
defaultAssignedUserId: string | null;
|
|
17
|
+
defaultStatus: { id: string; name: string; color: string } | null;
|
|
18
|
+
defaultAssignedUser: { id: string; name: string; email: string } | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface MetaLeadFormData {
|
|
22
|
+
name: string;
|
|
23
|
+
active: boolean;
|
|
24
|
+
pageId: string;
|
|
25
|
+
accessToken: string;
|
|
26
|
+
verifyToken: string;
|
|
27
|
+
defaultStatusId: string | null;
|
|
28
|
+
defaultAssignedUserId: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const EMPTY_FORM: MetaLeadFormData = {
|
|
32
|
+
name: '',
|
|
33
|
+
active: true,
|
|
34
|
+
pageId: '',
|
|
35
|
+
accessToken: '',
|
|
36
|
+
verifyToken: '',
|
|
37
|
+
defaultStatusId: null,
|
|
38
|
+
defaultAssignedUserId: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface MetaLeadIntegrationProps {
|
|
42
|
+
readonly statuses: ReadonlyArray<{ id: string; name: string; color: string }>;
|
|
43
|
+
readonly users: ReadonlyArray<{ id: string; name: string; email: string }>;
|
|
44
|
+
readonly onOpenLogs: (configId: string, configName: string) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function MetaLeadIntegration({ statuses, users, onOpenLogs }: MetaLeadIntegrationProps) {
|
|
48
|
+
const toast = useAppToast();
|
|
49
|
+
const { confirm, ConfirmDialog } = useConfirm();
|
|
50
|
+
|
|
51
|
+
const [loading, setLoading] = useState(true);
|
|
52
|
+
const [saving, setSaving] = useState(false);
|
|
53
|
+
const [configs, setConfigs] = useState<MetaLeadConfig[]>([]);
|
|
54
|
+
const [showModal, setShowModal] = useState(false);
|
|
55
|
+
const [editingConfig, setEditingConfig] = useState<string | null>(null);
|
|
56
|
+
const [formData, setFormData] = useState<MetaLeadFormData>(EMPTY_FORM);
|
|
57
|
+
|
|
58
|
+
const loadConfigs = useCallback(async () => {
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch('/api/settings/meta-leads');
|
|
61
|
+
if (res.ok) {
|
|
62
|
+
const data = await res.json();
|
|
63
|
+
setConfigs(Array.isArray(data) ? data : []);
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
toast.error('Impossible de charger les configurations Meta. Veuillez rafraîchir la page.');
|
|
67
|
+
} finally {
|
|
68
|
+
setLoading(false);
|
|
69
|
+
}
|
|
70
|
+
}, [toast]);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
loadConfigs();
|
|
74
|
+
}, [loadConfigs]);
|
|
75
|
+
|
|
76
|
+
const resetForm = () => {
|
|
77
|
+
setShowModal(false);
|
|
78
|
+
setEditingConfig(null);
|
|
79
|
+
setFormData(EMPTY_FORM);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleOpenAdd = () => {
|
|
83
|
+
setEditingConfig(null);
|
|
84
|
+
setFormData(EMPTY_FORM);
|
|
85
|
+
setShowModal(true);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const handleEdit = (config: MetaLeadConfig) => {
|
|
89
|
+
setEditingConfig(config.id);
|
|
90
|
+
setFormData({
|
|
91
|
+
name: config.name,
|
|
92
|
+
active: config.active,
|
|
93
|
+
pageId: config.pageId,
|
|
94
|
+
accessToken: '',
|
|
95
|
+
verifyToken: config.verifyToken,
|
|
96
|
+
defaultStatusId: config.defaultStatusId,
|
|
97
|
+
defaultAssignedUserId: config.defaultAssignedUserId,
|
|
98
|
+
});
|
|
99
|
+
setShowModal(true);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
setSaving(true);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const url = editingConfig
|
|
108
|
+
? `/api/settings/meta-leads/${editingConfig}`
|
|
109
|
+
: '/api/settings/meta-leads';
|
|
110
|
+
const method = editingConfig ? 'PUT' : 'POST';
|
|
111
|
+
|
|
112
|
+
const response = await fetch(url, {
|
|
113
|
+
method,
|
|
114
|
+
headers: { 'Content-Type': 'application/json' },
|
|
115
|
+
body: JSON.stringify(formData),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const data = await response.json();
|
|
119
|
+
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(data.error || 'Erreur lors de la sauvegarde de la configuration Meta');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
toast.success(
|
|
125
|
+
editingConfig
|
|
126
|
+
? 'Configuration Meta Lead Ads mise à jour avec succès'
|
|
127
|
+
: 'Configuration Meta Lead Ads créée avec succès',
|
|
128
|
+
);
|
|
129
|
+
resetForm();
|
|
130
|
+
await loadConfigs();
|
|
131
|
+
} catch (error: any) {
|
|
132
|
+
toast.error(devToast('Impossible de sauvegarder la configuration Meta. Veuillez réessayer.', error));
|
|
133
|
+
} finally {
|
|
134
|
+
setSaving(false);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const handleDelete = async (id: string) => {
|
|
139
|
+
const confirmed = await confirm({
|
|
140
|
+
title: 'Supprimer la configuration Meta Lead',
|
|
141
|
+
description: 'Êtes-vous sûr de vouloir supprimer cette configuration ?',
|
|
142
|
+
confirmText: 'Supprimer',
|
|
143
|
+
cancelText: 'Annuler',
|
|
144
|
+
variant: 'destructive',
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!confirmed) return;
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const response = await fetch(`/api/settings/meta-leads/${id}`, {
|
|
151
|
+
method: 'DELETE',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
const data = await response.json();
|
|
156
|
+
throw new Error(data.error || 'Erreur lors de la suppression');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
toast.success('Configuration supprimée avec succès');
|
|
160
|
+
await loadConfigs();
|
|
161
|
+
} catch (error: any) {
|
|
162
|
+
toast.error(devToast('Impossible de supprimer la configuration. Veuillez réessayer.', error));
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const getWebhookUrl = () => {
|
|
167
|
+
const origin = globalThis.window?.location.origin ?? '';
|
|
168
|
+
return `${origin}/api/webhooks/meta-leads`;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const handleCopyWebhook = () => {
|
|
172
|
+
const url = getWebhookUrl();
|
|
173
|
+
navigator.clipboard?.writeText(url).then(() => toast.success('Lien webhook copié'));
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<>
|
|
178
|
+
<div className="rounded-lg bg-white p-6 shadow-sm">
|
|
179
|
+
<div className="flex items-center justify-between">
|
|
180
|
+
<div>
|
|
181
|
+
<h2 className="text-lg font-bold text-gray-900">Intégration Meta Lead Ads</h2>
|
|
182
|
+
<p className="mt-1 text-sm text-gray-600">
|
|
183
|
+
Recevez automatiquement les leads depuis Facebook Lead Ads.
|
|
184
|
+
</p>
|
|
185
|
+
</div>
|
|
186
|
+
<button
|
|
187
|
+
type="button"
|
|
188
|
+
onClick={handleOpenAdd}
|
|
189
|
+
className="cursor-pointer rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
|
|
190
|
+
>
|
|
191
|
+
+ Ajouter
|
|
192
|
+
</button>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
{loading && (
|
|
196
|
+
<div className="mt-6 text-center text-gray-500">Chargement...</div>
|
|
197
|
+
)}
|
|
198
|
+
|
|
199
|
+
{!loading && configs.length === 0 && (
|
|
200
|
+
<div className="mt-6 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8 text-center">
|
|
201
|
+
<p className="text-sm text-gray-600">Aucune configuration Meta Lead Ads</p>
|
|
202
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
203
|
+
Cliquez sur "+ Ajouter" pour créer votre première configuration
|
|
204
|
+
</p>
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
|
|
208
|
+
{!loading && configs.length > 0 && (
|
|
209
|
+
<div className="mt-6 space-y-3">
|
|
210
|
+
{configs.map((config) => (
|
|
211
|
+
<div
|
|
212
|
+
key={config.id}
|
|
213
|
+
className="flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 p-4"
|
|
214
|
+
>
|
|
215
|
+
<div className="flex-1">
|
|
216
|
+
<div className="flex items-center gap-2">
|
|
217
|
+
<h3 className="font-medium text-gray-900">{config.name}</h3>
|
|
218
|
+
{config.active ? (
|
|
219
|
+
<span className="rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
|
|
220
|
+
Actif
|
|
221
|
+
</span>
|
|
222
|
+
) : (
|
|
223
|
+
<span className="rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-800">
|
|
224
|
+
Inactif
|
|
225
|
+
</span>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
<p className="mt-1 text-xs text-gray-500">Page ID: {config.pageId}</p>
|
|
229
|
+
<p className="mt-0.5 text-xs text-gray-400">Webhook : {getWebhookUrl()}</p>
|
|
230
|
+
</div>
|
|
231
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
onClick={() => onOpenLogs(config.id, config.name)}
|
|
235
|
+
className="cursor-pointer rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-100"
|
|
236
|
+
>
|
|
237
|
+
Voir les logs
|
|
238
|
+
</button>
|
|
239
|
+
<button
|
|
240
|
+
type="button"
|
|
241
|
+
onClick={handleCopyWebhook}
|
|
242
|
+
className="cursor-pointer rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-100"
|
|
243
|
+
>
|
|
244
|
+
Copier le lien webhook
|
|
245
|
+
</button>
|
|
246
|
+
<button
|
|
247
|
+
type="button"
|
|
248
|
+
onClick={() => handleEdit(config)}
|
|
249
|
+
className="cursor-pointer rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-100"
|
|
250
|
+
>
|
|
251
|
+
Modifier
|
|
252
|
+
</button>
|
|
253
|
+
<button
|
|
254
|
+
type="button"
|
|
255
|
+
onClick={() => handleDelete(config.id)}
|
|
256
|
+
className="cursor-pointer rounded-lg border border-blue-300 px-3 py-1.5 text-xs font-medium text-blue-700 transition-colors hover:bg-blue-50"
|
|
257
|
+
>
|
|
258
|
+
Supprimer
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
))}
|
|
263
|
+
</div>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{showModal && (
|
|
268
|
+
<div className="ui-fade-in fixed inset-0 z-50 flex min-h-dvh items-center justify-center bg-gray-500/20 p-4 backdrop-blur-sm sm:p-6">
|
|
269
|
+
<div className="ui-scale-in flex max-h-[90vh] w-full max-w-2xl flex-col rounded-lg bg-white p-6 shadow-xl sm:p-8">
|
|
270
|
+
<div className="shrink-0 border-b border-gray-100 pb-4">
|
|
271
|
+
<div className="flex items-center justify-between">
|
|
272
|
+
<h2 className="text-xl font-bold text-gray-900 sm:text-2xl">
|
|
273
|
+
{editingConfig ? 'Modifier' : 'Ajouter'} une configuration Meta Lead Ads
|
|
274
|
+
</h2>
|
|
275
|
+
<button
|
|
276
|
+
type="button"
|
|
277
|
+
onClick={resetForm}
|
|
278
|
+
className="cursor-pointer rounded-lg p-2 text-gray-400 transition-colors hover:bg-gray-100"
|
|
279
|
+
>
|
|
280
|
+
<svg
|
|
281
|
+
className="h-6 w-6"
|
|
282
|
+
fill="none"
|
|
283
|
+
stroke="currentColor"
|
|
284
|
+
viewBox="0 0 24 24"
|
|
285
|
+
>
|
|
286
|
+
<path
|
|
287
|
+
strokeLinecap="round"
|
|
288
|
+
strokeLinejoin="round"
|
|
289
|
+
strokeWidth={2}
|
|
290
|
+
d="M6 18L18 6M6 6l12 12"
|
|
291
|
+
/>
|
|
292
|
+
</svg>
|
|
293
|
+
</button>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
<form
|
|
298
|
+
id="meta-lead-form"
|
|
299
|
+
onSubmit={handleSubmit}
|
|
300
|
+
className="flex-1 space-y-4 overflow-y-auto pt-4"
|
|
301
|
+
>
|
|
302
|
+
<div>
|
|
303
|
+
<label htmlFor="meta-lead-name" className="block text-sm font-medium text-gray-700">
|
|
304
|
+
Nom de la configuration *
|
|
305
|
+
</label>
|
|
306
|
+
<input
|
|
307
|
+
id="meta-lead-name"
|
|
308
|
+
type="text"
|
|
309
|
+
required
|
|
310
|
+
value={formData.name}
|
|
311
|
+
onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
|
|
312
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
313
|
+
placeholder="Ex: Facebook Lead Ads"
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div className="flex items-center">
|
|
318
|
+
<input
|
|
319
|
+
id="meta-lead-active"
|
|
320
|
+
type="checkbox"
|
|
321
|
+
checked={formData.active}
|
|
322
|
+
onChange={(e) =>
|
|
323
|
+
setFormData((prev) => ({ ...prev, active: e.target.checked }))
|
|
324
|
+
}
|
|
325
|
+
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-gray-400/30"
|
|
326
|
+
/>
|
|
327
|
+
<label
|
|
328
|
+
htmlFor="meta-lead-active"
|
|
329
|
+
className="ml-2 text-sm font-medium text-gray-700"
|
|
330
|
+
>
|
|
331
|
+
Activer l'intégration Meta Lead Ads
|
|
332
|
+
</label>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<div>
|
|
336
|
+
<label htmlFor="meta-lead-page-id" className="block text-sm font-medium text-gray-700">Page ID *</label>
|
|
337
|
+
<input
|
|
338
|
+
id="meta-lead-page-id"
|
|
339
|
+
type="text"
|
|
340
|
+
required
|
|
341
|
+
value={formData.pageId}
|
|
342
|
+
onChange={(e) => setFormData((prev) => ({ ...prev, pageId: e.target.value }))}
|
|
343
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
344
|
+
placeholder="Page ID Facebook"
|
|
345
|
+
/>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<div>
|
|
349
|
+
<label htmlFor="meta-lead-access-token" className="block text-sm font-medium text-gray-700">Access Token *</label>
|
|
350
|
+
<input
|
|
351
|
+
id="meta-lead-access-token"
|
|
352
|
+
type="password"
|
|
353
|
+
autoComplete="off"
|
|
354
|
+
required
|
|
355
|
+
value={formData.accessToken}
|
|
356
|
+
onChange={(e) =>
|
|
357
|
+
setFormData((prev) => ({ ...prev, accessToken: e.target.value }))
|
|
358
|
+
}
|
|
359
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
360
|
+
placeholder="Access Token"
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div>
|
|
365
|
+
<label htmlFor="meta-lead-verify-token" className="block text-sm font-medium text-gray-700">Verify Token *</label>
|
|
366
|
+
<input
|
|
367
|
+
id="meta-lead-verify-token"
|
|
368
|
+
type="text"
|
|
369
|
+
required
|
|
370
|
+
value={formData.verifyToken}
|
|
371
|
+
onChange={(e) =>
|
|
372
|
+
setFormData((prev) => ({ ...prev, verifyToken: e.target.value }))
|
|
373
|
+
}
|
|
374
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
375
|
+
placeholder="Verify Token"
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
380
|
+
<div>
|
|
381
|
+
<label htmlFor="meta-lead-assigned-user" className="block text-sm font-medium text-gray-700">
|
|
382
|
+
Utilisateur assigné par défaut (optionnel)
|
|
383
|
+
</label>
|
|
384
|
+
<select
|
|
385
|
+
id="meta-lead-assigned-user"
|
|
386
|
+
value={formData.defaultAssignedUserId || ''}
|
|
387
|
+
onChange={(e) =>
|
|
388
|
+
setFormData((prev) => ({
|
|
389
|
+
...prev,
|
|
390
|
+
defaultAssignedUserId: e.target.value || null,
|
|
391
|
+
}))
|
|
392
|
+
}
|
|
393
|
+
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
394
|
+
>
|
|
395
|
+
<option value="">Aucun utilisateur par défaut</option>
|
|
396
|
+
{users.map((user) => (
|
|
397
|
+
<option key={user.id} value={user.id}>
|
|
398
|
+
{user.name} ({user.email})
|
|
399
|
+
</option>
|
|
400
|
+
))}
|
|
401
|
+
</select>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div>
|
|
405
|
+
<label htmlFor="meta-lead-default-status" className="block text-sm font-medium text-gray-700">
|
|
406
|
+
Statut par défaut
|
|
407
|
+
</label>
|
|
408
|
+
<StatusSelect
|
|
409
|
+
id="meta-lead-default-status"
|
|
410
|
+
statuses={[...statuses]}
|
|
411
|
+
value={formData.defaultStatusId || ''}
|
|
412
|
+
onChange={(v) =>
|
|
413
|
+
setFormData((prev) => ({
|
|
414
|
+
...prev,
|
|
415
|
+
defaultStatusId: v || null,
|
|
416
|
+
}))
|
|
417
|
+
}
|
|
418
|
+
placeholder="Aucun statut par défaut"
|
|
419
|
+
className="mt-1"
|
|
420
|
+
/>
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
</form>
|
|
424
|
+
|
|
425
|
+
<div className="shrink-0 border-t border-gray-100 pt-4">
|
|
426
|
+
<div className="flex flex-col gap-3 sm:flex-row sm:justify-end">
|
|
427
|
+
<button
|
|
428
|
+
type="button"
|
|
429
|
+
onClick={resetForm}
|
|
430
|
+
className="w-full cursor-pointer rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 sm:w-auto"
|
|
431
|
+
>
|
|
432
|
+
Annuler
|
|
433
|
+
</button>
|
|
434
|
+
<button
|
|
435
|
+
type="submit"
|
|
436
|
+
form="meta-lead-form"
|
|
437
|
+
disabled={saving}
|
|
438
|
+
className="w-full cursor-pointer rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto"
|
|
439
|
+
>
|
|
440
|
+
{saving ? 'Enregistrement...' : 'Enregistrer'}
|
|
441
|
+
</button>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
447
|
+
|
|
448
|
+
<ConfirmDialog />
|
|
449
|
+
</>
|
|
450
|
+
);
|
|
451
|
+
}
|
|
@@ -9,7 +9,7 @@ import { useMobileMenuContext } from '@/contexts/mobile-menu-context';
|
|
|
9
9
|
import { useSidebarContext } from '@/contexts/sidebar-context';
|
|
10
10
|
import { useViewAs } from '@/contexts/view-as-context';
|
|
11
11
|
import { ViewAsModal } from '@/components/view-as-modal';
|
|
12
|
-
import { Eye, X, PanelLeftClose, PanelLeftOpen } from 'lucide-react';
|
|
12
|
+
import { Eye, X, PanelLeftClose, PanelLeftOpen, FlaskConical } from 'lucide-react';
|
|
13
13
|
import { cn } from '@/lib/utils';
|
|
14
14
|
import { NAV_PAGES } from '@/config/nav-pages';
|
|
15
15
|
|
|
@@ -56,7 +56,7 @@ export function Sidebar() {
|
|
|
56
56
|
{/* Overlay for mobile */}
|
|
57
57
|
{isMobileMenuOpen && (
|
|
58
58
|
<div
|
|
59
|
-
className="fixed inset-0 z-40
|
|
59
|
+
className="bg-foreground/10 ui-fade-in fixed inset-0 z-40 backdrop-blur-sm lg:hidden"
|
|
60
60
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
61
61
|
/>
|
|
62
62
|
)}
|
|
@@ -64,12 +64,16 @@ export function Sidebar() {
|
|
|
64
64
|
{/* Sidebar */}
|
|
65
65
|
<div
|
|
66
66
|
className={cn(
|
|
67
|
-
'group fixed top-0 left-0 z-40 flex h-screen flex-col border-r
|
|
67
|
+
'group border-sidebar-border bg-sidebar text-sidebar-foreground fixed top-0 left-0 z-40 flex h-screen flex-col border-r shadow-(--shadow-card) transition-[width,transform] duration-300 ease-(--ease-standard) lg:relative lg:translate-x-0',
|
|
68
68
|
isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0',
|
|
69
69
|
!isSidebarExpanded ? 'w-64 lg:w-16' : 'w-64 lg:w-64',
|
|
70
70
|
)}
|
|
71
71
|
onMouseEnter={() => {
|
|
72
|
-
if (
|
|
72
|
+
if (
|
|
73
|
+
typeof globalThis.window !== 'undefined' &&
|
|
74
|
+
globalThis.window.innerWidth >= 1024 &&
|
|
75
|
+
!isPinned
|
|
76
|
+
) {
|
|
73
77
|
setExpandedByHover(true);
|
|
74
78
|
}
|
|
75
79
|
}}
|
|
@@ -86,7 +90,7 @@ export function Sidebar() {
|
|
|
86
90
|
<div className="flex items-center justify-end">
|
|
87
91
|
<button
|
|
88
92
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
89
|
-
className="
|
|
93
|
+
className="text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground cursor-pointer rounded-lg p-2 transition-colors duration-200 lg:hidden"
|
|
90
94
|
aria-label="Close menu"
|
|
91
95
|
>
|
|
92
96
|
<X className="h-5 w-5" />
|
|
@@ -103,21 +107,36 @@ export function Sidebar() {
|
|
|
103
107
|
href={item.href}
|
|
104
108
|
onClick={handleLinkClick}
|
|
105
109
|
className={cn(
|
|
106
|
-
'flex items-center gap-3 rounded-lg py-2 text-sm font-medium transition-
|
|
110
|
+
'flex items-center gap-3 rounded-lg py-2 text-sm font-medium transition-[color,background-color,box-shadow] duration-(--duration-normal) ease-(--ease-standard)',
|
|
107
111
|
!isSidebarExpanded ? 'px-3 lg:justify-center lg:px-2' : 'px-3',
|
|
108
112
|
isActive
|
|
109
113
|
? 'bg-sidebar-primary text-sidebar-primary-foreground shadow-sm'
|
|
110
|
-
: 'text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
|
114
|
+
: 'text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:scale-[0.97]',
|
|
111
115
|
)}
|
|
112
116
|
title={!isSidebarExpanded ? displayName : undefined}
|
|
113
117
|
>
|
|
114
|
-
<Icon className="h-5 w-5 shrink-0" />
|
|
115
|
-
{isSidebarExpanded &&
|
|
116
|
-
<span className="whitespace-nowrap">{displayName}</span>
|
|
117
|
-
)}
|
|
118
|
+
<Icon aria-hidden="true" className="h-5 w-5 shrink-0" />
|
|
119
|
+
{isSidebarExpanded && <span className="whitespace-nowrap">{displayName}</span>}
|
|
118
120
|
</Link>
|
|
119
121
|
);
|
|
120
122
|
})}
|
|
123
|
+
{process.env.NODE_ENV === 'development' && (
|
|
124
|
+
<Link
|
|
125
|
+
href="/dev"
|
|
126
|
+
onClick={handleLinkClick}
|
|
127
|
+
className={cn(
|
|
128
|
+
'flex items-center gap-3 rounded-lg py-2 text-sm font-medium transition-[color,background-color,box-shadow] duration-(--duration-normal) ease-(--ease-standard)',
|
|
129
|
+
!isSidebarExpanded ? 'px-3 lg:justify-center lg:px-2' : 'px-3',
|
|
130
|
+
pathname === '/dev'
|
|
131
|
+
? 'bg-sidebar-primary text-sidebar-primary-foreground shadow-sm'
|
|
132
|
+
: 'text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:scale-[0.97]',
|
|
133
|
+
)}
|
|
134
|
+
title={!isSidebarExpanded ? 'Dev' : undefined}
|
|
135
|
+
>
|
|
136
|
+
<FlaskConical aria-hidden="true" className="h-5 w-5 shrink-0" />
|
|
137
|
+
{isSidebarExpanded && <span className="whitespace-nowrap">Dev</span>}
|
|
138
|
+
</Link>
|
|
139
|
+
)}
|
|
121
140
|
</div>
|
|
122
141
|
</div>
|
|
123
142
|
</nav>
|
|
@@ -126,7 +145,7 @@ export function Sidebar() {
|
|
|
126
145
|
{isRealAdmin && (
|
|
127
146
|
<div
|
|
128
147
|
className={cn(
|
|
129
|
-
'border-
|
|
148
|
+
'border-sidebar-border border-t transition-[padding] duration-300',
|
|
130
149
|
!isSidebarExpanded ? 'p-3 lg:p-2' : 'p-3',
|
|
131
150
|
)}
|
|
132
151
|
>
|
|
@@ -134,7 +153,7 @@ export function Sidebar() {
|
|
|
134
153
|
<button
|
|
135
154
|
onClick={() => setShowViewAsModal(true)}
|
|
136
155
|
className={cn(
|
|
137
|
-
'w-full cursor-pointer rounded-lg border-2 p-3 text-left transition-
|
|
156
|
+
'w-full cursor-pointer rounded-lg border-2 p-3 text-left transition-colors',
|
|
138
157
|
isViewingAsOther
|
|
139
158
|
? 'border-sidebar-primary bg-sidebar-primary text-sidebar-primary-foreground hover:opacity-95'
|
|
140
159
|
: 'border-sidebar-border bg-sidebar text-sidebar-foreground hover:border-sidebar-ring hover:bg-sidebar-accent',
|
|
@@ -175,7 +194,7 @@ export function Sidebar() {
|
|
|
175
194
|
: session?.user?.name || 'Utilisateur'}
|
|
176
195
|
</p>
|
|
177
196
|
</div>
|
|
178
|
-
<Eye className="h-5 w-5 shrink-0" />
|
|
197
|
+
<Eye aria-hidden="true" className="h-5 w-5 shrink-0" />
|
|
179
198
|
</div>
|
|
180
199
|
</button>
|
|
181
200
|
) : (
|
|
@@ -191,7 +210,7 @@ export function Sidebar() {
|
|
|
191
210
|
aria-label="Changer de vue"
|
|
192
211
|
>
|
|
193
212
|
<div className="flex items-center justify-center">
|
|
194
|
-
<Eye className="h-5 w-5" />
|
|
213
|
+
<Eye aria-hidden="true" className="h-5 w-5" />
|
|
195
214
|
</div>
|
|
196
215
|
</button>
|
|
197
216
|
)}
|
|
@@ -201,14 +220,14 @@ export function Sidebar() {
|
|
|
201
220
|
{/* Bouton Réduire / Développer la navigation (desktop uniquement) */}
|
|
202
221
|
<div
|
|
203
222
|
className={cn(
|
|
204
|
-
'hidden border-t
|
|
223
|
+
'border-sidebar-border hidden border-t lg:block',
|
|
205
224
|
!isSidebarExpanded ? 'p-3 lg:p-2' : 'p-3',
|
|
206
225
|
)}
|
|
207
226
|
>
|
|
208
227
|
<button
|
|
209
228
|
onClick={togglePin}
|
|
210
229
|
className={cn(
|
|
211
|
-
'flex w-full cursor-pointer items-center gap-3 rounded-lg py-2 text-sm font-medium
|
|
230
|
+
'text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full cursor-pointer items-center gap-3 rounded-lg py-2 text-sm font-medium transition-colors duration-200',
|
|
212
231
|
!isSidebarExpanded ? 'justify-center px-2' : 'px-3',
|
|
213
232
|
)}
|
|
214
233
|
title={isPinned ? 'Réduire la navigation' : 'Développer la navigation'}
|
|
@@ -216,12 +235,12 @@ export function Sidebar() {
|
|
|
216
235
|
>
|
|
217
236
|
{isPinned ? (
|
|
218
237
|
<>
|
|
219
|
-
<PanelLeftClose className="h-5 w-5 shrink-0" />
|
|
238
|
+
<PanelLeftClose aria-hidden="true" className="h-5 w-5 shrink-0" />
|
|
220
239
|
<span className="whitespace-nowrap">Réduire la navigation</span>
|
|
221
240
|
</>
|
|
222
241
|
) : (
|
|
223
242
|
<div className="flex gap-2">
|
|
224
|
-
<PanelLeftOpen className="h-5 w-5 shrink-0" />
|
|
243
|
+
<PanelLeftOpen aria-hidden="true" className="h-5 w-5 shrink-0" />
|
|
225
244
|
<span className="hidden whitespace-nowrap group-hover:block">
|
|
226
245
|
Développer la navigation
|
|
227
246
|
</span>
|
|
@@ -233,40 +252,40 @@ export function Sidebar() {
|
|
|
233
252
|
{/* User Profile */}
|
|
234
253
|
<div
|
|
235
254
|
className={cn(
|
|
236
|
-
'border-
|
|
255
|
+
'border-sidebar-border border-t transition-[padding] duration-300',
|
|
237
256
|
!isSidebarExpanded ? 'p-4 lg:p-2' : 'p-4',
|
|
238
257
|
)}
|
|
239
258
|
>
|
|
240
259
|
{isSidebarExpanded ? (
|
|
241
260
|
<>
|
|
242
261
|
<div className="flex items-center gap-3">
|
|
243
|
-
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full
|
|
262
|
+
<div className="bg-sidebar-accent text-sidebar-accent-foreground flex h-10 w-10 shrink-0 items-center justify-center rounded-full">
|
|
244
263
|
{!isMounted ? 'U' : session?.user?.name?.[0]?.toUpperCase() || 'U'}
|
|
245
264
|
</div>
|
|
246
265
|
<div className="min-w-0 flex-1">
|
|
247
|
-
<p className="truncate text-sm font-medium
|
|
266
|
+
<p className="text-sidebar-foreground truncate text-sm font-medium">
|
|
248
267
|
{!isMounted ? 'Utilisateur' : session?.user?.name || 'Utilisateur'}
|
|
249
268
|
</p>
|
|
250
|
-
<p className="
|
|
269
|
+
<p className="text-sidebar-foreground/70 truncate text-xs">
|
|
251
270
|
{!isMounted ? '' : session?.user?.email}
|
|
252
271
|
</p>
|
|
253
272
|
</div>
|
|
254
273
|
</div>
|
|
255
274
|
<button
|
|
256
275
|
onClick={handleSignOut}
|
|
257
|
-
className="mt-3 w-full cursor-pointer rounded-lg
|
|
276
|
+
className="bg-sidebar-accent text-sidebar-accent-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground mt-3 w-full cursor-pointer rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-200"
|
|
258
277
|
>
|
|
259
278
|
Déconnexion
|
|
260
279
|
</button>
|
|
261
280
|
</>
|
|
262
281
|
) : (
|
|
263
282
|
<div className="flex flex-col items-center gap-2">
|
|
264
|
-
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full
|
|
283
|
+
<div className="bg-sidebar-accent text-sidebar-accent-foreground flex h-10 w-10 shrink-0 items-center justify-center rounded-full">
|
|
265
284
|
{!isMounted ? 'U' : session?.user?.name?.[0]?.toUpperCase() || 'U'}
|
|
266
285
|
</div>
|
|
267
286
|
<button
|
|
268
287
|
onClick={handleSignOut}
|
|
269
|
-
className="
|
|
288
|
+
className="text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground cursor-pointer rounded-lg p-2 transition-colors duration-200"
|
|
270
289
|
title="Déconnexion"
|
|
271
290
|
aria-label="Déconnexion"
|
|
272
291
|
>
|