|
1 |
+// ----------------------------------------------------------------------------- |
|
2 |
+// Groupe KA — composant partagé (lou-ka / immo-ka) |
|
3 |
+// components/AmenityIco.tsx : icône contextuelle d'une commodité ou inclusion. |
|
4 |
+// Fini l'icône unique répétée dix fois : chaque libellé est associé par |
|
5 |
+// mots-clés (accents neutralisés) à l'un des ~30 pictos maison — trait 1.8, |
|
6 |
+// 24×24, currentColor. Repli : ✓ (confirmé) ou étoile (mention libre). |
|
7 |
+// ----------------------------------------------------------------------------- |
|
8 |
+import { ReactNode } from "react"; |
|
9 |
+ |
|
10 |
+const ICONS: Record<string, ReactNode> = { |
|
11 |
+ heat: (<><rect x="4" y="9" width="16" height="9" rx="2" /><path d="M8 9v9M12 9v9M16 9v9M7.6 3.5c0 1.6 1.2 1.6 1.2 3.2M11.4 3.5c0 1.6 1.2 1.6 1.2 3.2M15.2 3.5c0 1.6 1.2 1.6 1.2 3.2" /></>), |
|
12 |
+ bolt: <path d="M13 2.5 4.5 13.5H11l-1.5 8 8.5-11H11.5l1.5-8z" />, |
|
13 |
+ droplet: (<><path d="M12 3.5S6.2 9.8 6.2 13.6a5.8 5.8 0 0 0 11.6 0C17.8 9.8 12 3.5 12 3.5z" /><path d="M9.4 14a2.7 2.7 0 0 0 2.1 2.6" /></>), |
|
14 |
+ wifi: <path d="M3.5 9.5a13 13 0 0 1 17 0M6.5 13a8.5 8.5 0 0 1 11 0M9.5 16.4a4.4 4.4 0 0 1 5 0M12 19.6h.01" />, |
|
15 |
+ dishwasher: (<><rect x="4.5" y="3" width="15" height="18" rx="2" /><path d="M4.5 8.5h15M8 12.5V17M12 12.5V17M16 12.5V17M7.5 5.7h.01M10.5 5.7h.01" /></>), |
|
16 |
+ washer: (<><rect x="4.5" y="3" width="15" height="18" rx="2" /><circle cx="12" cy="14" r="4.2" /><path d="M4.5 7.5h15M16.4 5.2h.01M9.2 14c1 .8 1.9.8 2.8 0s1.9-.8 2.8 0" /></>), |
|
17 |
+ fridge: (<><rect x="6.5" y="2.5" width="11" height="19" rx="2" /><path d="M6.5 9.5h11M9.5 5.5v2M9.5 12.5V16" /></>), |
|
18 |
+ snow: <path d="M12 2.5v19M3.8 7.25l16.4 9.5M20.2 7.25 3.8 16.75" />, |
|
19 |
+ elevator: (<><rect x="4.5" y="3" width="15" height="18" rx="2" /><path d="M12 3v18M7 11l1.5-1.8L10 11M14 13l1.5 1.8L17 13" /></>), |
|
20 |
+ balcony: <path d="M4 11.5h16M5 11.5V20M19 11.5V20M9.5 11.5V20M14.5 11.5V20M4 20h16M7 11.5V6.5a5 5 0 0 1 10 0v5" />, |
|
21 |
+ pool: <path d="M9 4.5v9M13.5 4.5v9M9 7.5h4.5M3 16.5c1.5 1.3 3 1.3 4.5 0s3-1.3 4.5 0 3 1.3 4.5 0 3-1.3 4.5 0M3 20c1.5 1.3 3 1.3 4.5 0s3-1.3 4.5 0 3 1.3 4.5 0 3-1.3 4.5 0" />, |
|
22 |
+ gym: <path d="M6.7 6.7v10.6M17.3 6.7v10.6M3.5 9.2v5.6M20.5 9.2v5.6M6.7 12h10.6" />, |
|
23 |
+ laundry: <path d="M4 9.5h16l-1.8 10a1.8 1.8 0 0 1-1.8 1.5H7.6a1.8 1.8 0 0 1-1.8-1.5zM8 9.5 12 3l4 6.5M9.3 13.5v3.5M12 13.5v3.5M14.7 13.5v3.5" />, |
|
24 |
+ box: <path d="M3.5 8 12 3.5 20.5 8v8L12 20.5 3.5 16zM3.5 8 12 12.5 20.5 8M12 12.5v8" />, |
|
25 |
+ parking: (<><rect x="3.5" y="3.5" width="17" height="17" rx="3.5" /><path d="M9.5 16.5v-9H13a2.9 2.9 0 0 1 0 5.8H9.5" /></>), |
|
26 |
+ garage: <path d="M3.5 20V9.5L12 4l8.5 5.5V20M7 20v-6.5h10V20M7 16.8h10" />, |
|
27 |
+ sofa: <path d="M5.5 11V8.8A2.8 2.8 0 0 1 8.3 6h7.4a2.8 2.8 0 0 1 2.8 2.8V11M3.5 13.7a2.2 2.2 0 0 1 4.4 0v1h8.2v-1a2.2 2.2 0 0 1 4.4 0V17.5h-17zM5.5 17.5v1.8M18.5 17.5v1.8" />, |
|
28 |
+ nosmoke: <path d="M4 4l16 16M14.5 13H4v3h6.5M17.5 13H20v3h-1" />, |
|
29 |
+ paw: (<><circle cx="8.2" cy="7.8" r="1.7" /><circle cx="15.8" cy="7.8" r="1.7" /><circle cx="4.8" cy="12" r="1.6" /><circle cx="19.2" cy="12" r="1.6" /><path d="M12 11c2.8 0 5.2 2.3 5.2 4.8 0 1.8-1.4 3-3.2 3-.8 0-1.3-.3-2-.3s-1.2.3-2 .3c-1.8 0-3.2-1.2-3.2-3C6.8 13.3 9.2 11 12 11z" /></>), |
|
30 |
+ fire: (<><path d="M4 4.5h16M5.5 4.5V19.5M18.5 4.5V19.5M4 19.5h16" /><path d="M12 8.5c-1.8 2-3 3.4-3 5.3a3 3 0 0 0 6 0c0-1.9-1.2-3.3-3-5.3z" /></>), |
|
31 |
+ tree: <path d="M12 3 7.2 9.5h2.4L5.5 15.5h13L14.4 9.5h2.4zM12 15.5V21" />, |
|
32 |
+ spa: <path d="M8 3.5c0 1.7-1.4 2-1.4 3.7S8 9.5 8 11M12.5 3.5c0 1.7-1.4 2-1.4 3.7s1.4 2.3 1.4 3.8M17 3.5c0 1.7-1.4 2-1.4 3.7S17 9.5 17 11M3.5 15.5c1.4 1.3 2.8 1.3 4.2 0s2.9-1.3 4.3 0 2.8 1.3 4.2 0 2.9-1.3 4.3 0M3.5 19.5c1.4 1.3 2.8 1.3 4.2 0s2.9-1.3 4.3 0 2.8 1.3 4.2 0 2.9-1.3 4.3 0" />, |
|
33 |
+ shield: (<><path d="M12 3 5 6v5.5c0 4.4 3 7.6 7 9.5 4-1.9 7-5.1 7-9.5V6z" /><path d="m9 11.5 2.2 2.2L15.5 9" /></>), |
|
34 |
+ eye: (<><path d="M2.5 12S6.3 5.8 12 5.8 21.5 12 21.5 12 17.7 18.2 12 18.2 2.5 12 2.5 12z" /><circle cx="12" cy="12" r="2.8" /></>), |
|
35 |
+ waves: <path d="M3 9c1.5 1.3 3 1.3 4.5 0s3-1.3 4.5 0 3 1.3 4.5 0 3-1.3 4.5 0M3 14c1.5 1.3 3 1.3 4.5 0s3-1.3 4.5 0 3 1.3 4.5 0 3-1.3 4.5 0M3 19c1.5 1.3 3 1.3 4.5 0s3-1.3 4.5 0 3 1.3 4.5 0 3-1.3 4.5 0" />, |
|
36 |
+ calendar: (<><rect x="4" y="5" width="16" height="16" rx="2" /><path d="M4 10h16M8.5 3v4M15.5 3v4M8 14h.01M12 14h.01M16 14h.01" /></>), |
|
37 |
+ ruler: <path d="M3.5 16.2 16.2 3.5l4.3 4.3L7.8 20.5zM7.8 15.9l1.8 1.8M10.6 13.1l1.8 1.8M13.4 10.3l1.8 1.8M16.2 7.5 18 9.3" />, |
|
38 |
+ bed: <path d="M3.5 18V6.5M3.5 13.5h17V18M3.5 10h6.5v3.5M20.5 13.5v-1a3 3 0 0 0-3-3H10" />, |
|
39 |
+ bath: <path d="M4 12.5h16v1.7a4.8 4.8 0 0 1-4.8 4.8H8.8A4.8 4.8 0 0 1 4 14.2zM6 12.5V6a2.5 2.5 0 0 1 4.6-1.3M7 19l-1 1.8M17 19l1 1.8" />, |
|
40 |
+ people: (<><circle cx="9" cy="8" r="3.2" /><path d="M3.5 20.5c0-3.1 2.4-5.2 5.5-5.2s5.5 2.1 5.5 5.2M15.5 5.4a3.2 3.2 0 0 1 0 5.2M20.5 20.5c0-2.7-1.8-4.6-4.3-5.1" /></>), |
|
41 |
+ stairs: <path d="M3.5 20.5h4.2v-4.2h4.2v-4.2h4.2V7.9h4.4" />, |
|
42 |
+ door: (<><rect x="6" y="3" width="12" height="18" rx="1.5" /><path d="M14.8 12h.01M4 21h16" /></>), |
|
43 |
+ home: <path d="m3.5 11 8.5-7 8.5 7M6 9.5V20h12V9.5" />, |
|
44 |
+ sun: (<><circle cx="12" cy="12" r="4" /><path d="M12 2.5v2.5M12 19v2.5M2.5 12H5M19 12h2.5M5.3 5.3l1.8 1.8M16.9 16.9l1.8 1.8M18.7 5.3l-1.8 1.8M7.1 16.9l-1.8 1.8" /></>), |
|
45 |
+ check: <path d="m4.5 12.5 5.3 5.3L19.5 6.5" />, |
|
46 |
+ spark: <path d="M12 3.5 13.9 10l6.6 2-6.6 2L12 20.5 10.1 14l-6.6-2 6.6-2z" />, |
|
47 |
+}; |
|
48 |
+ |
|
49 |
+/** [motif (sur libellé minuscule sans accents), clé d'icône] — ordre = priorité */ |
|
50 |
+const RULES: [RegExp, string][] = [ |
|
51 |
+ [/lave-?vaisselle/, "dishwasher"], |
|
52 |
+ [/laveuse|secheuse|lessive/, "washer"], |
|
53 |
+ [/buanderie/, "laundry"], |
|
54 |
+ [/electromenager|frigo|refrigerateur|cuisiniere|four incl|poele/, "fridge"], |
|
55 |
+ [/climatis|air clim|thermopompe|echangeur d.air|\ba\/?c\b/, "snow"], |
|
56 |
+ [/chauff/, "heat"], |
|
57 |
+ [/eau chaude/, "droplet"], |
|
58 |
+ [/electric|eclair|hydro/, "bolt"], |
|
59 |
+ [/internet|wi-?fi|cablodistribution|\bcable\b|fibre/, "wifi"], |
|
60 |
+ [/ascenseur/, "elevator"], |
|
61 |
+ [/balcon|terrasse|patio|loggia|veranda/, "balcony"], |
|
62 |
+ [/piscine/, "pool"], |
|
63 |
+ [/gym|salle d.entrainement|exercice/, "gym"], |
|
64 |
+ [/rangement|locker|entreposage|walk-?in|penderie/, "box"], |
|
65 |
+ [/garage/, "garage"], |
|
66 |
+ [/stationnement|parking|abri d.auto/, "parking"], |
|
67 |
+ [/meuble/, "sofa"], |
|
68 |
+ [/non-?fumeur|sans fumee/, "nosmoke"], |
|
69 |
+ [/animau|chat|chien|\bpet\b/, "paw"], |
|
70 |
+ [/foyer|cheminee|poele a bois/, "fire"], |
|
71 |
+ [/spa\b|jacuzzi|sauna|bain tourbillon/, "spa"], |
|
72 |
+ [/securite|surveill|camera|alarme|interphone|concierge|portier/, "shield"], |
|
73 |
+ [/\bvue\b|panoram/, "eye"], |
|
74 |
+ [/bord de l.eau|acces au lac|\blac\b|riviere|plage|navigable/, "waves"], |
|
75 |
+ [/cour|jardin|arbre|boise|verdure|gazon|amenagement paysager/, "tree"], |
|
76 |
+ [/ensoleill|luminosite|lumineux|solarium/, "sun"], |
|
77 |
+ [/dispo|libre |libre$/, "calendar"], |
|
78 |
+ [/pi2|pieds carres|superficie|\bm2\b/, "ruler"], |
|
79 |
+ [/chambre/, "bed"], |
|
80 |
+ [/salle[s]? de bain|salle[s]? d.eau|douche|\bsdb\b/, "bath"], |
|
81 |
+ [/occupant|personne|colocataire/, "people"], |
|
82 |
+ [/etage|niveau|escalier|mezzanine/, "stairs"], |
|
83 |
+ [/studio|loft|(^|\s)\d ?1\/2|½/, "door"], |
|
84 |
+ [/maison|plain-?pied|unifamiliale/, "home"], |
|
85 |
+ [/egout|septique|fosse/, "waves"], |
|
86 |
+ [/aqueduc|approvisionnement en eau|puits/, "droplet"], |
|
87 |
+ [/construction|neuve|fondation|toiture|revetement|renov/, "home"], |
|
88 |
+ [/commerce|zonage|usage/, "box"], |
|
89 |
+]; |
|
90 |
+ |
|
91 |
+/** minuscules + accents neutralisés, pour un appariement robuste */ |
|
92 |
+const norm = (s: string) => |
|
93 |
+ s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); |
|
94 |
+ |
|
95 |
+export function amenityKey(label: string, fallback = "check"): string { |
|
96 |
+ const n = norm(label); |
|
97 |
+ for (const [re, key] of RULES) if (re.test(n)) return key; |
|
98 |
+ return fallback; |
|
99 |
+} |
|
100 |
+ |
|
101 |
+export default function AmenityIco({ label, size = 16, fallback = "check" }: |
|
102 |
+ { label: string; size?: number; fallback?: string }) { |
|
103 |
+ return ( |
|
104 |
+ <svg |
|
105 |
+ width={size} height={size} viewBox="0 0 24 24" aria-hidden="true" |
|
106 |
+ fill="none" stroke="currentColor" strokeWidth={1.8} |
|
107 |
+ strokeLinecap="round" strokeLinejoin="round" |
|
108 |
+ > |
|
109 |
+ {ICONS[amenityKey(label, fallback)] ?? ICONS.check} |
|
110 |
+ </svg> |
|
111 |
+ ); |
|
112 |
+} |