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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"skills": {
|
|
4
|
+
"frontend-design": {
|
|
5
|
+
"source": "anthropics/skills",
|
|
6
|
+
"sourceType": "github",
|
|
7
|
+
"computedHash": "063a0e6448123cd359ad0044cc46b0e490cc7964d45ef4bb9fd842bd2ffbca67"
|
|
8
|
+
},
|
|
9
|
+
"supabase-postgres-best-practices": {
|
|
10
|
+
"source": "supabase/agent-skills",
|
|
11
|
+
"sourceType": "github",
|
|
12
|
+
"computedHash": "9c87c315aed143ee3b34bec8117100f5035e0df09e6b23e1ecc772cff434c9ad"
|
|
13
|
+
},
|
|
14
|
+
"vercel-react-best-practices": {
|
|
15
|
+
"source": "vercel-labs/agent-skills",
|
|
16
|
+
"sourceType": "github",
|
|
17
|
+
"computedHash": "3462ec83f862abb2d532953df33a4dbf87f4616849da5d4b5cc7c13601aaf997"
|
|
18
|
+
},
|
|
19
|
+
"web-design-guidelines": {
|
|
20
|
+
"source": "vercel-labs/agent-skills",
|
|
21
|
+
"sourceType": "github",
|
|
22
|
+
"computedHash": "a6a44d5498f7e8f68289902f3dedfc6f38ae0cee1e96527c80724cf27f727c2a"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import { useRouter, useParams } from 'next/navigation';
|
|
5
5
|
import { Eye, EyeOff } from 'lucide-react';
|
|
6
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
6
7
|
|
|
7
8
|
export default function InvitePage() {
|
|
8
9
|
const router = useRouter();
|
|
9
10
|
const params = useParams();
|
|
11
|
+
const toast = useAppToast();
|
|
10
12
|
const token = params.token as string;
|
|
11
13
|
|
|
12
14
|
const [password, setPassword] = useState('');
|
|
@@ -19,6 +21,14 @@ export default function InvitePage() {
|
|
|
19
21
|
const [showPassword, setShowPassword] = useState(false);
|
|
20
22
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
21
23
|
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!error) return;
|
|
26
|
+
toast.error(error);
|
|
27
|
+
if (userEmail) {
|
|
28
|
+
setError('');
|
|
29
|
+
}
|
|
30
|
+
}, [error, toast, userEmail]);
|
|
31
|
+
|
|
22
32
|
// Vérifier que le token est valide au chargement
|
|
23
33
|
useEffect(() => {
|
|
24
34
|
const validateToken = async () => {
|
|
@@ -27,17 +37,20 @@ export default function InvitePage() {
|
|
|
27
37
|
const data = await response.json();
|
|
28
38
|
|
|
29
39
|
if (!response.ok) {
|
|
30
|
-
|
|
40
|
+
const msg = data.error || 'Lien invalide ou expiré';
|
|
31
41
|
setValidating(false);
|
|
42
|
+
toast.error(msg);
|
|
43
|
+
router.replace('/signin');
|
|
32
44
|
return;
|
|
33
45
|
}
|
|
34
46
|
|
|
35
47
|
setUserEmail(data.email);
|
|
36
48
|
setUserName(data.name || '');
|
|
37
49
|
setValidating(false);
|
|
38
|
-
} catch
|
|
39
|
-
setError('Erreur lors de la validation du lien');
|
|
50
|
+
} catch {
|
|
40
51
|
setValidating(false);
|
|
52
|
+
toast.error('Erreur lors de la validation du lien');
|
|
53
|
+
router.replace('/signin');
|
|
41
54
|
}
|
|
42
55
|
};
|
|
43
56
|
|
|
@@ -79,8 +92,9 @@ export default function InvitePage() {
|
|
|
79
92
|
router.push(
|
|
80
93
|
'/signin?message=Mot de passe défini avec succès, vous pouvez maintenant vous connecter',
|
|
81
94
|
);
|
|
82
|
-
} catch (err:
|
|
83
|
-
|
|
95
|
+
} catch (err: unknown) {
|
|
96
|
+
const message = err instanceof Error ? err.message : 'Erreur lors de la définition du mot de passe';
|
|
97
|
+
setError(message);
|
|
84
98
|
} finally {
|
|
85
99
|
setLoading(false);
|
|
86
100
|
}
|
|
@@ -96,23 +110,8 @@ export default function InvitePage() {
|
|
|
96
110
|
);
|
|
97
111
|
}
|
|
98
112
|
|
|
99
|
-
if (
|
|
100
|
-
return
|
|
101
|
-
<div className="flex min-h-screen items-center justify-center px-4">
|
|
102
|
-
<div className="w-full max-w-md rounded-2xl bg-white p-8 shadow-xl">
|
|
103
|
-
<div className="text-center">
|
|
104
|
-
<h1 className="text-2xl font-bold text-red-600">Lien invalide</h1>
|
|
105
|
-
<p className="mt-4 text-gray-600">{error}</p>
|
|
106
|
-
<button
|
|
107
|
-
onClick={() => router.push('/signin')}
|
|
108
|
-
className="mt-6 cursor-pointer rounded-lg bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-700"
|
|
109
|
-
>
|
|
110
|
-
Retour à la connexion
|
|
111
|
-
</button>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
113
|
+
if (!userEmail && !validating) {
|
|
114
|
+
return null;
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
return (
|
|
@@ -130,8 +129,6 @@ export default function InvitePage() {
|
|
|
130
129
|
<p className="mt-1 text-sm break-all text-gray-500">{userEmail}</p>
|
|
131
130
|
</div>
|
|
132
131
|
|
|
133
|
-
{error && <div className="mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600">{error}</div>}
|
|
134
|
-
|
|
135
132
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
136
133
|
<div>
|
|
137
134
|
<label className="block text-sm font-medium text-gray-700">Mot de passe</label>
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
import { useState, useEffect, Suspense } from 'react';
|
|
4
4
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
5
5
|
import { Eye, EyeOff } from 'lucide-react';
|
|
6
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
6
7
|
|
|
7
8
|
function ResetPasswordCompleteContent() {
|
|
8
9
|
const router = useRouter();
|
|
9
10
|
const searchParams = useSearchParams();
|
|
11
|
+
const toast = useAppToast();
|
|
10
12
|
const token = searchParams.get('token') || '';
|
|
11
13
|
|
|
12
14
|
const [password, setPassword] = useState('');
|
|
@@ -26,26 +28,30 @@ function ResetPasswordCompleteContent() {
|
|
|
26
28
|
const data = await response.json();
|
|
27
29
|
|
|
28
30
|
if (!response.ok) {
|
|
29
|
-
|
|
31
|
+
const msg = data.error || 'Lien invalide ou expiré';
|
|
30
32
|
setValidating(false);
|
|
33
|
+
toast.error(msg);
|
|
34
|
+
router.replace('/signin');
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
setUserEmail(data.email);
|
|
35
39
|
setValidating(false);
|
|
36
40
|
} catch (err) {
|
|
37
|
-
setError('Erreur lors de la validation du lien');
|
|
38
41
|
setValidating(false);
|
|
42
|
+
toast.error('Erreur lors de la validation du lien');
|
|
43
|
+
router.replace('/signin');
|
|
39
44
|
}
|
|
40
45
|
};
|
|
41
46
|
|
|
42
47
|
if (token) {
|
|
43
48
|
validateToken();
|
|
44
49
|
} else {
|
|
45
|
-
setError('Token manquant');
|
|
46
50
|
setValidating(false);
|
|
51
|
+
toast.error('Token manquant');
|
|
52
|
+
router.replace('/signin');
|
|
47
53
|
}
|
|
48
|
-
}, [token]);
|
|
54
|
+
}, [token, toast, router]);
|
|
49
55
|
|
|
50
56
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
51
57
|
e.preventDefault();
|
|
@@ -97,23 +103,8 @@ function ResetPasswordCompleteContent() {
|
|
|
97
103
|
);
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
if (
|
|
101
|
-
return
|
|
102
|
-
<div className="flex min-h-screen items-center justify-center px-4">
|
|
103
|
-
<div className="w-full max-w-md rounded-2xl bg-white p-8 shadow-xl">
|
|
104
|
-
<div className="text-center">
|
|
105
|
-
<h1 className="text-2xl font-bold text-red-600">Lien invalide</h1>
|
|
106
|
-
<p className="mt-4 text-gray-600">{error}</p>
|
|
107
|
-
<button
|
|
108
|
-
onClick={() => router.push('/signin')}
|
|
109
|
-
className="mt-6 cursor-pointer rounded-lg bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-700"
|
|
110
|
-
>
|
|
111
|
-
Retour à la connexion
|
|
112
|
-
</button>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
);
|
|
106
|
+
if (!userEmail && !validating) {
|
|
107
|
+
return null;
|
|
117
108
|
}
|
|
118
109
|
|
|
119
110
|
return (
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState } from 'react';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
5
|
import Link from 'next/link';
|
|
6
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
6
7
|
|
|
7
8
|
export default function ResetPasswordPage() {
|
|
8
9
|
const router = useRouter();
|
|
10
|
+
const toast = useAppToast();
|
|
9
11
|
const [email, setEmail] = useState('');
|
|
10
12
|
const [error, setError] = useState('');
|
|
11
13
|
const [success, setSuccess] = useState(false);
|
|
12
14
|
const [loading, setLoading] = useState(false);
|
|
13
15
|
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!error) return;
|
|
18
|
+
toast.error(error);
|
|
19
|
+
setError('');
|
|
20
|
+
}, [error, toast]);
|
|
21
|
+
|
|
14
22
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
15
23
|
e.preventDefault();
|
|
16
24
|
setError('');
|
|
@@ -30,8 +38,9 @@ export default function ResetPasswordPage() {
|
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
setSuccess(true);
|
|
33
|
-
} catch (err:
|
|
34
|
-
|
|
41
|
+
} catch (err: unknown) {
|
|
42
|
+
const message = err instanceof Error ? err.message : "Erreur lors de l'envoi du code";
|
|
43
|
+
setError(message);
|
|
35
44
|
} finally {
|
|
36
45
|
setLoading(false);
|
|
37
46
|
}
|
|
@@ -102,11 +111,6 @@ export default function ResetPasswordPage() {
|
|
|
102
111
|
</p>
|
|
103
112
|
</div>
|
|
104
113
|
|
|
105
|
-
{/* Error Message */}
|
|
106
|
-
{error && (
|
|
107
|
-
<div className="mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600">{error}</div>
|
|
108
|
-
)}
|
|
109
|
-
|
|
110
114
|
{/* Form */}
|
|
111
115
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
112
116
|
<div>
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
import { useState, useEffect, Suspense } from 'react';
|
|
4
4
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
5
5
|
import Link from 'next/link';
|
|
6
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
6
7
|
|
|
7
8
|
function VerifyResetCodeContent() {
|
|
8
9
|
const router = useRouter();
|
|
9
10
|
const searchParams = useSearchParams();
|
|
11
|
+
const toast = useAppToast();
|
|
10
12
|
const email = searchParams.get('email') || '';
|
|
11
13
|
|
|
12
14
|
const [code, setCode] = useState(['', '', '', '', '', '']);
|
|
@@ -19,6 +21,12 @@ function VerifyResetCodeContent() {
|
|
|
19
21
|
}
|
|
20
22
|
}, [email, router]);
|
|
21
23
|
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!error) return;
|
|
26
|
+
toast.error(error);
|
|
27
|
+
setError('');
|
|
28
|
+
}, [error, toast]);
|
|
29
|
+
|
|
22
30
|
const handleCodeChange = (index: number, value: string) => {
|
|
23
31
|
if (!/^\d*$/.test(value)) return; // Seulement des chiffres
|
|
24
32
|
|
|
@@ -79,8 +87,9 @@ function VerifyResetCodeContent() {
|
|
|
79
87
|
|
|
80
88
|
// Rediriger vers la page de définition du nouveau mot de passe
|
|
81
89
|
router.push(`/reset-password/complete?token=${data.token}`);
|
|
82
|
-
} catch (err:
|
|
83
|
-
|
|
90
|
+
} catch (err: unknown) {
|
|
91
|
+
const message = err instanceof Error ? err.message : 'Code invalide';
|
|
92
|
+
setError(message);
|
|
84
93
|
setCode(['', '', '', '', '', '']);
|
|
85
94
|
const firstInput = document.getElementById('code-0') as HTMLInputElement;
|
|
86
95
|
firstInput?.focus();
|
|
@@ -100,11 +109,6 @@ function VerifyResetCodeContent() {
|
|
|
100
109
|
<p className="mt-1 text-sm font-medium break-all text-gray-900">{email}</p>
|
|
101
110
|
</div>
|
|
102
111
|
|
|
103
|
-
{/* Error Message */}
|
|
104
|
-
{error && (
|
|
105
|
-
<div className="mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600">{error}</div>
|
|
106
|
-
)}
|
|
107
|
-
|
|
108
112
|
{/* Form */}
|
|
109
113
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
110
114
|
<div className="flex justify-center gap-2 sm:gap-3">
|
|
@@ -144,7 +148,7 @@ function VerifyResetCodeContent() {
|
|
|
144
148
|
body: JSON.stringify({ email }),
|
|
145
149
|
});
|
|
146
150
|
setError('');
|
|
147
|
-
} catch
|
|
151
|
+
} catch {
|
|
148
152
|
// Ignorer les erreurs silencieusement
|
|
149
153
|
}
|
|
150
154
|
}}
|
|
@@ -5,10 +5,12 @@ import { useRouter, useSearchParams } from 'next/navigation';
|
|
|
5
5
|
import Link from 'next/link';
|
|
6
6
|
import { signIn, signOut } from '@/lib/auth-client';
|
|
7
7
|
import { Eye, EyeOff } from 'lucide-react';
|
|
8
|
+
import { useAppToast } from '@/contexts/app-toast-context';
|
|
8
9
|
|
|
9
10
|
function SignInContent() {
|
|
10
11
|
const router = useRouter();
|
|
11
12
|
const searchParams = useSearchParams();
|
|
13
|
+
const toast = useAppToast();
|
|
12
14
|
const [email, setEmail] = useState('');
|
|
13
15
|
const [password, setPassword] = useState('');
|
|
14
16
|
const [error, setError] = useState('');
|
|
@@ -25,28 +27,40 @@ function SignInContent() {
|
|
|
25
27
|
}
|
|
26
28
|
}, [searchParams, router]);
|
|
27
29
|
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!successMessage) return;
|
|
32
|
+
toast.success(successMessage);
|
|
33
|
+
setSuccessMessage('');
|
|
34
|
+
}, [successMessage, toast]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!error) return;
|
|
38
|
+
toast.error(error);
|
|
39
|
+
setError('');
|
|
40
|
+
}, [error, toast]);
|
|
41
|
+
|
|
28
42
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
29
43
|
e.preventDefault();
|
|
30
44
|
setError('');
|
|
31
45
|
setLoading(true);
|
|
32
46
|
|
|
33
47
|
try {
|
|
48
|
+
// Étape 1 : Vérifier d'abord si l'email/password est correct
|
|
34
49
|
await signIn.email({
|
|
35
50
|
email,
|
|
36
51
|
password,
|
|
37
52
|
});
|
|
38
53
|
|
|
39
|
-
//
|
|
54
|
+
// Étape 2 : Si l'authentification réussit, vérifier si le compte est actif
|
|
40
55
|
try {
|
|
41
56
|
const res = await fetch('/api/auth/check-active', { method: 'GET' });
|
|
42
57
|
if (res.ok) {
|
|
43
58
|
const data = await res.json();
|
|
44
59
|
if (!data.active) {
|
|
45
|
-
// Déconnecter immédiatement et afficher
|
|
60
|
+
// Déconnecter immédiatement et afficher le même message générique
|
|
61
|
+
// pour ne pas révéler que le compte existe et est simplement désactivé
|
|
46
62
|
await signOut();
|
|
47
|
-
setError(
|
|
48
|
-
'Votre compte a été désactivé. Merci de contacter un administrateur pour le réactiver.',
|
|
49
|
-
);
|
|
63
|
+
setError('Email ou mot de passe incorrect');
|
|
50
64
|
return;
|
|
51
65
|
}
|
|
52
66
|
}
|
|
@@ -55,6 +69,7 @@ function SignInContent() {
|
|
|
55
69
|
// On ne bloque pas la connexion dans ce cas, mais on logue l'erreur
|
|
56
70
|
}
|
|
57
71
|
|
|
72
|
+
// Étape 3 : Rediriger vers le dashboard si tout est OK
|
|
58
73
|
router.push('/dashboard');
|
|
59
74
|
} catch (err) {
|
|
60
75
|
setError('Email ou mot de passe incorrect');
|
|
@@ -74,18 +89,6 @@ function SignInContent() {
|
|
|
74
89
|
<p className="mt-2 text-sm text-gray-600">Connectez-vous à votre compte CRM</p>
|
|
75
90
|
</div>
|
|
76
91
|
|
|
77
|
-
{/* Success Message */}
|
|
78
|
-
{successMessage && (
|
|
79
|
-
<div className="mb-4 rounded-lg bg-green-50 p-4 text-sm text-green-600">
|
|
80
|
-
{successMessage}
|
|
81
|
-
</div>
|
|
82
|
-
)}
|
|
83
|
-
|
|
84
|
-
{/* Error Message */}
|
|
85
|
-
{error && (
|
|
86
|
-
<div className="mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600">{error}</div>
|
|
87
|
-
)}
|
|
88
|
-
|
|
89
92
|
{/* Form */}
|
|
90
93
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
91
94
|
<div>
|