create-crm-tmp 1.1.2 → 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 +53 -67
- package/template/components.json +22 -0
- package/template/exemple-contacts.csv +54 -0
- package/template/next.config.ts +27 -1
- package/template/package.json +64 -27
- package/template/prisma/schema.prisma +821 -72
- 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 +2231 -2188
- 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 +5035 -4126
- 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 +12 -17
- 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 -85
- 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 +33 -23
- 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 +161 -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 +16 -1
- 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 +94 -55
- 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 +509 -146
- package/template/src/app/api/workflows/route.ts +46 -4
- package/template/src/app/globals.css +210 -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 +14 -39
- package/template/src/lib/template-variables.ts +67 -33
- package/template/src/lib/utils.ts +26 -2
- package/template/src/lib/workflow-executor.ts +445 -229
- 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/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/sales-analytics-chart.tsx +0 -77
- 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,157 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link';
|
|
4
|
-
import {
|
|
5
|
-
Phone,
|
|
6
|
-
Mail,
|
|
7
|
-
Calendar,
|
|
8
|
-
MessageSquare,
|
|
9
|
-
FileText,
|
|
10
|
-
TrendingUp,
|
|
11
|
-
RefreshCw,
|
|
12
|
-
CalendarCheck,
|
|
13
|
-
CalendarX,
|
|
14
|
-
CalendarClock,
|
|
15
|
-
UserCheck,
|
|
16
|
-
} from 'lucide-react';
|
|
17
|
-
import { cn } from '@/lib/utils';
|
|
18
|
-
|
|
19
|
-
interface Interaction {
|
|
20
|
-
id: string;
|
|
21
|
-
type: string;
|
|
22
|
-
title: string | null;
|
|
23
|
-
content: string;
|
|
24
|
-
date: string;
|
|
25
|
-
contact: {
|
|
26
|
-
id: string;
|
|
27
|
-
name: string;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface RecentActivityProps {
|
|
32
|
-
readonly interactions: Interaction[];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const interactionIcons: Record<string, typeof Phone> = {
|
|
36
|
-
CALL: Phone,
|
|
37
|
-
SMS: MessageSquare,
|
|
38
|
-
EMAIL: Mail,
|
|
39
|
-
MEETING: Calendar,
|
|
40
|
-
NOTE: FileText,
|
|
41
|
-
STATUS_CHANGE: TrendingUp,
|
|
42
|
-
CONTACT_UPDATE: RefreshCw,
|
|
43
|
-
APPOINTMENT_CREATED: CalendarCheck,
|
|
44
|
-
APPOINTMENT_DELETED: CalendarX,
|
|
45
|
-
APPOINTMENT_CHANGED: CalendarClock,
|
|
46
|
-
ASSIGNMENT_CHANGE: UserCheck,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const interactionColors: Record<string, string> = {
|
|
50
|
-
CALL: 'bg-blue-50 text-blue-500',
|
|
51
|
-
SMS: 'bg-emerald-50 text-emerald-500',
|
|
52
|
-
EMAIL: 'bg-orange-50 text-orange-500',
|
|
53
|
-
MEETING: 'bg-purple-50 text-purple-500',
|
|
54
|
-
NOTE: 'bg-gray-50 text-gray-500',
|
|
55
|
-
STATUS_CHANGE: 'bg-amber-50 text-amber-500',
|
|
56
|
-
CONTACT_UPDATE: 'bg-cyan-50 text-cyan-500',
|
|
57
|
-
APPOINTMENT_CREATED: 'bg-emerald-50 text-emerald-500',
|
|
58
|
-
APPOINTMENT_DELETED: 'bg-red-50 text-red-500',
|
|
59
|
-
APPOINTMENT_CHANGED: 'bg-orange-50 text-orange-500',
|
|
60
|
-
ASSIGNMENT_CHANGE: 'bg-pink-50 text-pink-500',
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const interactionLabels: Record<string, string> = {
|
|
64
|
-
CALL: 'Appel',
|
|
65
|
-
SMS: 'SMS',
|
|
66
|
-
EMAIL: 'Email',
|
|
67
|
-
MEETING: 'Réunion',
|
|
68
|
-
NOTE: 'Note',
|
|
69
|
-
STATUS_CHANGE: 'Changement de statut',
|
|
70
|
-
CONTACT_UPDATE: 'Mise à jour',
|
|
71
|
-
APPOINTMENT_CREATED: 'RDV créé',
|
|
72
|
-
APPOINTMENT_DELETED: 'RDV supprimé',
|
|
73
|
-
APPOINTMENT_CHANGED: 'RDV modifié',
|
|
74
|
-
ASSIGNMENT_CHANGE: 'Assignation',
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export function RecentActivity({ interactions }: RecentActivityProps) {
|
|
78
|
-
return (
|
|
79
|
-
<div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
|
|
80
|
-
<div className="mb-4 flex items-center justify-between">
|
|
81
|
-
<div>
|
|
82
|
-
<h3 className="text-base font-semibold text-gray-900">Activité Récente</h3>
|
|
83
|
-
<p className="mt-0.5 text-xs text-gray-400">Dernières interactions</p>
|
|
84
|
-
</div>
|
|
85
|
-
<Link
|
|
86
|
-
href="/contacts"
|
|
87
|
-
className="dash-link cursor-pointer text-xs font-medium"
|
|
88
|
-
>
|
|
89
|
-
Voir tout →
|
|
90
|
-
</Link>
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
{interactions.length === 0 ? (
|
|
94
|
-
<div className="flex flex-1 items-center justify-center">
|
|
95
|
-
<div className="text-center">
|
|
96
|
-
<FileText className="mx-auto h-10 w-10 text-gray-200" />
|
|
97
|
-
<p className="mt-2 text-sm text-gray-400">Aucune activité récente</p>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
) : (
|
|
101
|
-
<div className="flex-1 space-y-1 overflow-auto">
|
|
102
|
-
{interactions.map((interaction) => {
|
|
103
|
-
const Icon = interactionIcons[interaction.type] || FileText;
|
|
104
|
-
const color = interactionColors[interaction.type] || 'bg-gray-50 text-gray-500';
|
|
105
|
-
const label = interactionLabels[interaction.type] || interaction.type;
|
|
106
|
-
const date = new Date(interaction.date);
|
|
107
|
-
const now = new Date();
|
|
108
|
-
const diffMs = now.getTime() - date.getTime();
|
|
109
|
-
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
110
|
-
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
|
111
|
-
|
|
112
|
-
let timeAgo = '';
|
|
113
|
-
if (diffMinutes < 1) {
|
|
114
|
-
timeAgo = "À l'instant";
|
|
115
|
-
} else if (diffMinutes < 60) {
|
|
116
|
-
timeAgo = `Il y a ${diffMinutes} min`;
|
|
117
|
-
} else if (diffHours < 24) {
|
|
118
|
-
timeAgo = `Il y a ${diffHours}h`;
|
|
119
|
-
} else {
|
|
120
|
-
timeAgo = date.toLocaleDateString('fr-FR', {
|
|
121
|
-
day: 'numeric',
|
|
122
|
-
month: 'short',
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<div
|
|
128
|
-
key={interaction.id}
|
|
129
|
-
className="group flex items-start gap-3 rounded-xl border-l-2 border-transparent px-3 py-2.5 transition-all duration-150 dash-hover-border-left"
|
|
130
|
-
>
|
|
131
|
-
<div className={cn('rounded-lg p-1.5', color)}>
|
|
132
|
-
<Icon className="h-3.5 w-3.5" />
|
|
133
|
-
</div>
|
|
134
|
-
<div className="min-w-0 flex-1">
|
|
135
|
-
<div className="flex items-start justify-between gap-2">
|
|
136
|
-
<div className="min-w-0">
|
|
137
|
-
<p className="text-sm font-medium text-gray-900">{label}</p>
|
|
138
|
-
<Link
|
|
139
|
-
href={`/contacts/${interaction.contact.id}`}
|
|
140
|
-
className="dash-hover-text cursor-pointer text-xs text-gray-500 transition-colors"
|
|
141
|
-
>
|
|
142
|
-
{interaction.contact.name}
|
|
143
|
-
</Link>
|
|
144
|
-
</div>
|
|
145
|
-
<span className="shrink-0 text-[10px] font-medium text-gray-400">
|
|
146
|
-
{timeAgo}
|
|
147
|
-
</span>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
);
|
|
152
|
-
})}
|
|
153
|
-
</div>
|
|
154
|
-
)}
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
|
|
4
|
-
|
|
5
|
-
interface SalesAnalyticsChartProps {
|
|
6
|
-
data: Array<{ month: string; count: number }>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function SalesAnalyticsChart({ data }: SalesAnalyticsChartProps) {
|
|
10
|
-
// Calculer le total et la croissance
|
|
11
|
-
const total = data.reduce((sum, item) => sum + item.count, 0);
|
|
12
|
-
const previousTotal =
|
|
13
|
-
data.length > 1 ? data.slice(0, -1).reduce((sum, item) => sum + item.count, 0) : 0;
|
|
14
|
-
const growth = previousTotal > 0 ? ((total - previousTotal) / previousTotal) * 100 : 0;
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
|
|
18
|
-
<div className="mb-4 flex items-center justify-between">
|
|
19
|
-
<div>
|
|
20
|
-
<h3 className="text-lg font-bold text-gray-900">Analytiques des Ventes</h3>
|
|
21
|
-
</div>
|
|
22
|
-
<select className="rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none">
|
|
23
|
-
<option>Mensuel</option>
|
|
24
|
-
<option>Hebdomadaire</option>
|
|
25
|
-
<option>Annuel</option>
|
|
26
|
-
</select>
|
|
27
|
-
</div>
|
|
28
|
-
<div className="mb-4 flex items-baseline gap-4">
|
|
29
|
-
<div>
|
|
30
|
-
<p className="text-2xl font-bold text-gray-900">
|
|
31
|
-
{total.toLocaleString('fr-FR')} contacts ce mois
|
|
32
|
-
</p>
|
|
33
|
-
<p className="mt-1 text-sm font-semibold text-emerald-600">
|
|
34
|
-
Augmentation ce mois : +{growth.toFixed(1)}%
|
|
35
|
-
</p>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
<div className="h-[280px]">
|
|
39
|
-
<ResponsiveContainer width="100%" height="100%">
|
|
40
|
-
<BarChart data={data} barCategoryGap="20%">
|
|
41
|
-
<defs>
|
|
42
|
-
<linearGradient id="barGradient" x1="0" y1="0" x2="0" y2="1">
|
|
43
|
-
<stop offset="0%" stopColor="#8b5cf6" stopOpacity={1} />
|
|
44
|
-
<stop offset="100%" stopColor="#6366f1" stopOpacity={0.8} />
|
|
45
|
-
</linearGradient>
|
|
46
|
-
</defs>
|
|
47
|
-
<XAxis
|
|
48
|
-
dataKey="month"
|
|
49
|
-
stroke="#9ca3af"
|
|
50
|
-
fontSize={12}
|
|
51
|
-
tickLine={false}
|
|
52
|
-
axisLine={false}
|
|
53
|
-
/>
|
|
54
|
-
<YAxis
|
|
55
|
-
stroke="#9ca3af"
|
|
56
|
-
fontSize={12}
|
|
57
|
-
tickLine={false}
|
|
58
|
-
axisLine={false}
|
|
59
|
-
tickFormatter={(value) => `${value}`}
|
|
60
|
-
/>
|
|
61
|
-
<Tooltip
|
|
62
|
-
contentStyle={{
|
|
63
|
-
backgroundColor: 'white',
|
|
64
|
-
border: '1px solid #e5e7eb',
|
|
65
|
-
borderRadius: '12px',
|
|
66
|
-
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
|
|
67
|
-
}}
|
|
68
|
-
labelStyle={{ color: '#374151', fontWeight: 600 }}
|
|
69
|
-
formatter={(value: number) => [`${value.toLocaleString('fr-FR')}`, 'Contacts']}
|
|
70
|
-
/>
|
|
71
|
-
<Bar dataKey="count" fill="url(#barGradient)" radius={[8, 8, 0, 0]} />
|
|
72
|
-
</BarChart>
|
|
73
|
-
</ResponsiveContainer>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Bar,
|
|
5
|
-
BarChart,
|
|
6
|
-
ResponsiveContainer,
|
|
7
|
-
Tooltip,
|
|
8
|
-
XAxis,
|
|
9
|
-
YAxis,
|
|
10
|
-
CartesianGrid,
|
|
11
|
-
Cell,
|
|
12
|
-
} from 'recharts';
|
|
13
|
-
import { useDashboardTheme } from '@/contexts/dashboard-theme-context';
|
|
14
|
-
|
|
15
|
-
interface StatusDistributionChartProps {
|
|
16
|
-
readonly data: Array<{ name: string; value: number }>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function StatusDistributionChart({ data }: Readonly<StatusDistributionChartProps>) {
|
|
20
|
-
const { theme } = useDashboardTheme();
|
|
21
|
-
|
|
22
|
-
// Générer une palette basée sur le thème actif + couleurs complémentaires
|
|
23
|
-
const statusColors = [
|
|
24
|
-
theme.hex[500],
|
|
25
|
-
theme.hex[400],
|
|
26
|
-
theme.hex[300],
|
|
27
|
-
theme.hex[200],
|
|
28
|
-
'#10b981',
|
|
29
|
-
'#34d399',
|
|
30
|
-
'#6ee7b7',
|
|
31
|
-
'#3b82f6',
|
|
32
|
-
'#60a5fa',
|
|
33
|
-
'#93c5fd',
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
|
|
38
|
-
<div className="mb-4">
|
|
39
|
-
<h3 className="text-base font-semibold text-gray-900">Répartition par Statut</h3>
|
|
40
|
-
<p className="mt-0.5 text-xs text-gray-400">Distribution des contacts</p>
|
|
41
|
-
</div>
|
|
42
|
-
<div className="min-h-0 flex-1">
|
|
43
|
-
<ResponsiveContainer width="100%" height="100%">
|
|
44
|
-
<BarChart
|
|
45
|
-
data={data}
|
|
46
|
-
layout="vertical"
|
|
47
|
-
margin={{ top: 0, right: 20, left: 0, bottom: 0 }}
|
|
48
|
-
>
|
|
49
|
-
<CartesianGrid strokeDasharray="3 3" stroke="#f3f4f6" horizontal={false} />
|
|
50
|
-
<XAxis type="number" stroke="#d1d5db" fontSize={11} tickLine={false} axisLine={false} />
|
|
51
|
-
<YAxis
|
|
52
|
-
dataKey="name"
|
|
53
|
-
type="category"
|
|
54
|
-
stroke="#d1d5db"
|
|
55
|
-
fontSize={11}
|
|
56
|
-
tickLine={false}
|
|
57
|
-
axisLine={false}
|
|
58
|
-
width={90}
|
|
59
|
-
/>
|
|
60
|
-
<Tooltip
|
|
61
|
-
contentStyle={{
|
|
62
|
-
backgroundColor: '#fff',
|
|
63
|
-
border: '1px solid #f3f4f6',
|
|
64
|
-
borderRadius: '12px',
|
|
65
|
-
boxShadow: '0 4px 12px rgba(0,0,0,0.08)',
|
|
66
|
-
fontSize: '13px',
|
|
67
|
-
}}
|
|
68
|
-
/>
|
|
69
|
-
<Bar dataKey="value" radius={[0, 6, 6, 0]} name="Contacts" barSize={20}>
|
|
70
|
-
{data.map((entry, index) => (
|
|
71
|
-
<Cell
|
|
72
|
-
key={`status-${entry.name}`}
|
|
73
|
-
fill={statusColors[index % statusColors.length]}
|
|
74
|
-
/>
|
|
75
|
-
))}
|
|
76
|
-
</Bar>
|
|
77
|
-
</BarChart>
|
|
78
|
-
</ResponsiveContainer>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link';
|
|
4
|
-
import { Users } from 'lucide-react';
|
|
5
|
-
|
|
6
|
-
interface Contact {
|
|
7
|
-
id: string;
|
|
8
|
-
name: string;
|
|
9
|
-
phone: string;
|
|
10
|
-
email: string | null;
|
|
11
|
-
status: string;
|
|
12
|
-
interactionsCount: number;
|
|
13
|
-
assignedCommercial?: string;
|
|
14
|
-
assignedTelepro?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface TopContactsListProps {
|
|
18
|
-
readonly contacts: Contact[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function TopContactsList({ contacts }: TopContactsListProps) {
|
|
22
|
-
if (contacts.length === 0) {
|
|
23
|
-
return (
|
|
24
|
-
<div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
|
|
25
|
-
<div className="flex items-center justify-between">
|
|
26
|
-
<div>
|
|
27
|
-
<h3 className="text-base font-semibold text-gray-900">Derniers Prospects</h3>
|
|
28
|
-
<p className="mt-0.5 text-xs text-gray-400">Contacts récemment ajoutés</p>
|
|
29
|
-
</div>
|
|
30
|
-
<Link
|
|
31
|
-
href="/contacts"
|
|
32
|
-
className="dash-link cursor-pointer text-xs font-medium"
|
|
33
|
-
>
|
|
34
|
-
Voir tout →
|
|
35
|
-
</Link>
|
|
36
|
-
</div>
|
|
37
|
-
<div className="flex flex-1 items-center justify-center">
|
|
38
|
-
<div className="text-center">
|
|
39
|
-
<Users className="mx-auto h-10 w-10 text-gray-200" />
|
|
40
|
-
<p className="mt-2 text-sm text-gray-400">Aucun contact</p>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
|
|
49
|
-
<div className="mb-4 flex items-center justify-between">
|
|
50
|
-
<div>
|
|
51
|
-
<h3 className="text-base font-semibold text-gray-900">Derniers Prospects</h3>
|
|
52
|
-
<p className="mt-0.5 text-xs text-gray-400">Contacts récemment ajoutés</p>
|
|
53
|
-
</div>
|
|
54
|
-
<Link
|
|
55
|
-
href="/contacts"
|
|
56
|
-
className="dash-link cursor-pointer text-xs font-medium"
|
|
57
|
-
>
|
|
58
|
-
Voir tout →
|
|
59
|
-
</Link>
|
|
60
|
-
</div>
|
|
61
|
-
<div className="flex-1 overflow-auto">
|
|
62
|
-
<table className="w-full">
|
|
63
|
-
<thead>
|
|
64
|
-
<tr className="border-b border-gray-100">
|
|
65
|
-
<th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
|
|
66
|
-
Prospect
|
|
67
|
-
</th>
|
|
68
|
-
<th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
|
|
69
|
-
Email
|
|
70
|
-
</th>
|
|
71
|
-
<th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
|
|
72
|
-
Télépro
|
|
73
|
-
</th>
|
|
74
|
-
<th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
|
|
75
|
-
Statut
|
|
76
|
-
</th>
|
|
77
|
-
</tr>
|
|
78
|
-
</thead>
|
|
79
|
-
<tbody>
|
|
80
|
-
{contacts.map((contact) => {
|
|
81
|
-
const nameParts = contact.name.split(' ');
|
|
82
|
-
const firstName = nameParts[0] || '';
|
|
83
|
-
const lastName = nameParts.slice(1).join(' ') || '';
|
|
84
|
-
const initials = `${firstName[0] || ''}${lastName[0] || ''}`.toUpperCase();
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<tr
|
|
88
|
-
key={contact.id}
|
|
89
|
-
className="border-b border-gray-50 transition-colors dash-hover-bg"
|
|
90
|
-
>
|
|
91
|
-
<td className="py-3">
|
|
92
|
-
<div className="flex items-center gap-2.5">
|
|
93
|
-
<div className="dash-avatar flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[10px] font-semibold">
|
|
94
|
-
{initials || '?'}
|
|
95
|
-
</div>
|
|
96
|
-
<Link
|
|
97
|
-
href={`/contacts/${contact.id}`}
|
|
98
|
-
className="dash-hover-text cursor-pointer text-sm font-medium text-gray-900 transition-colors"
|
|
99
|
-
>
|
|
100
|
-
{contact.name}
|
|
101
|
-
</Link>
|
|
102
|
-
</div>
|
|
103
|
-
</td>
|
|
104
|
-
<td className="py-3 text-xs text-gray-500">{contact.email || '-'}</td>
|
|
105
|
-
<td className="py-3 text-xs text-gray-500">{contact.assignedTelepro || '-'}</td>
|
|
106
|
-
<td className="py-3">
|
|
107
|
-
<span className="inline-flex rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-medium text-gray-700">
|
|
108
|
-
{contact.status}
|
|
109
|
-
</span>
|
|
110
|
-
</td>
|
|
111
|
-
</tr>
|
|
112
|
-
);
|
|
113
|
-
})}
|
|
114
|
-
</tbody>
|
|
115
|
-
</table>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
);
|
|
119
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { X, GripVertical } from 'lucide-react';
|
|
4
|
-
import { cn } from '@/lib/utils';
|
|
5
|
-
|
|
6
|
-
interface WidgetWrapperProps {
|
|
7
|
-
readonly children: React.ReactNode;
|
|
8
|
-
readonly onRemove?: () => void;
|
|
9
|
-
readonly className?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function WidgetWrapper({ children, onRemove, className }: WidgetWrapperProps) {
|
|
13
|
-
return (
|
|
14
|
-
<div className={cn('group/widget relative h-full w-full', className)}>
|
|
15
|
-
{/* Barre d'outils au hover (visible uniquement si des actions sont disponibles) */}
|
|
16
|
-
{onRemove && (
|
|
17
|
-
<div className="absolute top-0 right-0 z-10 flex items-center gap-1 opacity-0 transition-opacity duration-150 group-hover/widget:opacity-100">
|
|
18
|
-
{/* Poignée de drag */}
|
|
19
|
-
<div className="drag-handle flex h-7 w-7 cursor-grab items-center justify-center rounded-lg bg-white/90 shadow-sm ring-1 ring-gray-200/60 backdrop-blur-sm transition-colors hover:bg-gray-50 active:cursor-grabbing">
|
|
20
|
-
<GripVertical className="h-3.5 w-3.5 text-gray-400" />
|
|
21
|
-
</div>
|
|
22
|
-
{/* Bouton supprimer */}
|
|
23
|
-
<button
|
|
24
|
-
onClick={(e) => {
|
|
25
|
-
e.stopPropagation();
|
|
26
|
-
onRemove();
|
|
27
|
-
}}
|
|
28
|
-
className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg bg-white/90 shadow-sm ring-1 ring-gray-200/60 backdrop-blur-sm transition-colors hover:bg-red-50 hover:text-red-600"
|
|
29
|
-
>
|
|
30
|
-
<X className="h-3.5 w-3.5" />
|
|
31
|
-
</button>
|
|
32
|
-
</div>
|
|
33
|
-
)}
|
|
34
|
-
|
|
35
|
-
{/* Contenu du widget */}
|
|
36
|
-
{children}
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
type DashboardTheme,
|
|
6
|
-
DASHBOARD_THEMES,
|
|
7
|
-
DEFAULT_THEME_KEY,
|
|
8
|
-
getThemeByKey,
|
|
9
|
-
} from '@/lib/dashboard-themes';
|
|
10
|
-
|
|
11
|
-
interface DashboardThemeContextType {
|
|
12
|
-
theme: DashboardTheme;
|
|
13
|
-
setThemeKey: (key: string) => void;
|
|
14
|
-
themes: DashboardTheme[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const DashboardThemeContext = createContext<DashboardThemeContextType | undefined>(undefined);
|
|
18
|
-
|
|
19
|
-
const STORAGE_KEY = 'dashboard_theme';
|
|
20
|
-
|
|
21
|
-
export function DashboardThemeProvider({ children }: Readonly<{ children: ReactNode }>) {
|
|
22
|
-
const [themeKey, setThemeKeyState] = useState(DEFAULT_THEME_KEY);
|
|
23
|
-
|
|
24
|
-
// Charger le thème depuis le localStorage
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const stored = localStorage.getItem(STORAGE_KEY);
|
|
27
|
-
if (stored && DASHBOARD_THEMES.some((t) => t.key === stored)) {
|
|
28
|
-
setThemeKeyState(stored);
|
|
29
|
-
}
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
|
-
const setThemeKey = (key: string) => {
|
|
33
|
-
setThemeKeyState(key);
|
|
34
|
-
localStorage.setItem(STORAGE_KEY, key);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const theme = getThemeByKey(themeKey);
|
|
38
|
-
|
|
39
|
-
const value = useMemo(
|
|
40
|
-
() => ({ theme, setThemeKey, themes: DASHBOARD_THEMES }),
|
|
41
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
-
[theme],
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<DashboardThemeContext.Provider value={value}>
|
|
47
|
-
{children}
|
|
48
|
-
</DashboardThemeContext.Provider>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function useDashboardTheme() {
|
|
53
|
-
const context = useContext(DashboardThemeContext);
|
|
54
|
-
if (!context) {
|
|
55
|
-
throw new Error('useDashboardTheme doit être utilisé dans un DashboardThemeProvider');
|
|
56
|
-
}
|
|
57
|
-
return context;
|
|
58
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thèmes de couleur pour le tableau de bord
|
|
3
|
-
* Chaque thème fournit des valeurs hex pour toutes les nuances nécessaires
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface DashboardTheme {
|
|
7
|
-
key: string;
|
|
8
|
-
label: string;
|
|
9
|
-
hex: {
|
|
10
|
-
50: string;
|
|
11
|
-
100: string;
|
|
12
|
-
200: string;
|
|
13
|
-
300: string;
|
|
14
|
-
400: string;
|
|
15
|
-
500: string;
|
|
16
|
-
600: string;
|
|
17
|
-
700: string;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const DASHBOARD_THEMES: DashboardTheme[] = [
|
|
22
|
-
{
|
|
23
|
-
key: 'orange',
|
|
24
|
-
label: 'Orange',
|
|
25
|
-
hex: {
|
|
26
|
-
50: '#fff7ed',
|
|
27
|
-
100: '#ffedd5',
|
|
28
|
-
200: '#fed7aa',
|
|
29
|
-
300: '#fdba74',
|
|
30
|
-
400: '#fb923c',
|
|
31
|
-
500: '#f97316',
|
|
32
|
-
600: '#ea580c',
|
|
33
|
-
700: '#c2410c',
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
key: 'blue',
|
|
38
|
-
label: 'Bleu',
|
|
39
|
-
hex: {
|
|
40
|
-
50: '#eff6ff',
|
|
41
|
-
100: '#dbeafe',
|
|
42
|
-
200: '#bfdbfe',
|
|
43
|
-
300: '#93c5fd',
|
|
44
|
-
400: '#60a5fa',
|
|
45
|
-
500: '#3b82f6',
|
|
46
|
-
600: '#2563eb',
|
|
47
|
-
700: '#1d4ed8',
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
key: 'violet',
|
|
52
|
-
label: 'Violet',
|
|
53
|
-
hex: {
|
|
54
|
-
50: '#f5f3ff',
|
|
55
|
-
100: '#ede9fe',
|
|
56
|
-
200: '#ddd6fe',
|
|
57
|
-
300: '#c4b5fd',
|
|
58
|
-
400: '#a78bfa',
|
|
59
|
-
500: '#8b5cf6',
|
|
60
|
-
600: '#7c3aed',
|
|
61
|
-
700: '#6d28d9',
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
key: 'emerald',
|
|
66
|
-
label: 'Émeraude',
|
|
67
|
-
hex: {
|
|
68
|
-
50: '#ecfdf5',
|
|
69
|
-
100: '#d1fae5',
|
|
70
|
-
200: '#a7f3d0',
|
|
71
|
-
300: '#6ee7b7',
|
|
72
|
-
400: '#34d399',
|
|
73
|
-
500: '#10b981',
|
|
74
|
-
600: '#059669',
|
|
75
|
-
700: '#047857',
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
key: 'rose',
|
|
80
|
-
label: 'Rose',
|
|
81
|
-
hex: {
|
|
82
|
-
50: '#fff1f2',
|
|
83
|
-
100: '#ffe4e6',
|
|
84
|
-
200: '#fecdd3',
|
|
85
|
-
300: '#fda4af',
|
|
86
|
-
400: '#fb7185',
|
|
87
|
-
500: '#f43f5e',
|
|
88
|
-
600: '#e11d48',
|
|
89
|
-
700: '#be123c',
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
key: 'cyan',
|
|
94
|
-
label: 'Cyan',
|
|
95
|
-
hex: {
|
|
96
|
-
50: '#ecfeff',
|
|
97
|
-
100: '#cffafe',
|
|
98
|
-
200: '#a5f3fc',
|
|
99
|
-
300: '#67e8f9',
|
|
100
|
-
400: '#22d3ee',
|
|
101
|
-
500: '#06b6d4',
|
|
102
|
-
600: '#0891b2',
|
|
103
|
-
700: '#0e7490',
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
key: 'amber',
|
|
108
|
-
label: 'Ambre',
|
|
109
|
-
hex: {
|
|
110
|
-
50: '#fffbeb',
|
|
111
|
-
100: '#fef3c7',
|
|
112
|
-
200: '#fde68a',
|
|
113
|
-
300: '#fcd34d',
|
|
114
|
-
400: '#fbbf24',
|
|
115
|
-
500: '#f59e0b',
|
|
116
|
-
600: '#d97706',
|
|
117
|
-
700: '#b45309',
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
key: 'indigo',
|
|
122
|
-
label: 'Indigo',
|
|
123
|
-
hex: {
|
|
124
|
-
50: '#eef2ff',
|
|
125
|
-
100: '#e0e7ff',
|
|
126
|
-
200: '#c7d2fe',
|
|
127
|
-
300: '#a5b4fc',
|
|
128
|
-
400: '#818cf8',
|
|
129
|
-
500: '#6366f1',
|
|
130
|
-
600: '#4f46e5',
|
|
131
|
-
700: '#4338ca',
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
export const DEFAULT_THEME_KEY = 'orange';
|
|
137
|
-
|
|
138
|
-
export function getThemeByKey(key: string): DashboardTheme {
|
|
139
|
-
return DASHBOARD_THEMES.find((t) => t.key === key) || DASHBOARD_THEMES[0];
|
|
140
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// Layout par défaut pour les nouveaux utilisateurs
|
|
2
|
-
// Ce fichier est séparé du widget-registry pour être importable côté serveur
|
|
3
|
-
export const DEFAULT_WIDGETS = [
|
|
4
|
-
{ type: 'stat_total_contacts', x: 0, y: 0, w: 3, h: 2 },
|
|
5
|
-
{ type: 'stat_new_contacts', x: 3, y: 0, w: 3, h: 2 },
|
|
6
|
-
{ type: 'stat_completed_tasks', x: 6, y: 0, w: 3, h: 2 },
|
|
7
|
-
{ type: 'stat_pending_tasks', x: 9, y: 0, w: 3, h: 2 },
|
|
8
|
-
{ type: 'contacts_chart', x: 0, y: 2, w: 6, h: 4 },
|
|
9
|
-
{ type: 'top_contacts', x: 6, y: 2, w: 6, h: 4 },
|
|
10
|
-
{ type: 'activity_chart', x: 0, y: 6, w: 8, h: 4 },
|
|
11
|
-
{ type: 'tasks_pie', x: 8, y: 6, w: 4, h: 4 },
|
|
12
|
-
{ type: 'upcoming_tasks', x: 0, y: 10, w: 6, h: 5 },
|
|
13
|
-
{ type: 'recent_activity', x: 6, y: 10, w: 6, h: 5 },
|
|
14
|
-
];
|