mautourco-components 0.1.1

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 (163) hide show
  1. package/README.md +187 -0
  2. package/dist/components/atoms/Avatar/Avatar.d.ts +14 -0
  3. package/dist/components/atoms/Avatar/Avatar.js +31 -0
  4. package/dist/components/atoms/Button/Button.d.ts +27 -0
  5. package/dist/components/atoms/Button/Button.js +35 -0
  6. package/dist/components/atoms/Checkbox/Checkbox.d.ts +13 -0
  7. package/dist/components/atoms/Checkbox/Checkbox.js +33 -0
  8. package/dist/components/atoms/Icon/Icon.d.ts +10 -0
  9. package/dist/components/atoms/Icon/Icon.js +102 -0
  10. package/dist/components/atoms/Icon/icons/ArrivalIcon.d.ts +8 -0
  11. package/dist/components/atoms/Icon/icons/ArrivalIcon.js +31 -0
  12. package/dist/components/atoms/Icon/icons/CalendarIcon.d.ts +12 -0
  13. package/dist/components/atoms/Icon/icons/CalendarIcon.js +41 -0
  14. package/dist/components/atoms/Icon/icons/CarIcon.d.ts +8 -0
  15. package/dist/components/atoms/Icon/icons/CarIcon.js +30 -0
  16. package/dist/components/atoms/Icon/icons/Check.d.ts +8 -0
  17. package/dist/components/atoms/Icon/icons/Check.js +30 -0
  18. package/dist/components/atoms/Icon/icons/CheckCircleIcon.d.ts +8 -0
  19. package/dist/components/atoms/Icon/icons/CheckCircleIcon.js +30 -0
  20. package/dist/components/atoms/Icon/icons/Chevron.d.ts +9 -0
  21. package/dist/components/atoms/Icon/icons/Chevron.js +54 -0
  22. package/dist/components/atoms/Icon/icons/ChevronDownIcon.d.ts +8 -0
  23. package/dist/components/atoms/Icon/icons/ChevronDownIcon.js +30 -0
  24. package/dist/components/atoms/Icon/icons/Close.d.ts +8 -0
  25. package/dist/components/atoms/Icon/icons/Close.js +30 -0
  26. package/dist/components/atoms/Icon/icons/DeleteIcon.d.ts +8 -0
  27. package/dist/components/atoms/Icon/icons/DeleteIcon.js +30 -0
  28. package/dist/components/atoms/Icon/icons/DepartureIcon.d.ts +8 -0
  29. package/dist/components/atoms/Icon/icons/DepartureIcon.js +30 -0
  30. package/dist/components/atoms/Icon/icons/EyeIcon.d.ts +8 -0
  31. package/dist/components/atoms/Icon/icons/EyeIcon.js +30 -0
  32. package/dist/components/atoms/Icon/icons/FacebookIcon.d.ts +8 -0
  33. package/dist/components/atoms/Icon/icons/FacebookIcon.js +36 -0
  34. package/dist/components/atoms/Icon/icons/InfoIcon.d.ts +8 -0
  35. package/dist/components/atoms/Icon/icons/InfoIcon.js +30 -0
  36. package/dist/components/atoms/Icon/icons/LinkedInIcon.d.ts +8 -0
  37. package/dist/components/atoms/Icon/icons/LinkedInIcon.js +36 -0
  38. package/dist/components/atoms/Icon/icons/MapPinIcon.d.ts +8 -0
  39. package/dist/components/atoms/Icon/icons/MapPinIcon.js +30 -0
  40. package/dist/components/atoms/Icon/icons/MautoucoLogo.d.ts +8 -0
  41. package/dist/components/atoms/Icon/icons/MautoucoLogo.js +37 -0
  42. package/dist/components/atoms/Icon/icons/MenuIcon.d.ts +8 -0
  43. package/dist/components/atoms/Icon/icons/MenuIcon.js +37 -0
  44. package/dist/components/atoms/Icon/icons/MoreIcon.d.ts +8 -0
  45. package/dist/components/atoms/Icon/icons/MoreIcon.js +30 -0
  46. package/dist/components/atoms/Icon/icons/Search.d.ts +8 -0
  47. package/dist/components/atoms/Icon/icons/Search.js +30 -0
  48. package/dist/components/atoms/Icon/icons/Settings.d.ts +8 -0
  49. package/dist/components/atoms/Icon/icons/Settings.js +30 -0
  50. package/dist/components/atoms/Icon/icons/StrollerIcon.d.ts +8 -0
  51. package/dist/components/atoms/Icon/icons/StrollerIcon.js +30 -0
  52. package/dist/components/atoms/Icon/icons/TwitterIcon.d.ts +8 -0
  53. package/dist/components/atoms/Icon/icons/TwitterIcon.js +36 -0
  54. package/dist/components/atoms/Icon/icons/User.d.ts +8 -0
  55. package/dist/components/atoms/Icon/icons/User.js +30 -0
  56. package/dist/components/atoms/Icon/icons/UserIcon.d.ts +12 -0
  57. package/dist/components/atoms/Icon/icons/UserIcon.js +41 -0
  58. package/dist/components/atoms/Icon/icons/Youtube.d.ts +8 -0
  59. package/dist/components/atoms/Icon/icons/Youtube.js +36 -0
  60. package/dist/components/atoms/Inputs/DropdownInput/DropdownInput.d.ts +12 -0
  61. package/dist/components/atoms/Inputs/DropdownInput/DropdownInput.js +53 -0
  62. package/dist/components/atoms/Inputs/Input/Input.d.ts +15 -0
  63. package/dist/components/atoms/Inputs/Input/Input.js +27 -0
  64. package/dist/components/atoms/Inputs/Textarea/Textarea.d.ts +14 -0
  65. package/dist/components/atoms/Inputs/Textarea/Textarea.js +15 -0
  66. package/dist/components/atoms/Link/Link.d.ts +44 -0
  67. package/dist/components/atoms/Link/Link.js +76 -0
  68. package/dist/components/atoms/SelectedValue/SelectedValue.d.ts +11 -0
  69. package/dist/components/atoms/SelectedValue/SelectedValue.js +29 -0
  70. package/dist/components/atoms/Spinner/Spinner.d.ts +9 -0
  71. package/dist/components/atoms/Spinner/Spinner.js +38 -0
  72. package/dist/components/atoms/Spinner/variants/ButtonSpinner.d.ts +8 -0
  73. package/dist/components/atoms/Spinner/variants/ButtonSpinner.js +19 -0
  74. package/dist/components/atoms/Spinner/variants/LoadingSpinner.d.ts +7 -0
  75. package/dist/components/atoms/Spinner/variants/LoadingSpinner.js +7 -0
  76. package/dist/components/atoms/Tab/Tab.d.ts +22 -0
  77. package/dist/components/atoms/Tab/Tab.js +54 -0
  78. package/dist/components/atoms/Typography/Heading/Heading.d.ts +9 -0
  79. package/dist/components/atoms/Typography/Heading/Heading.js +25 -0
  80. package/dist/components/atoms/Typography/Text/Text.d.ts +10 -0
  81. package/dist/components/atoms/Typography/Text/Text.js +77 -0
  82. package/dist/components/atoms/Typography/Typography.d.ts +24 -0
  83. package/dist/components/atoms/Typography/Typography.js +100 -0
  84. package/dist/components/molecules/MultiSelectDropdown/MultiSelectDropdown.d.ts +29 -0
  85. package/dist/components/molecules/MultiSelectDropdown/MultiSelectDropdown.js +106 -0
  86. package/dist/components/molecules/UserCard/UserCard.d.ts +20 -0
  87. package/dist/components/molecules/UserCard/UserCard.js +57 -0
  88. package/dist/components/organisms/Footer/Footer.d.ts +38 -0
  89. package/dist/components/organisms/Footer/Footer.js +74 -0
  90. package/dist/components/organisms/TopNavigation/DesktopNav.d.ts +33 -0
  91. package/dist/components/organisms/TopNavigation/DesktopNav.js +26 -0
  92. package/dist/components/organisms/TopNavigation/MobileNav.d.ts +32 -0
  93. package/dist/components/organisms/TopNavigation/MobileNav.js +45 -0
  94. package/dist/components/organisms/TopNavigation/TopNavigation.d.ts +33 -0
  95. package/dist/components/organisms/TopNavigation/TopNavigation.js +20 -0
  96. package/dist/hooks/useMobile.d.ts +5 -0
  97. package/dist/hooks/useMobile.js +26 -0
  98. package/dist/index.d.ts +23 -0
  99. package/dist/index.js +28 -0
  100. package/dist/styles/tokens/tokens.d.ts +3108 -0
  101. package/dist/styles/tokens/tokens.js +2652 -0
  102. package/package.json +90 -0
  103. package/src/components/atoms/Avatar/Avatar.tsx +60 -0
  104. package/src/components/atoms/Button/Button.css +200 -0
  105. package/src/components/atoms/Button/Button.tsx +82 -0
  106. package/src/components/atoms/Checkbox/Checkbox.tsx +69 -0
  107. package/src/components/atoms/Icon/Icon.tsx +135 -0
  108. package/src/components/atoms/Icon/icons/ArrivalIcon.tsx +52 -0
  109. package/src/components/atoms/Icon/icons/CalendarIcon.tsx +63 -0
  110. package/src/components/atoms/Icon/icons/CarIcon.tsx +44 -0
  111. package/src/components/atoms/Icon/icons/Check.tsx +36 -0
  112. package/src/components/atoms/Icon/icons/CheckCircleIcon.tsx +48 -0
  113. package/src/components/atoms/Icon/icons/Chevron.tsx +73 -0
  114. package/src/components/atoms/Icon/icons/ChevronDownIcon.tsx +46 -0
  115. package/src/components/atoms/Icon/icons/Close.tsx +40 -0
  116. package/src/components/atoms/Icon/icons/DeleteIcon.tsx +44 -0
  117. package/src/components/atoms/Icon/icons/DepartureIcon.tsx +50 -0
  118. package/src/components/atoms/Icon/icons/EyeIcon.tsx +44 -0
  119. package/src/components/atoms/Icon/icons/FacebookIcon.tsx +50 -0
  120. package/src/components/atoms/Icon/icons/InfoIcon.tsx +44 -0
  121. package/src/components/atoms/Icon/icons/LinkedInIcon.tsx +50 -0
  122. package/src/components/atoms/Icon/icons/MapPinIcon.tsx +44 -0
  123. package/src/components/atoms/Icon/icons/MautoucoLogo.tsx +93 -0
  124. package/src/components/atoms/Icon/icons/MenuIcon.tsx +49 -0
  125. package/src/components/atoms/Icon/icons/MoreIcon.tsx +44 -0
  126. package/src/components/atoms/Icon/icons/Search.tsx +37 -0
  127. package/src/components/atoms/Icon/icons/Settings.tsx +38 -0
  128. package/src/components/atoms/Icon/icons/StrollerIcon.tsx +44 -0
  129. package/src/components/atoms/Icon/icons/TwitterIcon.tsx +50 -0
  130. package/src/components/atoms/Icon/icons/User.tsx +37 -0
  131. package/src/components/atoms/Icon/icons/UserIcon.tsx +63 -0
  132. package/src/components/atoms/Icon/icons/Youtube.tsx +50 -0
  133. package/src/components/atoms/Inputs/DropdownInput/DropdownInput.tsx +96 -0
  134. package/src/components/atoms/Inputs/Input/Input.tsx +66 -0
  135. package/src/components/atoms/Inputs/Textarea/Textarea.tsx +51 -0
  136. package/src/components/atoms/Link/Link.tsx +168 -0
  137. package/src/components/atoms/SelectedValue/SelectedValue.tsx +59 -0
  138. package/src/components/atoms/Spinner/Spinner.tsx +56 -0
  139. package/src/components/atoms/Spinner/variants/ButtonSpinner.tsx +37 -0
  140. package/src/components/atoms/Spinner/variants/LoadingSpinner.tsx +22 -0
  141. package/src/components/atoms/Tab/Tab.css +147 -0
  142. package/src/components/atoms/Tab/Tab.tsx +96 -0
  143. package/src/components/atoms/Typography/Typography.tsx +153 -0
  144. package/src/components/molecules/MultiSelectDropdown/MultiSelectDropdown.tsx +245 -0
  145. package/src/components/molecules/UserCard/UserCard.stories.tsx +36 -0
  146. package/src/components/molecules/UserCard/UserCard.tsx +173 -0
  147. package/src/components/organisms/Footer/Footer.tsx +290 -0
  148. package/src/components/organisms/TopNavigation/DesktopNav.tsx +122 -0
  149. package/src/components/organisms/TopNavigation/MobileNav.tsx +212 -0
  150. package/src/components/organisms/TopNavigation/TopNavigation.tsx +45 -0
  151. package/src/styles/components/avatar.css +58 -0
  152. package/src/styles/components/checkbox.css +132 -0
  153. package/src/styles/components/dropdown.css +214 -0
  154. package/src/styles/components/forms.css +147 -0
  155. package/src/styles/components/multiselect-dropdown.css +231 -0
  156. package/src/styles/components/organism/footer.css +113 -0
  157. package/src/styles/components/organism/topnavigation.css +162 -0
  158. package/src/styles/components/scrollbar.css +63 -0
  159. package/src/styles/components/selected-value.css +80 -0
  160. package/src/styles/components/typography.css +251 -0
  161. package/src/styles/tokens/_tokens.scss +2072 -0
  162. package/src/styles/tokens/tokens.css +2075 -0
  163. package/src/styles/tokens/tokens.js +2653 -0
@@ -0,0 +1,173 @@
1
+ import React, { useEffect, useMemo, useRef, useState } from "react";
2
+ import { Avatar } from "../../atoms/Avatar/Avatar";
3
+
4
+ type User = {
5
+ name: string;
6
+ agency: string;
7
+ isAdmin: boolean;
8
+ };
9
+
10
+ type Agency = {
11
+ id: number;
12
+ name: string;
13
+ localisation: string;
14
+ };
15
+
16
+ export interface UserCardProps {
17
+ user: User;
18
+ agencies: Agency[];
19
+ onSelectAgency?: (agency: Agency) => void;
20
+ selectedAgency?: Agency; // ✅ keep type as Agency
21
+ className?: string;
22
+ }
23
+
24
+ export const UserCard: React.FC<UserCardProps> = ({
25
+ user,
26
+ agencies,
27
+ onSelectAgency,
28
+ selectedAgency,
29
+ className = "",
30
+ }) => {
31
+ const [open, setOpen] = useState(false);
32
+ const [query, setQuery] = useState("");
33
+ const rootRef = useRef<HTMLDivElement>(null);
34
+ const searchRef = useRef<HTMLInputElement>(null);
35
+
36
+ // Close dropdown on outside click
37
+ useEffect(() => {
38
+ if (!user.isAdmin || !open) return;
39
+ const handleClickOutside = (e: MouseEvent) => {
40
+ if (!rootRef.current?.contains(e.target as Node)) setOpen(false);
41
+ };
42
+ document.addEventListener("mousedown", handleClickOutside);
43
+ return () => document.removeEventListener("mousedown", handleClickOutside);
44
+ }, [open, user.isAdmin]);
45
+
46
+ // Auto-focus search input when dropdown opens
47
+ useEffect(() => {
48
+ if (user.isAdmin && open) setTimeout(() => searchRef.current?.focus(), 0);
49
+ }, [open, user.isAdmin]);
50
+
51
+ // Filter agencies based on query
52
+ const filteredAgencies = useMemo(() => {
53
+ const q = query.trim().toLowerCase();
54
+ if (!q) return agencies;
55
+ return agencies.filter(
56
+ (a) =>
57
+ a.name.toLowerCase().includes(q) ||
58
+ a.localisation.toLowerCase().includes(q)
59
+ );
60
+ }, [agencies, query]);
61
+
62
+ const handleSelect = (a: Agency) => {
63
+ onSelectAgency?.(a);
64
+ setOpen(false);
65
+ setQuery("");
66
+ };
67
+
68
+ return (
69
+ <div ref={rootRef} className={`relative inline-flex items-center gap-3 ${className} ${user.isAdmin ? "cursor-pointer rounded-[11px] border border-[#D9D9D9] bg-white px-[13px] py-2" : ""}`}>
70
+ {/* Greeting */}
71
+ <div className="flex items-center gap-3">
72
+ <Avatar name={user.name} size="md" shape="circle" />
73
+ <div className="flex flex-col leading-tight">
74
+ <div className="text-[13px] text-muted-foreground">
75
+ Welcome back,{" "}
76
+ <span className="font-medium text-foreground">{user.name}</span>{" "}
77
+ <span aria-hidden>👋</span>
78
+ </div>
79
+ <div className="text-[13px] font-semibold text-foreground">
80
+ {selectedAgency ? selectedAgency.name : user.agency}
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ {/* Admin-only: Dropdown selector */}
86
+ {user.isAdmin && (
87
+ <>
88
+ <button
89
+ type="button"
90
+ aria-expanded={open}
91
+ aria-haspopup="listbox"
92
+ onClick={() => setOpen((v) => !v)}
93
+ className="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-teal-600 text-white shadow-[0_2px_6px_rgba(0,0,0,0.15)] outline-none focus-visible:ring-2 focus-visible:ring-teal-300"
94
+ title="Open agency selector"
95
+ >
96
+ <svg
97
+ width="18"
98
+ height="18"
99
+ viewBox="0 0 24 24"
100
+ aria-hidden="true"
101
+ className={`transition-transform ${open ? "rotate-180" : ""}`}
102
+ >
103
+ <path
104
+ d="M6 9l6 6 6-6"
105
+ fill="none"
106
+ stroke="currentColor"
107
+ strokeWidth="2"
108
+ strokeLinecap="round"
109
+ strokeLinejoin="round"
110
+ />
111
+ </svg>
112
+ </button>
113
+
114
+ {open && (
115
+ <div className="absolute right-0 top-12 z-50 w-[340px] rounded-2xl border border-black/5 bg-white p-3 shadow-[0_12px_30px_rgba(0,0,0,0.15)]">
116
+ {/* Search */}
117
+ <div className="relative mb-3">
118
+ <input
119
+ ref={searchRef}
120
+ value={query}
121
+ onChange={(e) => setQuery(e.target.value)}
122
+ placeholder="Search"
123
+ className="w-full rounded-xl border border-black/10 bg-white px-10 py-2 text-sm outline-none focus:border-black/20 shadow-[inset_0_1px_2px_rgba(0,0,0,0.06)]"
124
+ />
125
+ <div className="absolute -translate-y-1/2 pointer-events-none left-3 top-1/2">
126
+ <svg width="18" height="18" viewBox="0 0 24 24">
127
+ <path
128
+ d="M21 21l-4.3-4.3M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15z"
129
+ fill="none"
130
+ stroke="currentColor"
131
+ strokeWidth="1.6"
132
+ strokeLinecap="round"
133
+ />
134
+ </svg>
135
+ </div>
136
+ </div>
137
+
138
+ {/* Agency List */}
139
+ <ul role="listbox" className="pr-1 overflow-auto max-h-64">
140
+ {filteredAgencies.map((a) => {
141
+ const isActive = selectedAgency?.id === a.id;
142
+ return (
143
+ <li key={a.id}>
144
+ <button
145
+ type="button"
146
+ onClick={() => handleSelect(a)}
147
+ className={`flex w-full items-center justify-between gap-3 rounded-lg px-2 py-2 text-left hover:bg-black/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-300 ${
148
+ isActive ? "bg-black/[0.04]" : ""
149
+ }`}
150
+ aria-selected={isActive}
151
+ role="option"
152
+ >
153
+ <span className="text-[14px]">{a.name}</span>
154
+ <span className="text-[13px] font-semibold">
155
+ {a.localisation}
156
+ </span>
157
+ </button>
158
+ </li>
159
+ );
160
+ })}
161
+ {filteredAgencies.length === 0 && (
162
+ <li className="px-2 py-6 text-sm text-center text-muted-foreground">
163
+ No results
164
+ </li>
165
+ )}
166
+ </ul>
167
+ </div>
168
+ )}
169
+ </>
170
+ )}
171
+ </div>
172
+ );
173
+ };
@@ -0,0 +1,290 @@
1
+ import React, { useState } from "react";
2
+ import { Text } from "../../atoms/Typography/Typography";
3
+ import { Link } from "../../atoms/Link/Link";
4
+ import Icon from "../../atoms/Icon/Icon";
5
+
6
+ type FooterLink = {
7
+ label: string;
8
+ href?: string;
9
+ onClick?: () => void;
10
+ };
11
+
12
+ type FooterColumn = {
13
+ id: string;
14
+ title: string;
15
+ links: FooterLink[];
16
+ };
17
+
18
+ type SocialLink = {
19
+ id: string;
20
+ icon: React.ReactNode;
21
+ label: string;
22
+ href: string;
23
+ };
24
+
25
+ type ContactBlock = {
26
+ title: string;
27
+ addressLines: string[];
28
+ phone?: string;
29
+ fax?: string;
30
+ email?: string;
31
+ };
32
+
33
+ export interface FooterProps {
34
+ logo?: React.ReactNode; // SVG or img already styled
35
+ columns?: FooterColumn[];
36
+ contact?: ContactBlock;
37
+ socials?: SocialLink[];
38
+ className?: string;
39
+ homeUrl?: string;
40
+ onLinkClick?: (link: { label: string; route: string }) => void;
41
+ }
42
+
43
+ export const Footer: React.FC<FooterProps> = ({
44
+ logo,
45
+ columns = defaultColumns,
46
+ contact = defaultContact,
47
+ socials = defaultSocials,
48
+ className = "",
49
+ homeUrl,
50
+ onLinkClick,
51
+ }) => {
52
+ const [openSection, setOpenSection] = useState<string | null>(columns[0]?.id);
53
+
54
+ const toggleSection = (id: string) => {
55
+ setOpenSection((prev) => (prev === id ? null : id));
56
+ };
57
+
58
+ const year = new Date().getFullYear();
59
+
60
+ return (
61
+ <footer className="footer">
62
+ <div className="footer__wrapper">
63
+ <div className="footer__desktop">
64
+ <div className="footer__top-row">
65
+ <div className="footer__content-left">
66
+ {columns.map((col) => (
67
+ <div key={col.id} className="footer__column">
68
+ <Text
69
+ as="h4"
70
+ className="footer__column-title"
71
+ >
72
+ {col.title}
73
+ </Text>
74
+ <div className="footer__column-links">
75
+ {col.links.map((link) => (
76
+ <Link
77
+ key={link.label}
78
+ href={link.href}
79
+ onClick={link.onClick}
80
+ theme="dark"
81
+ className="text-xs no-underline hover:opacity-80"
82
+ >
83
+ {link.label}
84
+ </Link>
85
+ ))}
86
+ </div>
87
+ </div>
88
+ ))}
89
+
90
+ <div className="footer__contact">
91
+ <Text
92
+ as="h4"
93
+ className="footer__column-title"
94
+ >
95
+ {contact.title}
96
+ </Text>
97
+ <div className="footer__contact-info">
98
+ {contact.addressLines.map((line) => (
99
+ <p key={line}>{line}</p>
100
+ ))}
101
+ {contact.phone && <p>Tel: {contact.phone}</p>}
102
+ {contact.fax && <p>Fax: {contact.fax}</p>}
103
+ {contact.email && (
104
+ <Link
105
+ href={`mailto:${contact.email}`}
106
+ theme="dark"
107
+ className="text-xs no-underline"
108
+ >
109
+ {contact.email}
110
+ </Link>
111
+ )}
112
+ </div>
113
+ </div>
114
+
115
+ <div className="footer__socials">
116
+ <Text
117
+ as="h4"
118
+ className="footer__column-title"
119
+ >
120
+ Socials
121
+ </Text>
122
+ <div className="footer__socials-icons">
123
+ {socials.map((s) => (
124
+ <a
125
+ key={s.id}
126
+ href={s.href}
127
+ aria-label={s.label}
128
+ className="footer__social-link"
129
+ >
130
+ {s.icon}
131
+ </a>
132
+ ))}
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ {logo && (
138
+ <div className="footer__logo-section">
139
+ <div className="footer__logo-container">
140
+ <a
141
+ href={homeUrl ?? "/"}
142
+ onClick={(e) => {
143
+ e.preventDefault();
144
+ onLinkClick?.({ label: "Home", route: homeUrl ?? "/" });
145
+ }}
146
+ >
147
+ {logo}
148
+ </a>
149
+ <Text
150
+ as="p"
151
+ className="footer__copyright"
152
+ >
153
+ © {year} Mautourco, All Rights Reserved
154
+ </Text>
155
+ </div>
156
+ </div>
157
+ )}
158
+ </div>
159
+
160
+ {!logo && (
161
+ <Text as="p" className="footer__copyright-standalone">
162
+ © {year} Mautourco, All Rights Reserved
163
+ </Text>
164
+ )}
165
+ </div>
166
+
167
+ <div className="footer__mobile">
168
+ {columns.map((col) => (
169
+ <div key={col.id} className="footer__mobile-section">
170
+ <button
171
+ type="button"
172
+ onClick={() => toggleSection(col.id)}
173
+ className="footer__mobile-section-button"
174
+ >
175
+ <span>{col.title}</span>
176
+ <span
177
+ className={`footer__mobile-section-icon ${
178
+ openSection === col.id ? "footer__mobile-section-icon--rotated" : ""
179
+ }`}
180
+ >
181
+ <Icon name="chevron-down" size="sm" color="white" />
182
+ </span>
183
+ </button>
184
+ {openSection === col.id && (
185
+ <div className="footer__mobile-section-content">
186
+ {col.links.map((link) => (
187
+ <Link
188
+ key={link.label}
189
+ href={link.href}
190
+ onClick={link.onClick}
191
+ theme="dark"
192
+ className="text-xs no-underline"
193
+ >
194
+ {link.label}
195
+ </Link>
196
+ ))}
197
+ </div>
198
+ )}
199
+ </div>
200
+ ))}
201
+
202
+ <div className="footer__mobile-contact">
203
+ <Text
204
+ as="h4"
205
+ className="footer__mobile-contact-title"
206
+ >
207
+ {contact.title}
208
+ </Text>
209
+ <div className="footer__mobile-contact-info">
210
+ {contact.addressLines.map((line) => (
211
+ <p key={line}>{line}</p>
212
+ ))}
213
+ {contact.phone && <p>Tel: {contact.phone}</p>}
214
+ {contact.fax && <p>Fax: {contact.fax}</p>}
215
+ {contact.email && (
216
+ <Link
217
+ href={`mailto:${contact.email}`}
218
+ theme="dark"
219
+ className="text-xs no-underline"
220
+ >
221
+ {contact.email}
222
+ </Link>
223
+ )}
224
+ </div>
225
+ </div>
226
+
227
+ <div className="footer__mobile-footer">
228
+ <a
229
+ href={homeUrl ?? "/"}
230
+ onClick={(e) => {
231
+ e.preventDefault();
232
+ onLinkClick?.({ label: "Home", route: homeUrl ?? "/" });
233
+ }}
234
+ >
235
+ {logo}
236
+ </a>
237
+ <Text as="p" className="footer__mobile-copyright">
238
+ © {year} Mautourco, All Rights Reserved
239
+ </Text>
240
+ </div>
241
+ </div>
242
+ </div>
243
+ </footer>
244
+ );
245
+ };
246
+
247
+ const defaultColumns: FooterColumn[] = [
248
+ {
249
+ id: "menu-1",
250
+ title: "Menu Title",
251
+ links: [
252
+ { label: "Menu Item", href: "#" },
253
+ { label: "Menu Item", href: "#" },
254
+ { label: "Menu Item", href: "#" },
255
+ { label: "Menu Item", href: "#" },
256
+ { label: "Menu Item", href: "#" },
257
+ { label: "Menu Item", href: "#" },
258
+ ],
259
+ },
260
+ {
261
+ id: "menu-2",
262
+ title: "Menu Title",
263
+ links: [
264
+ { label: "Menu Item", href: "#" },
265
+ { label: "Menu Item", href: "#" },
266
+ { label: "Menu Item", href: "#" },
267
+ { label: "Menu Item", href: "#" },
268
+ { label: "Menu Item", href: "#" },
269
+ { label: "Menu Item", href: "#" },
270
+ ],
271
+ },
272
+ ];
273
+
274
+ const defaultContact: ContactBlock = {
275
+ title: "Contact Us",
276
+ addressLines: [
277
+ "84, Gustave Colin Street,",
278
+ "Forest Side 74414, Mauritius",
279
+ ],
280
+ phone: "+ (230) 604 3000",
281
+ fax: "+ (230) 674 3720",
282
+ email: "info@mautourco.com",
283
+ };
284
+
285
+ const defaultSocials: SocialLink[] = [
286
+ { id: "fb", label: "Facebook", href: "#", icon: <span></span> },
287
+ { id: "ig", label: "Instagram", href: "#", icon: <span></span> },
288
+ { id: "in", label: "LinkedIn", href: "#", icon: <span></span> },
289
+ { id: "yt", label: "YouTube", href: "#", icon: <span></span> },
290
+ ];
@@ -0,0 +1,122 @@
1
+ import Button from "../../atoms/Button/Button";
2
+ import { UserCard } from "../../molecules/UserCard/UserCard";
3
+
4
+ type Link = {
5
+ label: string;
6
+ route: string;
7
+ isButton?: boolean;
8
+ disabled?: boolean;
9
+ };
10
+
11
+ type User = {
12
+ name: string;
13
+ agency: string;
14
+ isAdmin: boolean;
15
+ };
16
+
17
+ type Agency = {
18
+ id: number;
19
+ name: string;
20
+ localisation: string;
21
+ };
22
+
23
+ export interface DesktopNavProps {
24
+ links: Link[];
25
+ onLinkClick?: (link: Link) => void;
26
+ onLogin?: () => void;
27
+ onLogout?: () => void;
28
+ user?: User;
29
+ active?: string;
30
+ logoUrl: string | React.FC<React.SVGProps<SVGSVGElement>>;
31
+ homeUrl?: string;
32
+ selectedAgency?: Agency;
33
+ onAgencyChange?: (agency: Agency) => void;
34
+ agencies?: Agency[];
35
+ className?: string;
36
+ }
37
+
38
+ export const DesktopNav: React.FC<DesktopNavProps> = ({
39
+ links,
40
+ onLinkClick,
41
+ onLogin,
42
+ onLogout,
43
+ user,
44
+ active,
45
+ logoUrl,
46
+ homeUrl,
47
+ selectedAgency,
48
+ onAgencyChange,
49
+ agencies,
50
+ className = "",
51
+ }) => {
52
+ const Logo = typeof logoUrl === "function" ? logoUrl : null;
53
+
54
+ const handleLogoClick = (e: React.MouseEvent) => {
55
+ e.preventDefault();
56
+ onLinkClick?.({ label: "Home", route: homeUrl ?? "/" });
57
+ };
58
+
59
+ return (
60
+ <header className="header">
61
+ <div className="header-logo">
62
+ <a href={homeUrl ?? "/"} onClick={handleLogoClick} className="header-logo__link">
63
+ {Logo ? (
64
+ <Logo className="header-logo__image" />
65
+ ) : (
66
+ <img
67
+ src={logoUrl as string}
68
+ alt="Mautourco"
69
+ className="header-logo__image"
70
+ draggable={false}
71
+ />
72
+ )}
73
+ </a>
74
+ </div>
75
+ <nav className="header-nav">
76
+ {/* Menu desktop */}
77
+ <ul className="header-nav__items">
78
+ {links?.map((it) => {
79
+ const isActive = it.label === active;
80
+
81
+ return (
82
+ <li
83
+ key={it.label}
84
+ className={`header__item ${isActive ? "header__item--active" : ""}`}
85
+ >
86
+ <a
87
+ href={it.route}
88
+ className="header__item__link"
89
+ >
90
+ {it.isButton ? (
91
+ <Button size="sm" variant="outline-secondary">
92
+ {it.label}
93
+ </Button>
94
+ ) : (
95
+ <>{it.label}</>
96
+ )}
97
+ </a>
98
+ </li>
99
+ );
100
+ })}
101
+ </ul>
102
+
103
+ <div className={`header-nav__user-section ${user ? "header-nav__user-connected-section" : ""}`}>
104
+ {user ? (
105
+ <div className="header-nav__user-container">
106
+ <UserCard
107
+ user={user}
108
+ agencies={agencies || []}
109
+ selectedAgency={selectedAgency}
110
+ onSelectAgency={onAgencyChange}
111
+ />
112
+ </div>
113
+ ) : (
114
+ <Button size="sm" variant="secondary" onClick={onLogin}>
115
+ Login
116
+ </Button>
117
+ )}
118
+ </div>
119
+ </nav>
120
+ </header>
121
+ );
122
+ };