create-crm-tmp 1.0.2 → 1.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.
Files changed (73) hide show
  1. package/bin/create-crm-tmp.js +7 -3
  2. package/package.json +1 -1
  3. package/template/README.md +70 -5
  4. package/template/WORKFLOWS_CRON.md +49 -27
  5. package/template/package.json +18 -16
  6. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +20 -0
  7. package/template/prisma/schema.prisma +17 -0
  8. package/template/src/app/(dashboard)/agenda/page.tsx +279 -225
  9. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +1 -5
  10. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +20 -47
  11. package/template/src/app/(dashboard)/automatisation/new/page.tsx +0 -2
  12. package/template/src/app/(dashboard)/closing/page.tsx +5 -57
  13. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +60 -44
  14. package/template/src/app/(dashboard)/contacts/page.tsx +156 -210
  15. package/template/src/app/(dashboard)/dashboard/page.tsx +438 -91
  16. package/template/src/app/(dashboard)/settings/page.tsx +179 -77
  17. package/template/src/app/(dashboard)/users/layout.tsx +30 -0
  18. package/template/src/app/(dashboard)/users/list/page.tsx +213 -159
  19. package/template/src/app/(dashboard)/users/page.tsx +13 -46
  20. package/template/src/app/(dashboard)/users/permissions/page.tsx +0 -2
  21. package/template/src/app/(dashboard)/users/roles/page.tsx +0 -2
  22. package/template/src/app/api/audit-logs/route.ts +0 -2
  23. package/template/src/app/api/auth/google/status/route.ts +46 -7
  24. package/template/src/app/api/closing-reasons/route.ts +0 -2
  25. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +2 -1
  26. package/template/src/app/api/contacts/[id]/files/route.ts +25 -20
  27. package/template/src/app/api/contacts/[id]/route.ts +2 -3
  28. package/template/src/app/api/contacts/export/route.ts +14 -11
  29. package/template/src/app/api/contacts/import/route.ts +2 -6
  30. package/template/src/app/api/contacts/route.ts +1 -1
  31. package/template/src/app/api/dashboard/stats/route.ts +7 -0
  32. package/template/src/app/api/dashboard/widgets/[id]/route.ts +47 -0
  33. package/template/src/app/api/dashboard/widgets/route.ts +181 -0
  34. package/template/src/app/api/integrations/google-sheet/sync/route.ts +58 -28
  35. package/template/src/app/api/reminders/route.ts +4 -2
  36. package/template/src/app/api/roles/route.ts +1 -1
  37. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +1 -6
  38. package/template/src/app/api/settings/closing-reasons/route.ts +0 -2
  39. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +10 -5
  40. package/template/src/app/api/settings/google-sheet/route.ts +3 -3
  41. package/template/src/app/api/tasks/[id]/route.ts +4 -4
  42. package/template/src/app/api/tasks/meet/route.ts +1 -2
  43. package/template/src/app/api/tasks/route.ts +16 -18
  44. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  45. package/template/src/app/api/workflows/[id]/route.ts +2 -9
  46. package/template/src/app/api/workflows/route.ts +0 -1
  47. package/template/src/app/globals.css +96 -0
  48. package/template/src/components/dashboard/activity-chart.tsx +37 -37
  49. package/template/src/components/dashboard/add-widget-dialog.tsx +161 -0
  50. package/template/src/components/dashboard/color-picker.tsx +65 -0
  51. package/template/src/components/dashboard/contacts-chart.tsx +36 -30
  52. package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
  53. package/template/src/components/dashboard/recent-activity.tsx +79 -86
  54. package/template/src/components/dashboard/sales-analytics-chart.tsx +4 -8
  55. package/template/src/components/dashboard/stat-card.tsx +42 -40
  56. package/template/src/components/dashboard/status-distribution-chart.tsx +64 -27
  57. package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
  58. package/template/src/components/dashboard/top-contacts-list.tsx +41 -51
  59. package/template/src/components/dashboard/upcoming-tasks-list.tsx +71 -78
  60. package/template/src/components/dashboard/widget-wrapper.tsx +39 -0
  61. package/template/src/components/header.tsx +21 -12
  62. package/template/src/components/page-header.tsx +14 -47
  63. package/template/src/components/sidebar.tsx +3 -4
  64. package/template/src/contexts/dashboard-theme-context.tsx +58 -0
  65. package/template/src/lib/audit-log.ts +0 -2
  66. package/template/src/lib/dashboard-themes.ts +140 -0
  67. package/template/src/lib/default-widgets.ts +14 -0
  68. package/template/src/lib/google-drive.ts +38 -30
  69. package/template/src/lib/permissions.ts +56 -1
  70. package/template/src/lib/prisma.ts +0 -1
  71. package/template/src/lib/widget-registry.ts +177 -0
  72. package/template/src/lib/workflow-executor.ts +7 -13
  73. package/README.md +0 -89
@@ -3,7 +3,6 @@
3
3
  import { useState, useEffect, useRef, useMemo } from 'react';
4
4
  import { useRouter } from 'next/navigation';
5
5
  import { useUserRole } from '@/hooks/use-user-role';
6
- import { useMobileMenuContext } from '@/contexts/mobile-menu-context';
7
6
  import {
8
7
  Search,
9
8
  Plus,
@@ -97,7 +96,6 @@ const DEFAULT_COLUMNS: TableColumn[] = [
97
96
  export default function ContactsPage() {
98
97
  const router = useRouter();
99
98
  const { isAdmin } = useUserRole();
100
- const { toggle: toggleMobileMenu, isOpen: isMobileMenuOpen } = useMobileMenuContext();
101
99
 
102
100
  // Fonction pour formater les dates en français
103
101
  const formatDate = (dateString: string) => {
@@ -1400,42 +1398,17 @@ export default function ContactsPage() {
1400
1398
  return (
1401
1399
  <div className="bg-crms-bg flex h-full flex-col">
1402
1400
  {/* Header avec titre, badge et breadcrumbs */}
1403
- <div className="border-b border-gray-200 bg-white px-4 py-4 sm:px-6 lg:px-8">
1404
- <div className="mb-3 flex items-start justify-between gap-3">
1405
- {/* Mobile menu button */}
1406
- <button
1407
- onClick={toggleMobileMenu}
1408
- className="mt-1 shrink-0 cursor-pointer rounded-lg p-2 text-gray-700 transition-colors hover:bg-gray-100 lg:hidden"
1409
- aria-label="Toggle menu"
1410
- >
1411
- <svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1412
- {isMobileMenuOpen ? (
1413
- <path
1414
- strokeLinecap="round"
1415
- strokeLinejoin="round"
1416
- strokeWidth={2}
1417
- d="M6 18L18 6M6 6l12 12"
1418
- />
1419
- ) : (
1420
- <path
1421
- strokeLinecap="round"
1422
- strokeLinejoin="round"
1423
- strokeWidth={2}
1424
- d="M4 6h16M4 12h16M4 18h16"
1425
- />
1426
- )}
1427
- </svg>
1428
- </button>
1429
-
1401
+ <div className="border-b border-gray-200 bg-white px-4 py-3 sm:px-6 sm:py-4 lg:px-8">
1402
+ <div className="mb-3 flex items-center justify-between gap-2 sm:gap-3">
1430
1403
  {/* Titre et breadcrumbs */}
1431
- <div className="flex-1">
1432
- <div className="mb-1 flex items-center gap-2">
1433
- <h1 className="text-2xl font-bold text-gray-900">Contacts</h1>
1434
- <span className="rounded-full bg-indigo-100 px-2.5 py-0.5 text-sm font-semibold text-indigo-600">
1404
+ <div className="min-w-0 flex-1">
1405
+ <div className="mb-0.5 flex items-center gap-2 sm:mb-1">
1406
+ <h1 className="text-xl font-bold text-gray-900 sm:text-2xl">Contacts</h1>
1407
+ <span className="rounded-full bg-indigo-100 px-2 py-0.5 text-xs font-semibold text-indigo-600 sm:px-2.5 sm:text-sm">
1435
1408
  {totalContacts}
1436
1409
  </span>
1437
1410
  </div>
1438
- <p className="text-base text-gray-500">Home &gt; Contacts</p>
1411
+ <p className="text-sm text-gray-500 sm:text-base">Home &gt; Contacts</p>
1439
1412
  </div>
1440
1413
 
1441
1414
  <button
@@ -1666,30 +1639,30 @@ export default function ContactsPage() {
1666
1639
  </div>
1667
1640
 
1668
1641
  {/* Actions à droite */}
1669
- <div className="flex items-center gap-2">
1670
- {/* Gérer les colonnes */}
1642
+ <div className="flex items-center gap-1.5 sm:gap-2">
1643
+ {/* Gérer les colonnes - masqué sur mobile */}
1671
1644
  {viewMode === 'table' && (
1672
1645
  <button
1673
1646
  onClick={() => setShowColumnPanel(true)}
1674
- className="inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50"
1647
+ className="hidden cursor-pointer items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50 sm:inline-flex"
1675
1648
  title="Gérer les colonnes"
1676
1649
  >
1677
- Gérer les colonnes
1650
+ Colonnes
1678
1651
  </button>
1679
1652
  )}
1680
1653
  {/* Bouton Réinitialiser les filtres */}
1681
1654
  {hasActiveFilters() && (
1682
1655
  <button
1683
1656
  onClick={handleResetAllFilters}
1684
- className="inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50"
1657
+ className="inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-2.5 py-2 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50 sm:px-3"
1685
1658
  title="Réinitialiser tous les filtres"
1686
1659
  >
1687
1660
  <X className="h-3.5 w-3.5" />
1688
- Réinitialiser
1661
+ <span className="hidden sm:inline">Réinitialiser</span>
1689
1662
  </button>
1690
1663
  )}
1691
- {/* Groupe vue (liste / grille) */}
1692
- <div className="flex items-center rounded-lg border border-gray-200 bg-white p-1">
1664
+ {/* Groupe vue (liste / grille) - masqué sur mobile (cartes forcées) */}
1665
+ <div className="hidden items-center rounded-lg border border-gray-200 bg-white p-1 sm:flex">
1693
1666
  <button
1694
1667
  onClick={() => setViewMode('table')}
1695
1668
  className={cn(
@@ -1716,10 +1689,11 @@ export default function ContactsPage() {
1716
1689
  <button
1717
1690
  type="button"
1718
1691
  onClick={() => setShowImportModal(true)}
1719
- className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-medium text-gray-700 shadow-sm transition-colors hover:bg-gray-50 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none sm:text-sm"
1692
+ className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-gray-200 bg-white px-2.5 py-2 text-xs font-medium text-gray-700 shadow-sm transition-colors hover:bg-gray-50 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none sm:px-3 sm:text-sm"
1693
+ title="Importer"
1720
1694
  >
1721
1695
  <Upload className="h-4 w-4" />
1722
- Importer
1696
+ <span className="hidden sm:inline">Importer</span>
1723
1697
  </button>
1724
1698
 
1725
1699
  {/* Exporter tous les contacts (admin uniquement) */}
@@ -1730,20 +1704,22 @@ export default function ContactsPage() {
1730
1704
  setShowExportModal(true);
1731
1705
  }}
1732
1706
  disabled={exporting}
1733
- className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-indigo-600 bg-white px-4 py-2 text-xs font-semibold text-indigo-600 shadow-sm transition-colors hover:bg-indigo-50 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm"
1707
+ className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-indigo-600 bg-white px-2.5 py-2 text-xs font-semibold text-indigo-600 shadow-sm transition-colors hover:bg-indigo-50 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 sm:px-4 sm:text-sm"
1708
+ title="Exporter tous"
1734
1709
  >
1735
1710
  <Download className="h-4 w-4" />
1736
- Exporter tous
1711
+ <span className="hidden sm:inline">Exporter</span>
1737
1712
  </button>
1738
1713
  )}
1739
1714
 
1740
1715
  {/* Ajouter un contact */}
1741
1716
  <button
1742
1717
  onClick={handleNewContact}
1743
- className="inline-flex cursor-pointer items-center gap-2 rounded-lg bg-indigo-600 px-4 py-2 text-xs font-semibold text-white shadow-sm transition-colors hover:bg-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none sm:text-sm"
1718
+ className="inline-flex cursor-pointer items-center gap-2 rounded-lg bg-indigo-600 px-2.5 py-2 text-xs font-semibold text-white shadow-sm transition-colors hover:bg-indigo-700 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:outline-none sm:px-4 sm:text-sm"
1719
+ title="Ajouter un contact"
1744
1720
  >
1745
1721
  <Plus className="h-4 w-4" />
1746
- Ajouter un contact
1722
+ <span className="hidden sm:inline">Ajouter</span>
1747
1723
  </button>
1748
1724
  </div>
1749
1725
  </div>
@@ -1758,56 +1734,58 @@ export default function ContactsPage() {
1758
1734
 
1759
1735
  {/* Barre d'actions groupées */}
1760
1736
  {selectedContactIds.size > 0 && (
1761
- <div className="mb-4 flex items-center justify-between rounded-lg border border-indigo-200 bg-indigo-50 p-4 shadow-sm">
1762
- <div className="flex items-center gap-4">
1763
- <span className="text-sm font-medium text-indigo-900">
1764
- {selectedContactIds.size} contact(s) sélectionné(s)
1765
- </span>
1766
- <button
1767
- onClick={handleDeselectAll}
1768
- className="cursor-pointer text-sm text-indigo-600 hover:text-indigo-700 hover:underline"
1769
- >
1770
- Désélectionner tout
1771
- </button>
1772
- </div>
1773
- <div className="flex items-center gap-2">
1774
- <button
1775
- onClick={() => setShowBulkCommercialModal(true)}
1776
- className="flex cursor-pointer items-center gap-2 rounded-lg bg-orange-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-orange-700"
1777
- >
1778
- <Users className="h-4 w-4" />
1779
- Changer commercial
1780
- </button>
1781
- <button
1782
- onClick={() => setShowBulkStatusModal(true)}
1783
- className="flex cursor-pointer items-center gap-2 rounded-lg bg-green-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700"
1784
- >
1785
- <Tag className="h-4 w-4" />
1786
- Changer statut
1787
- </button>
1788
- {isAdmin && (
1789
- <>
1790
- <button
1791
- onClick={() => {
1792
- setExportAll(false);
1793
- setShowExportModal(true);
1794
- }}
1795
- disabled={bulkActionLoading || exporting}
1796
- className="flex cursor-pointer items-center gap-2 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"
1797
- >
1798
- <Download className="h-4 w-4" />
1799
- Exporter
1800
- </button>
1801
- <button
1802
- onClick={handleBulkDelete}
1803
- disabled={bulkActionLoading}
1804
- className="flex cursor-pointer items-center gap-2 rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50"
1805
- >
1806
- <Trash2 className="h-4 w-4" />
1807
- Supprimer
1808
- </button>
1809
- </>
1810
- )}
1737
+ <div className="mb-4 rounded-lg border border-indigo-200 bg-indigo-50 p-3 shadow-sm sm:p-4">
1738
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
1739
+ <div className="flex items-center gap-3 sm:gap-4">
1740
+ <span className="text-sm font-medium whitespace-nowrap text-indigo-900">
1741
+ {selectedContactIds.size} sélectionné(s)
1742
+ </span>
1743
+ <button
1744
+ onClick={handleDeselectAll}
1745
+ className="cursor-pointer text-sm text-indigo-600 hover:text-indigo-700 hover:underline"
1746
+ >
1747
+ Désélectionner
1748
+ </button>
1749
+ </div>
1750
+ <div className="flex items-center gap-2 overflow-x-auto pb-1 sm:pb-0">
1751
+ <button
1752
+ onClick={() => setShowBulkCommercialModal(true)}
1753
+ className="flex shrink-0 cursor-pointer items-center gap-2 rounded-lg bg-orange-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-orange-700 sm:px-4 sm:text-sm"
1754
+ >
1755
+ <Users className="h-4 w-4" />
1756
+ <span className="hidden sm:inline">Changer</span> commercial
1757
+ </button>
1758
+ <button
1759
+ onClick={() => setShowBulkStatusModal(true)}
1760
+ className="flex shrink-0 cursor-pointer items-center gap-2 rounded-lg bg-green-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-green-700 sm:px-4 sm:text-sm"
1761
+ >
1762
+ <Tag className="h-4 w-4" />
1763
+ <span className="hidden sm:inline">Changer</span> statut
1764
+ </button>
1765
+ {isAdmin && (
1766
+ <>
1767
+ <button
1768
+ onClick={() => {
1769
+ setExportAll(false);
1770
+ setShowExportModal(true);
1771
+ }}
1772
+ disabled={bulkActionLoading || exporting}
1773
+ className="flex shrink-0 cursor-pointer items-center gap-2 rounded-lg bg-blue-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50 sm:px-4 sm:text-sm"
1774
+ >
1775
+ <Download className="h-4 w-4" />
1776
+ Exporter
1777
+ </button>
1778
+ <button
1779
+ onClick={handleBulkDelete}
1780
+ disabled={bulkActionLoading}
1781
+ className="flex shrink-0 cursor-pointer items-center gap-2 rounded-lg bg-indigo-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50 sm:px-4 sm:text-sm"
1782
+ >
1783
+ <Trash2 className="h-4 w-4" />
1784
+ Supprimer
1785
+ </button>
1786
+ </>
1787
+ )}
1788
+ </div>
1811
1789
  </div>
1812
1790
  </div>
1813
1791
  )}
@@ -1841,9 +1819,9 @@ export default function ContactsPage() {
1841
1819
  </div>
1842
1820
  ) : (
1843
1821
  <>
1844
- {/* Vue Tableau */}
1822
+ {/* Vue Tableau — masquée sur mobile, cartes affichées à la place */}
1845
1823
  {viewMode === 'table' && (
1846
- <div className="overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm">
1824
+ <div className="hidden overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm sm:block">
1847
1825
  <div className="overflow-x-auto">
1848
1826
  <table className="min-w-full divide-y divide-gray-100 text-sm">
1849
1827
  <thead className="bg-gray-50/60">
@@ -1965,7 +1943,7 @@ export default function ContactsPage() {
1965
1943
  </div>
1966
1944
  )}
1967
1945
 
1968
- {/* Vue Cartes */}
1946
+ {/* Vue Cartes — toujours visible sur mobile, ou quand viewMode === 'cards' sur desktop */}
1969
1947
  {viewMode === 'cards' ? (
1970
1948
  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
1971
1949
  {sortedContacts.map((contact) => {
@@ -1973,12 +1951,12 @@ export default function ContactsPage() {
1973
1951
  <div
1974
1952
  key={contact.id}
1975
1953
  onClick={() => router.push(`/contacts/${contact.id}`)}
1976
- className="relative cursor-pointer rounded-lg border border-gray-200 bg-white p-6 shadow-sm transition-all hover:border-indigo-300 hover:shadow-md"
1954
+ className="relative cursor-pointer rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-all hover:border-indigo-300 hover:shadow-md sm:p-6"
1977
1955
  >
1978
- {/* En-tête avec trois points */}
1979
- <div className="mb-4 flex items-start justify-between">
1956
+ {/* En-tête */}
1957
+ <div className="mb-3 flex items-start justify-between sm:mb-4">
1980
1958
  <div className="flex items-center gap-3">
1981
- <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-indigo-100 text-lg font-semibold text-indigo-600">
1959
+ <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-indigo-100 text-base font-semibold text-indigo-600 sm:h-12 sm:w-12 sm:text-lg">
1982
1960
  {contact.isCompany ? (
1983
1961
  <span>🏢</span>
1984
1962
  ) : (
@@ -1986,12 +1964,11 @@ export default function ContactsPage() {
1986
1964
  )}
1987
1965
  </div>
1988
1966
  <div className="min-w-0">
1989
- <h3 className="text-lg font-semibold text-gray-900">
1967
+ <h3 className="truncate text-base font-semibold text-gray-900 sm:text-lg">
1990
1968
  {contact.civility && `${contact.civility}. `}
1991
1969
  {contact.firstName} {contact.lastName}
1992
1970
  </h3>
1993
- <p className="text-sm text-gray-500">
1994
- CRÉÉ LE :{' '}
1971
+ <p className="text-xs text-gray-500 sm:text-sm">
1995
1972
  {new Date(contact.createdAt).toLocaleDateString('fr-FR', {
1996
1973
  day: 'numeric',
1997
1974
  month: 'short',
@@ -2003,58 +1980,32 @@ export default function ContactsPage() {
2003
1980
  </div>
2004
1981
 
2005
1982
  {/* Informations de contact */}
2006
- <div className="mb-4 space-y-2">
2007
- <div className="flex items-center text-base text-gray-900">
2008
- <Phone className="mr-2 h-4 w-4 text-gray-400" />
2009
- <span className="font-medium">Tél. :</span>
2010
- <span className="ml-1">{contact.phone}</span>
2011
- </div>
2012
- {contact.secondaryPhone && (
2013
- <div className="text-sm text-gray-500">
2014
- Tél. secondaire : {contact.secondaryPhone}
2015
- </div>
2016
- )}
2017
- <div className="flex items-center text-base text-gray-900">
2018
- <Mail className="mr-2 h-4 w-4 text-gray-400" />
2019
- <span className="font-medium">Email :</span>
2020
- <span className="ml-1">{contact.email || '-'}</span>
2021
- </div>
2022
- {(contact.companyName || contact.companyRelation) && (
2023
- <div className="flex items-center text-base text-gray-900">
2024
- <Building2 className="mr-2 h-4 w-4 text-gray-400" />
2025
- <span>
2026
- {contact.companyName ||
2027
- (contact.companyRelation &&
2028
- (contact.companyRelation.firstName ||
2029
- contact.companyRelation.lastName ||
2030
- 'Sans nom'))}
2031
- </span>
1983
+ <div className="mb-3 space-y-1.5 sm:mb-4 sm:space-y-2">
1984
+ {contact.phone && (
1985
+ <div className="flex items-center text-sm text-gray-900 sm:text-base">
1986
+ <Phone className="mr-2 h-3.5 w-3.5 shrink-0 text-gray-400 sm:h-4 sm:w-4" />
1987
+ <span className="truncate">{contact.phone}</span>
2032
1988
  </div>
2033
1989
  )}
2034
- {contact.city && (
2035
- <div className="flex items-center text-base text-gray-500">
2036
- <MapPin className="mr-2 h-4 w-4 text-gray-400" />
2037
- <span className="font-medium">Localisation :</span>
2038
- <span className="ml-1">
2039
- {contact.city}
2040
- {contact.postalCode && `, ${contact.postalCode}`}
2041
- </span>
1990
+ {contact.email && (
1991
+ <div className="flex items-center text-sm text-gray-900 sm:text-base">
1992
+ <Mail className="mr-2 h-3.5 w-3.5 shrink-0 text-gray-400 sm:h-4 sm:w-4" />
1993
+ <span className="truncate">{contact.email}</span>
2042
1994
  </div>
2043
1995
  )}
2044
1996
  {contact.origin && (
2045
- <div className="flex items-center text-base text-gray-500">
2046
- <span className="mr-2 text-sm">🎯</span>
2047
- <span className="font-medium">Origine :</span>
2048
- <span className="ml-1">{contact.origin}</span>
1997
+ <div className="flex items-center text-xs text-gray-500 sm:text-sm">
1998
+ <span className="mr-2 text-xs sm:text-sm">🎯</span>
1999
+ <span className="truncate">{contact.origin}</span>
2049
2000
  </div>
2050
2001
  )}
2051
2002
  </div>
2052
2003
 
2053
2004
  {/* Badges et statut */}
2054
- <div className="mb-4 flex flex-wrap items-center gap-2">
2005
+ <div className="flex flex-wrap items-center gap-1.5 sm:gap-2">
2055
2006
  {contact.status && (
2056
2007
  <span
2057
- className="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold"
2008
+ className="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-semibold"
2058
2009
  style={{
2059
2010
  backgroundColor: `${contact.status.color}20`,
2060
2011
  color: contact.status.color,
@@ -2063,80 +2014,75 @@ export default function ContactsPage() {
2063
2014
  {contact.status.name}
2064
2015
  </span>
2065
2016
  )}
2066
- {contact.isCompany && (
2067
- <span className="inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-semibold text-blue-800">
2068
- Entreprise
2069
- </span>
2070
- )}
2071
2017
  {!contact.assignedCommercial && (
2072
- <span className="inline-flex items-center rounded-full border border-orange-300 bg-orange-50 px-2.5 py-0.5 text-xs font-semibold text-orange-800">
2018
+ <span className="inline-flex items-center rounded-full border border-orange-300 bg-orange-50 px-2 py-0.5 text-xs font-semibold text-orange-800">
2073
2019
  Non Attribué
2074
2020
  </span>
2075
2021
  )}
2076
2022
  </div>
2023
+ </div>
2024
+ );
2025
+ })}
2026
+ </div>
2027
+ ) : null}
2077
2028
 
2078
- {/* Pied de carte avec utilisateurs assignés */}
2079
- <div className="flex items-start justify-between border-t border-gray-100 pt-4">
2080
- <div className="space-y-2">
2081
- {/* Commercial */}
2082
- {contact.assignedCommercial && (
2083
- <div className="flex items-center gap-2">
2084
- <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-indigo-100 text-xs font-semibold text-indigo-600">
2085
- {contact.assignedCommercial.name
2086
- .split(' ')
2087
- .map((n) => n[0])
2088
- .join('')
2089
- .slice(0, 2)
2090
- .toUpperCase()}
2091
- </div>
2092
- <div className="min-w-0 flex-1">
2093
- <div className="flex items-center gap-1.5">
2094
- <span className="text-xs font-medium text-gray-900">
2095
- {contact.assignedCommercial.name}
2096
- </span>
2097
- <span className="inline-flex items-center rounded-full bg-blue-100 px-1.5 py-0.5 text-[10px] font-medium text-blue-700">
2098
- Commercial
2099
- </span>
2100
- </div>
2101
- </div>
2102
- </div>
2103
- )}
2104
-
2105
- {/* Télépro */}
2106
- {contact.assignedTelepro && (
2107
- <div className="flex items-center gap-2">
2108
- <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-purple-100 text-xs font-semibold text-purple-600">
2109
- {contact.assignedTelepro.name
2110
- .split(' ')
2111
- .map((n) => n[0])
2112
- .join('')
2113
- .slice(0, 2)
2114
- .toUpperCase()}
2115
- </div>
2116
- <div className="min-w-0 flex-1">
2117
- <div className="flex items-center gap-1.5">
2118
- <span className="text-xs font-medium text-gray-900">
2119
- {contact.assignedTelepro.name}
2120
- </span>
2121
- <span className="inline-flex items-center rounded-full bg-purple-100 px-1.5 py-0.5 text-[10px] font-medium text-purple-700">
2122
- Télépro
2123
- </span>
2124
- </div>
2125
- </div>
2126
- </div>
2029
+ {/* Fallback mobile : vue cartes compactes quand viewMode est table mais écran < sm */}
2030
+ {viewMode === 'table' && (
2031
+ <div className="grid grid-cols-1 gap-3 sm:hidden">
2032
+ {sortedContacts.map((contact) => (
2033
+ <div
2034
+ key={contact.id}
2035
+ onClick={() => router.push(`/contacts/${contact.id}`)}
2036
+ className="relative cursor-pointer rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-all hover:border-indigo-300 hover:shadow-md"
2037
+ >
2038
+ <div className="flex items-center gap-3">
2039
+ <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-indigo-100 text-base font-semibold text-indigo-600">
2040
+ {contact.isCompany ? (
2041
+ <span>🏢</span>
2042
+ ) : (
2043
+ (contact.firstName?.[0] || contact.lastName?.[0] || '?').toUpperCase()
2044
+ )}
2045
+ </div>
2046
+ <div className="min-w-0 flex-1">
2047
+ <h3 className="truncate text-sm font-semibold text-gray-900">
2048
+ {contact.firstName} {contact.lastName}
2049
+ </h3>
2050
+ <div className="mt-0.5 flex items-center gap-2">
2051
+ {contact.status && (
2052
+ <span
2053
+ className="inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-semibold"
2054
+ style={{
2055
+ backgroundColor: `${contact.status.color}20`,
2056
+ color: contact.status.color,
2057
+ }}
2058
+ >
2059
+ {contact.status.name}
2060
+ </span>
2127
2061
  )}
2128
-
2129
- {/* Aucun utilisateur assigné */}
2130
- {!contact.assignedCommercial && !contact.assignedTelepro && (
2131
- <div className="text-xs text-gray-400">Aucun utilisateur assigné</div>
2062
+ {contact.origin && (
2063
+ <span className="truncate text-xs text-gray-400">{contact.origin}</span>
2132
2064
  )}
2133
2065
  </div>
2134
2066
  </div>
2135
2067
  </div>
2136
- );
2137
- })}
2068
+ <div className="mt-2 space-y-1 pl-[52px]">
2069
+ {contact.email && (
2070
+ <p className="truncate text-xs text-gray-500">
2071
+ <Mail className="mr-1 inline h-3 w-3" />
2072
+ {contact.email}
2073
+ </p>
2074
+ )}
2075
+ {contact.phone && (
2076
+ <p className="text-xs text-gray-500">
2077
+ <Phone className="mr-1 inline h-3 w-3" />
2078
+ {contact.phone}
2079
+ </p>
2080
+ )}
2081
+ </div>
2082
+ </div>
2083
+ ))}
2138
2084
  </div>
2139
- ) : null}
2085
+ )}
2140
2086
 
2141
2087
  {/* Pagination */}
2142
2088
  {totalPages > 1 && (