@aws505/sheetsite 1.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.
Files changed (57) hide show
  1. package/README.md +105 -0
  2. package/dist/components/index.js +1696 -0
  3. package/dist/components/index.js.map +1 -0
  4. package/dist/components/index.mjs +1630 -0
  5. package/dist/components/index.mjs.map +1 -0
  6. package/dist/config/index.js +1840 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/config/index.mjs +1793 -0
  9. package/dist/config/index.mjs.map +1 -0
  10. package/dist/data/index.js +1296 -0
  11. package/dist/data/index.js.map +1 -0
  12. package/dist/data/index.mjs +1220 -0
  13. package/dist/data/index.mjs.map +1 -0
  14. package/dist/index.js +5433 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/index.mjs +5285 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/dist/seo/index.js +187 -0
  19. package/dist/seo/index.js.map +1 -0
  20. package/dist/seo/index.mjs +155 -0
  21. package/dist/seo/index.mjs.map +1 -0
  22. package/dist/theme/index.js +552 -0
  23. package/dist/theme/index.js.map +1 -0
  24. package/dist/theme/index.mjs +526 -0
  25. package/dist/theme/index.mjs.map +1 -0
  26. package/package.json +96 -0
  27. package/src/components/index.ts +41 -0
  28. package/src/components/layout/Footer.tsx +234 -0
  29. package/src/components/layout/Header.tsx +134 -0
  30. package/src/components/sections/FAQ.tsx +178 -0
  31. package/src/components/sections/Gallery.tsx +107 -0
  32. package/src/components/sections/Hero.tsx +202 -0
  33. package/src/components/sections/Hours.tsx +225 -0
  34. package/src/components/sections/Services.tsx +216 -0
  35. package/src/components/sections/Testimonials.tsx +184 -0
  36. package/src/components/ui/Button.tsx +158 -0
  37. package/src/components/ui/Card.tsx +162 -0
  38. package/src/components/ui/Icons.tsx +508 -0
  39. package/src/config/index.ts +207 -0
  40. package/src/config/presets/generic.ts +153 -0
  41. package/src/config/presets/home-kitchen.ts +154 -0
  42. package/src/config/presets/index.ts +708 -0
  43. package/src/config/presets/professional.ts +165 -0
  44. package/src/config/presets/repair.ts +160 -0
  45. package/src/config/presets/restaurant.ts +162 -0
  46. package/src/config/presets/salon.ts +178 -0
  47. package/src/config/presets/tailor.ts +159 -0
  48. package/src/config/types.ts +314 -0
  49. package/src/data/csv-parser.ts +154 -0
  50. package/src/data/defaults.ts +202 -0
  51. package/src/data/google-drive.ts +148 -0
  52. package/src/data/index.ts +535 -0
  53. package/src/data/sheets.ts +709 -0
  54. package/src/data/types.ts +379 -0
  55. package/src/seo/index.ts +272 -0
  56. package/src/theme/colors.ts +351 -0
  57. package/src/theme/index.ts +249 -0
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Icon Library
3
+ *
4
+ * SVG icons for common use cases across business websites.
5
+ * All icons use currentColor for easy theming.
6
+ */
7
+
8
+ import React from 'react';
9
+
10
+ export interface IconProps {
11
+ className?: string;
12
+ size?: number;
13
+ 'aria-hidden'?: boolean;
14
+ }
15
+
16
+ const defaultProps: IconProps = {
17
+ className: '',
18
+ size: 24,
19
+ 'aria-hidden': true,
20
+ };
21
+
22
+ // =============================================================================
23
+ // GENERAL ICONS
24
+ // =============================================================================
25
+
26
+ export function PhoneIcon({ className = '', size = 24, ...props }: IconProps) {
27
+ return (
28
+ <svg
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ width={size}
31
+ height={size}
32
+ viewBox="0 0 24 24"
33
+ fill="none"
34
+ stroke="currentColor"
35
+ strokeWidth="2"
36
+ strokeLinecap="round"
37
+ strokeLinejoin="round"
38
+ className={className}
39
+ {...props}
40
+ >
41
+ <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" />
42
+ </svg>
43
+ );
44
+ }
45
+
46
+ export function MailIcon({ className = '', size = 24, ...props }: IconProps) {
47
+ return (
48
+ <svg
49
+ xmlns="http://www.w3.org/2000/svg"
50
+ width={size}
51
+ height={size}
52
+ viewBox="0 0 24 24"
53
+ fill="none"
54
+ stroke="currentColor"
55
+ strokeWidth="2"
56
+ strokeLinecap="round"
57
+ strokeLinejoin="round"
58
+ className={className}
59
+ {...props}
60
+ >
61
+ <rect width="20" height="16" x="2" y="4" rx="2" />
62
+ <path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
63
+ </svg>
64
+ );
65
+ }
66
+
67
+ export function MapPinIcon({ className = '', size = 24, ...props }: IconProps) {
68
+ return (
69
+ <svg
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ width={size}
72
+ height={size}
73
+ viewBox="0 0 24 24"
74
+ fill="none"
75
+ stroke="currentColor"
76
+ strokeWidth="2"
77
+ strokeLinecap="round"
78
+ strokeLinejoin="round"
79
+ className={className}
80
+ {...props}
81
+ >
82
+ <path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" />
83
+ <circle cx="12" cy="10" r="3" />
84
+ </svg>
85
+ );
86
+ }
87
+
88
+ export function ClockIcon({ className = '', size = 24, ...props }: IconProps) {
89
+ return (
90
+ <svg
91
+ xmlns="http://www.w3.org/2000/svg"
92
+ width={size}
93
+ height={size}
94
+ viewBox="0 0 24 24"
95
+ fill="none"
96
+ stroke="currentColor"
97
+ strokeWidth="2"
98
+ strokeLinecap="round"
99
+ strokeLinejoin="round"
100
+ className={className}
101
+ {...props}
102
+ >
103
+ <circle cx="12" cy="12" r="10" />
104
+ <polyline points="12 6 12 12 16 14" />
105
+ </svg>
106
+ );
107
+ }
108
+
109
+ export function StarIcon({ className = '', size = 24, filled = false, ...props }: IconProps & { filled?: boolean }) {
110
+ return (
111
+ <svg
112
+ xmlns="http://www.w3.org/2000/svg"
113
+ width={size}
114
+ height={size}
115
+ viewBox="0 0 24 24"
116
+ fill={filled ? 'currentColor' : 'none'}
117
+ stroke="currentColor"
118
+ strokeWidth="2"
119
+ strokeLinecap="round"
120
+ strokeLinejoin="round"
121
+ className={className}
122
+ {...props}
123
+ >
124
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
125
+ </svg>
126
+ );
127
+ }
128
+
129
+ export function ChevronDownIcon({ className = '', size = 24, ...props }: IconProps) {
130
+ return (
131
+ <svg
132
+ xmlns="http://www.w3.org/2000/svg"
133
+ width={size}
134
+ height={size}
135
+ viewBox="0 0 24 24"
136
+ fill="none"
137
+ stroke="currentColor"
138
+ strokeWidth="2"
139
+ strokeLinecap="round"
140
+ strokeLinejoin="round"
141
+ className={className}
142
+ {...props}
143
+ >
144
+ <path d="m6 9 6 6 6-6" />
145
+ </svg>
146
+ );
147
+ }
148
+
149
+ export function ChevronRightIcon({ className = '', size = 24, ...props }: IconProps) {
150
+ return (
151
+ <svg
152
+ xmlns="http://www.w3.org/2000/svg"
153
+ width={size}
154
+ height={size}
155
+ viewBox="0 0 24 24"
156
+ fill="none"
157
+ stroke="currentColor"
158
+ strokeWidth="2"
159
+ strokeLinecap="round"
160
+ strokeLinejoin="round"
161
+ className={className}
162
+ {...props}
163
+ >
164
+ <path d="m9 18 6-6-6-6" />
165
+ </svg>
166
+ );
167
+ }
168
+
169
+ export function MenuIcon({ className = '', size = 24, ...props }: IconProps) {
170
+ return (
171
+ <svg
172
+ xmlns="http://www.w3.org/2000/svg"
173
+ width={size}
174
+ height={size}
175
+ viewBox="0 0 24 24"
176
+ fill="none"
177
+ stroke="currentColor"
178
+ strokeWidth="2"
179
+ strokeLinecap="round"
180
+ strokeLinejoin="round"
181
+ className={className}
182
+ {...props}
183
+ >
184
+ <line x1="4" x2="20" y1="12" y2="12" />
185
+ <line x1="4" x2="20" y1="6" y2="6" />
186
+ <line x1="4" x2="20" y1="18" y2="18" />
187
+ </svg>
188
+ );
189
+ }
190
+
191
+ export function XIcon({ className = '', size = 24, ...props }: IconProps) {
192
+ return (
193
+ <svg
194
+ xmlns="http://www.w3.org/2000/svg"
195
+ width={size}
196
+ height={size}
197
+ viewBox="0 0 24 24"
198
+ fill="none"
199
+ stroke="currentColor"
200
+ strokeWidth="2"
201
+ strokeLinecap="round"
202
+ strokeLinejoin="round"
203
+ className={className}
204
+ {...props}
205
+ >
206
+ <path d="M18 6 6 18" />
207
+ <path d="m6 6 12 12" />
208
+ </svg>
209
+ );
210
+ }
211
+
212
+ export function CheckIcon({ className = '', size = 24, ...props }: IconProps) {
213
+ return (
214
+ <svg
215
+ xmlns="http://www.w3.org/2000/svg"
216
+ width={size}
217
+ height={size}
218
+ viewBox="0 0 24 24"
219
+ fill="none"
220
+ stroke="currentColor"
221
+ strokeWidth="2"
222
+ strokeLinecap="round"
223
+ strokeLinejoin="round"
224
+ className={className}
225
+ {...props}
226
+ >
227
+ <path d="M20 6 9 17l-5-5" />
228
+ </svg>
229
+ );
230
+ }
231
+
232
+ // =============================================================================
233
+ // SERVICE ICONS
234
+ // =============================================================================
235
+
236
+ export function ScissorsIcon({ className = '', size = 24, ...props }: IconProps) {
237
+ return (
238
+ <svg
239
+ xmlns="http://www.w3.org/2000/svg"
240
+ width={size}
241
+ height={size}
242
+ viewBox="0 0 24 24"
243
+ fill="none"
244
+ stroke="currentColor"
245
+ strokeWidth="2"
246
+ strokeLinecap="round"
247
+ strokeLinejoin="round"
248
+ className={className}
249
+ {...props}
250
+ >
251
+ <circle cx="6" cy="6" r="3" />
252
+ <path d="M8.12 8.12 12 12" />
253
+ <path d="M20 4 8.12 15.88" />
254
+ <circle cx="6" cy="18" r="3" />
255
+ <path d="M14.8 14.8 20 20" />
256
+ </svg>
257
+ );
258
+ }
259
+
260
+ export function WrenchIcon({ className = '', size = 24, ...props }: IconProps) {
261
+ return (
262
+ <svg
263
+ xmlns="http://www.w3.org/2000/svg"
264
+ width={size}
265
+ height={size}
266
+ viewBox="0 0 24 24"
267
+ fill="none"
268
+ stroke="currentColor"
269
+ strokeWidth="2"
270
+ strokeLinecap="round"
271
+ strokeLinejoin="round"
272
+ className={className}
273
+ {...props}
274
+ >
275
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
276
+ </svg>
277
+ );
278
+ }
279
+
280
+ export function SparklesIcon({ className = '', size = 24, ...props }: IconProps) {
281
+ return (
282
+ <svg
283
+ xmlns="http://www.w3.org/2000/svg"
284
+ width={size}
285
+ height={size}
286
+ viewBox="0 0 24 24"
287
+ fill="none"
288
+ stroke="currentColor"
289
+ strokeWidth="2"
290
+ strokeLinecap="round"
291
+ strokeLinejoin="round"
292
+ className={className}
293
+ {...props}
294
+ >
295
+ <path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" />
296
+ <path d="M5 3v4" />
297
+ <path d="M19 17v4" />
298
+ <path d="M3 5h4" />
299
+ <path d="M17 19h4" />
300
+ </svg>
301
+ );
302
+ }
303
+
304
+ export function HeartIcon({ className = '', size = 24, filled = false, ...props }: IconProps & { filled?: boolean }) {
305
+ return (
306
+ <svg
307
+ xmlns="http://www.w3.org/2000/svg"
308
+ width={size}
309
+ height={size}
310
+ viewBox="0 0 24 24"
311
+ fill={filled ? 'currentColor' : 'none'}
312
+ stroke="currentColor"
313
+ strokeWidth="2"
314
+ strokeLinecap="round"
315
+ strokeLinejoin="round"
316
+ className={className}
317
+ {...props}
318
+ >
319
+ <path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" />
320
+ </svg>
321
+ );
322
+ }
323
+
324
+ export function UtensilsIcon({ className = '', size = 24, ...props }: IconProps) {
325
+ return (
326
+ <svg
327
+ xmlns="http://www.w3.org/2000/svg"
328
+ width={size}
329
+ height={size}
330
+ viewBox="0 0 24 24"
331
+ fill="none"
332
+ stroke="currentColor"
333
+ strokeWidth="2"
334
+ strokeLinecap="round"
335
+ strokeLinejoin="round"
336
+ className={className}
337
+ {...props}
338
+ >
339
+ <path d="M3 2v7c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2V2" />
340
+ <path d="M7 2v20" />
341
+ <path d="M21 15V2v0a5 5 0 0 0-5 5v6c0 1.1.9 2 2 2h3Zm0 0v7" />
342
+ </svg>
343
+ );
344
+ }
345
+
346
+ export function CakeIcon({ className = '', size = 24, ...props }: IconProps) {
347
+ return (
348
+ <svg
349
+ xmlns="http://www.w3.org/2000/svg"
350
+ width={size}
351
+ height={size}
352
+ viewBox="0 0 24 24"
353
+ fill="none"
354
+ stroke="currentColor"
355
+ strokeWidth="2"
356
+ strokeLinecap="round"
357
+ strokeLinejoin="round"
358
+ className={className}
359
+ {...props}
360
+ >
361
+ <path d="M20 21v-8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8" />
362
+ <path d="M4 16s.5-1 2-1 2.5 2 4 2 2.5-2 4-2 2.5 2 4 2 2-1 2-1" />
363
+ <path d="M2 21h20" />
364
+ <path d="M7 8v3" />
365
+ <path d="M12 8v3" />
366
+ <path d="M17 8v3" />
367
+ <path d="M7 4h.01" />
368
+ <path d="M12 4h.01" />
369
+ <path d="M17 4h.01" />
370
+ </svg>
371
+ );
372
+ }
373
+
374
+ export function BriefcaseIcon({ className = '', size = 24, ...props }: IconProps) {
375
+ return (
376
+ <svg
377
+ xmlns="http://www.w3.org/2000/svg"
378
+ width={size}
379
+ height={size}
380
+ viewBox="0 0 24 24"
381
+ fill="none"
382
+ stroke="currentColor"
383
+ strokeWidth="2"
384
+ strokeLinecap="round"
385
+ strokeLinejoin="round"
386
+ className={className}
387
+ {...props}
388
+ >
389
+ <rect width="20" height="14" x="2" y="7" rx="2" ry="2" />
390
+ <path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" />
391
+ </svg>
392
+ );
393
+ }
394
+
395
+ // =============================================================================
396
+ // SOCIAL ICONS
397
+ // =============================================================================
398
+
399
+ export function YelpIcon({ className = '', size = 24, ...props }: IconProps) {
400
+ return (
401
+ <svg
402
+ xmlns="http://www.w3.org/2000/svg"
403
+ width={size}
404
+ height={size}
405
+ viewBox="0 0 24 24"
406
+ fill="currentColor"
407
+ className={className}
408
+ {...props}
409
+ >
410
+ <path d="M20.16 12.73l-3.29 1.47c-.64.28-1.31-.26-1.2-.97l.52-3.4c.08-.56.49-.98 1.03-1.04l3.39-.38c.7-.08 1.22.58 1 1.23l-1.07 2.62c-.14.35-.36.41-.38.47zM14.13 15.58l-.52 3.4c-.11.7.55 1.25 1.2.97l3.29-1.47c.02-.06.24-.12.38-.47l1.07-2.62c.22-.65-.3-1.31-1-1.23l-3.39.38c-.54.06-.95.48-1.03 1.04zM10.98 3.05c-.47-.6-1.37-.5-1.7.2l-3.6 7.51c-.24.5-.01 1.1.51 1.33l2.63 1.21c.62.28 1.3-.2 1.27-.89l-.33-7.97c-.02-.56-.3-1.01-.78-1.39zM8.37 14.35l-2.63-1.21c-.52-.23-.75-.83-.51-1.33l3.6-7.51c.33-.7 1.23-.8 1.7-.2.48.38.76.83.78 1.39l.33 7.97c.03.69-.65 1.17-1.27.89zM10.04 16.42l-3.22 1.8c-.62.35-1.36-.14-1.28-.86l.44-3.52c.06-.53.42-.95.92-1.08l3.02-.78c.69-.18 1.32.43 1.09 1.1l-1.24 3.06c-.14.33-.38.34-.42.34-.04 0-.18-.01-.31.04v-.1z" />
411
+ </svg>
412
+ );
413
+ }
414
+
415
+ export function InstagramIcon({ className = '', size = 24, ...props }: IconProps) {
416
+ return (
417
+ <svg
418
+ xmlns="http://www.w3.org/2000/svg"
419
+ width={size}
420
+ height={size}
421
+ viewBox="0 0 24 24"
422
+ fill="none"
423
+ stroke="currentColor"
424
+ strokeWidth="2"
425
+ strokeLinecap="round"
426
+ strokeLinejoin="round"
427
+ className={className}
428
+ {...props}
429
+ >
430
+ <rect width="20" height="20" x="2" y="2" rx="5" ry="5" />
431
+ <path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" />
432
+ <line x1="17.5" x2="17.51" y1="6.5" y2="6.5" />
433
+ </svg>
434
+ );
435
+ }
436
+
437
+ export function FacebookIcon({ className = '', size = 24, ...props }: IconProps) {
438
+ return (
439
+ <svg
440
+ xmlns="http://www.w3.org/2000/svg"
441
+ width={size}
442
+ height={size}
443
+ viewBox="0 0 24 24"
444
+ fill="none"
445
+ stroke="currentColor"
446
+ strokeWidth="2"
447
+ strokeLinecap="round"
448
+ strokeLinejoin="round"
449
+ className={className}
450
+ {...props}
451
+ >
452
+ <path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" />
453
+ </svg>
454
+ );
455
+ }
456
+
457
+ // =============================================================================
458
+ // ICON MAP
459
+ // =============================================================================
460
+
461
+ /**
462
+ * Map of icon names to components.
463
+ * Use this to render icons dynamically based on string names.
464
+ */
465
+ export const iconMap: Record<string, React.FC<IconProps>> = {
466
+ phone: PhoneIcon,
467
+ mail: MailIcon,
468
+ email: MailIcon,
469
+ 'map-pin': MapPinIcon,
470
+ location: MapPinIcon,
471
+ clock: ClockIcon,
472
+ time: ClockIcon,
473
+ star: StarIcon,
474
+ 'chevron-down': ChevronDownIcon,
475
+ 'chevron-right': ChevronRightIcon,
476
+ menu: MenuIcon,
477
+ x: XIcon,
478
+ close: XIcon,
479
+ check: CheckIcon,
480
+ scissors: ScissorsIcon,
481
+ wrench: WrenchIcon,
482
+ tool: WrenchIcon,
483
+ sparkles: SparklesIcon,
484
+ heart: HeartIcon,
485
+ utensils: UtensilsIcon,
486
+ food: UtensilsIcon,
487
+ cake: CakeIcon,
488
+ briefcase: BriefcaseIcon,
489
+ yelp: YelpIcon,
490
+ instagram: InstagramIcon,
491
+ facebook: FacebookIcon,
492
+ };
493
+
494
+ /**
495
+ * Get an icon component by name.
496
+ */
497
+ export function getIcon(name: string): React.FC<IconProps> | null {
498
+ return iconMap[name.toLowerCase()] || null;
499
+ }
500
+
501
+ /**
502
+ * Render an icon by name.
503
+ */
504
+ export function Icon({ name, ...props }: IconProps & { name: string }) {
505
+ const IconComponent = getIcon(name);
506
+ if (!IconComponent) return null;
507
+ return <IconComponent {...props} />;
508
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * SheetSite Configuration Module
3
+ *
4
+ * Provides configuration types, business type presets, and utilities
5
+ * for setting up and customizing websites.
6
+ */
7
+
8
+ // Types
9
+ export * from './types';
10
+
11
+ // Presets
12
+ export {
13
+ presets,
14
+ getPreset,
15
+ getSupportedBusinessTypes,
16
+ businessCategories,
17
+ recommendBusinessType,
18
+ tailorPreset,
19
+ restaurantPreset,
20
+ homeKitchenPreset,
21
+ salonPreset,
22
+ repairPreset,
23
+ professionalPreset,
24
+ genericPreset,
25
+ } from './presets';
26
+
27
+ // =============================================================================
28
+ // CONFIGURATION UTILITIES
29
+ // =============================================================================
30
+
31
+ import type {
32
+ SiteConfig,
33
+ BusinessType,
34
+ BusinessPreset,
35
+ ThemePreset,
36
+ PageConfig,
37
+ HomeSectionType,
38
+ } from './types';
39
+ import { getPreset } from './presets';
40
+
41
+ /**
42
+ * Create a site configuration from a business preset.
43
+ * Allows overriding specific values.
44
+ */
45
+ export function createSiteConfig(
46
+ businessType: BusinessType | string,
47
+ overrides?: Partial<SiteConfig>
48
+ ): SiteConfig {
49
+ const preset = getPreset(businessType);
50
+ const config = { ...preset.config };
51
+
52
+ if (overrides) {
53
+ // Merge theme
54
+ if (overrides.theme) {
55
+ config.theme = { ...config.theme, ...overrides.theme };
56
+ }
57
+
58
+ // Merge pages
59
+ if (overrides.pages) {
60
+ config.pages = { ...config.pages, ...overrides.pages } as SiteConfig['pages'];
61
+ }
62
+
63
+ // Merge SEO
64
+ if (overrides.seo) {
65
+ config.seo = { ...config.seo, ...overrides.seo };
66
+ }
67
+
68
+ // Merge contact form
69
+ if (overrides.contactForm) {
70
+ config.contactForm = { ...config.contactForm, ...overrides.contactForm };
71
+ }
72
+
73
+ // Merge features
74
+ if (overrides.features) {
75
+ config.features = { ...config.features, ...overrides.features };
76
+ }
77
+
78
+ // Simple overrides
79
+ if (overrides.siteUrl) config.siteUrl = overrides.siteUrl;
80
+ if (overrides.customClasses) {
81
+ config.customClasses = { ...config.customClasses, ...overrides.customClasses };
82
+ }
83
+ }
84
+
85
+ return config;
86
+ }
87
+
88
+ /**
89
+ * Get the sheet template for a business type.
90
+ * Useful for generating documentation or Google Sheet templates.
91
+ */
92
+ export function getSheetTemplate(businessType: BusinessType | string): BusinessPreset['sheetTemplate'] {
93
+ const preset = getPreset(businessType);
94
+ return preset.sheetTemplate;
95
+ }
96
+
97
+ /**
98
+ * Get default data for a business type.
99
+ */
100
+ export function getDefaultData(businessType: BusinessType | string): BusinessPreset['defaults'] {
101
+ const preset = getPreset(businessType);
102
+ return preset.defaults;
103
+ }
104
+
105
+ /**
106
+ * Get icon suggestions for a business type.
107
+ */
108
+ export function getIconSuggestions(businessType: BusinessType | string): string[] {
109
+ const preset = getPreset(businessType);
110
+ return preset.iconSuggestions;
111
+ }
112
+
113
+ /**
114
+ * Get image suggestions for a business type.
115
+ */
116
+ export function getImageSuggestions(businessType: BusinessType | string): string[] {
117
+ const preset = getPreset(businessType);
118
+ return preset.imageSuggestions;
119
+ }
120
+
121
+ /**
122
+ * Validate a site configuration.
123
+ */
124
+ export function validateSiteConfig(config: SiteConfig): { valid: boolean; errors: string[] } {
125
+ const errors: string[] = [];
126
+
127
+ // Check required fields
128
+ if (!config.businessType) {
129
+ errors.push('businessType is required');
130
+ }
131
+
132
+ if (!config.pages?.home) {
133
+ errors.push('pages.home configuration is required');
134
+ }
135
+
136
+ if (!config.theme) {
137
+ errors.push('theme configuration is required');
138
+ }
139
+
140
+ // Check home page sections
141
+ if (config.pages?.home?.sections) {
142
+ const validSections: HomeSectionType[] = [
143
+ 'hero', 'services', 'menu', 'products', 'gallery',
144
+ 'testimonials', 'team', 'faq', 'hours', 'location',
145
+ 'contact-form', 'cta', 'how-it-works', 'announcements', 'about-preview',
146
+ ];
147
+
148
+ for (const section of config.pages.home.sections) {
149
+ if (!validSections.includes(section)) {
150
+ errors.push(`Invalid home section: ${section}`);
151
+ }
152
+ }
153
+ }
154
+
155
+ return {
156
+ valid: errors.length === 0,
157
+ errors,
158
+ };
159
+ }
160
+
161
+ // =============================================================================
162
+ // THEME HELPERS
163
+ // =============================================================================
164
+
165
+ /**
166
+ * Get available theme presets.
167
+ */
168
+ export const themePresets: ThemePreset[] = [
169
+ 'warm-brown',
170
+ 'cool-blue',
171
+ 'earth-green',
172
+ 'warm-amber',
173
+ 'elegant-gold',
174
+ 'fresh-teal',
175
+ 'bold-red',
176
+ 'soft-pink',
177
+ 'slate-gray',
178
+ 'forest-green',
179
+ ];
180
+
181
+ /**
182
+ * Get a description for a theme preset.
183
+ */
184
+ export function getThemeDescription(preset: ThemePreset): string {
185
+ const descriptions: Record<ThemePreset, string> = {
186
+ 'warm-brown': 'Warm, earthy tones perfect for tailoring, leather goods, and traditional crafts',
187
+ 'cool-blue': 'Professional and trustworthy, ideal for professional services and tech',
188
+ 'earth-green': 'Natural and organic, great for eco-friendly, outdoor, and health businesses',
189
+ 'warm-amber': 'Welcoming and appetizing, perfect for food, hospitality, and home businesses',
190
+ 'elegant-gold': 'Luxurious and sophisticated, suited for upscale and formal businesses',
191
+ 'fresh-teal': 'Modern and creative, ideal for spas, creative studios, and wellness',
192
+ 'bold-red': 'Energetic and urgent, great for automotive, sports, and action-oriented businesses',
193
+ 'soft-pink': 'Gentle and feminine, perfect for beauty, weddings, and delicate services',
194
+ 'slate-gray': 'Minimalist and industrial, suited for modern, urban businesses',
195
+ 'forest-green': 'Deep and natural, ideal for outdoor, nature, and sustainability-focused businesses',
196
+ };
197
+
198
+ return descriptions[preset];
199
+ }
200
+
201
+ /**
202
+ * Recommend a theme based on business type.
203
+ */
204
+ export function recommendTheme(businessType: BusinessType | string): ThemePreset {
205
+ const preset = getPreset(businessType);
206
+ return preset.config.theme.preset || 'cool-blue';
207
+ }