@aws505/sheetsite 1.0.2 → 1.0.4
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/README.md +185 -40
- package/dist/components/index.js +932 -60
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +916 -60
- package/dist/components/index.mjs.map +1 -1
- package/dist/index.js +195 -62
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +185 -62
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +15 -0
- package/src/components/layout/Header.tsx +1 -0
- package/src/components/sections/BeforeAfter.tsx +345 -0
- package/src/components/sections/FAQ.tsx +3 -3
- package/src/components/sections/Gallery.tsx +104 -4
- package/src/components/sections/Hero.tsx +19 -3
- package/src/components/sections/Menu.tsx +312 -0
- package/src/components/sections/Services.tsx +3 -3
- package/src/components/sections/Testimonials.tsx +1 -1
- package/src/components/sections/TrustBadges.tsx +283 -0
- package/src/components/ui/AnimatedSection.tsx +136 -0
- package/src/components/ui/FloatingClaimBanner.tsx +160 -0
|
@@ -472,6 +472,7 @@ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
472
472
|
var defaultNavigation = [
|
|
473
473
|
{ label: "Home", href: "/" },
|
|
474
474
|
{ label: "Services", href: "/#services" },
|
|
475
|
+
{ label: "Gallery", href: "/#gallery" },
|
|
475
476
|
{ label: "Hours", href: "/#hours" },
|
|
476
477
|
{ label: "Reviews", href: "/#reviews" },
|
|
477
478
|
{ label: "FAQ", href: "/#faq" }
|
|
@@ -730,6 +731,15 @@ function Footer({
|
|
|
730
731
|
|
|
731
732
|
// src/components/sections/Hero.tsx
|
|
732
733
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
734
|
+
var defaultTrustSignals = {
|
|
735
|
+
salon: ["Expert Stylists", "Walk-Ins Welcome", "Quality Products"],
|
|
736
|
+
restaurant: ["Fresh Ingredients", "Family Recipes", "Friendly Service"],
|
|
737
|
+
repair: ["Certified Technicians", "Honest Estimates", "Quality Parts"],
|
|
738
|
+
tailor: ["Expert Craftsmanship", "Perfect Fit Guaranteed", "Quick Turnaround"],
|
|
739
|
+
professional: ["Quality Work", "Fair Prices", "Fast Service"],
|
|
740
|
+
retail: ["Quality Products", "Great Selection", "Friendly Service"],
|
|
741
|
+
default: ["Quality Work", "Fair Prices", "Fast Service"]
|
|
742
|
+
};
|
|
733
743
|
function Hero({
|
|
734
744
|
business,
|
|
735
745
|
variant = "centered",
|
|
@@ -738,8 +748,11 @@ function Hero({
|
|
|
738
748
|
todayHours,
|
|
739
749
|
backgroundImage,
|
|
740
750
|
overlay = true,
|
|
741
|
-
className = ""
|
|
751
|
+
className = "",
|
|
752
|
+
trustSignals
|
|
742
753
|
}) {
|
|
754
|
+
const businessType = business.type || "default";
|
|
755
|
+
const signals = trustSignals || defaultTrustSignals[businessType] || defaultTrustSignals.default;
|
|
743
756
|
const bgStyle = backgroundImage ? { backgroundImage: `url(${backgroundImage})` } : void 0;
|
|
744
757
|
const handleCallClick = () => {
|
|
745
758
|
if (business.phone) {
|
|
@@ -819,8 +832,8 @@ function Hero({
|
|
|
819
832
|
) }),
|
|
820
833
|
/* @__PURE__ */ jsx4("h1", { className: "text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-4", children: business.name }),
|
|
821
834
|
business.tagline && /* @__PURE__ */ jsx4("p", { className: "text-xl md:text-2xl text-white/90 mb-6", children: business.tagline }),
|
|
822
|
-
business.aboutShort && /* @__PURE__ */ jsx4("p", { className:
|
|
823
|
-
/* @__PURE__ */ jsx4("div", { className:
|
|
835
|
+
business.aboutShort && /* @__PURE__ */ jsx4("p", { className: `text-lg text-white/80 mb-8 max-w-xl ${variant === "centered" ? "mx-auto" : ""}`, children: business.aboutShort }),
|
|
836
|
+
/* @__PURE__ */ jsx4("div", { className: `flex flex-wrap gap-4 mb-8 ${variant === "centered" ? "justify-center" : ""}`, children: signals.map((signal) => /* @__PURE__ */ jsxs4("div", { className: "flex items-center text-white/90", children: [
|
|
824
837
|
/* @__PURE__ */ jsx4("svg", { className: "w-5 h-5 text-accent-400 mr-2", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx4(
|
|
825
838
|
"path",
|
|
826
839
|
{
|
|
@@ -894,7 +907,7 @@ function Services({
|
|
|
894
907
|
4: "md:grid-cols-2 lg:grid-cols-4"
|
|
895
908
|
};
|
|
896
909
|
if (variant === "list") {
|
|
897
|
-
return /* @__PURE__ */ jsx5("section", { id, className: `py-16 ${className}`, children: /* @__PURE__ */ jsxs5("div", { className: "container mx-auto px-4", children: [
|
|
910
|
+
return /* @__PURE__ */ jsx5("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs5("div", { className: "container mx-auto px-4", children: [
|
|
898
911
|
/* @__PURE__ */ jsx5(SectionHeader, { title, subtitle }),
|
|
899
912
|
/* @__PURE__ */ jsx5("div", { className: "max-w-3xl mx-auto divide-y divide-gray-200", children: displayedServices.map((service) => /* @__PURE__ */ jsx5(
|
|
900
913
|
ServiceListItem,
|
|
@@ -908,7 +921,7 @@ function Services({
|
|
|
908
921
|
] }) });
|
|
909
922
|
}
|
|
910
923
|
if (variant === "minimal") {
|
|
911
|
-
return /* @__PURE__ */ jsx5("section", { id, className: `py-16 ${className}`, children: /* @__PURE__ */ jsxs5("div", { className: "container mx-auto px-4", children: [
|
|
924
|
+
return /* @__PURE__ */ jsx5("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs5("div", { className: "container mx-auto px-4", children: [
|
|
912
925
|
/* @__PURE__ */ jsx5(SectionHeader, { title, subtitle }),
|
|
913
926
|
/* @__PURE__ */ jsx5("div", { className: `grid gap-6 ${gridCols[columns]}`, children: displayedServices.map((service) => /* @__PURE__ */ jsx5(
|
|
914
927
|
ServiceMinimalCard,
|
|
@@ -920,7 +933,7 @@ function Services({
|
|
|
920
933
|
)) })
|
|
921
934
|
] }) });
|
|
922
935
|
}
|
|
923
|
-
return /* @__PURE__ */ jsx5("section", { id, className: `py-16 bg-gray-50 ${className}`, children: /* @__PURE__ */ jsxs5("div", { className: "container mx-auto px-4", children: [
|
|
936
|
+
return /* @__PURE__ */ jsx5("section", { id, className: `py-16 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ jsxs5("div", { className: "container mx-auto px-4", children: [
|
|
924
937
|
/* @__PURE__ */ jsx5(SectionHeader, { title, subtitle }),
|
|
925
938
|
/* @__PURE__ */ jsx5("div", { className: `grid gap-6 ${gridCols[columns]}`, children: displayedServices.map((service) => /* @__PURE__ */ jsx5(
|
|
926
939
|
ServiceCard,
|
|
@@ -1003,7 +1016,7 @@ function Testimonials({
|
|
|
1003
1016
|
2: "md:grid-cols-2 max-w-4xl mx-auto",
|
|
1004
1017
|
3: "md:grid-cols-2 lg:grid-cols-3"
|
|
1005
1018
|
};
|
|
1006
|
-
return /* @__PURE__ */ jsx6("section", { id, className: `py-16 ${className}`, children: /* @__PURE__ */ jsxs6("div", { className: "container mx-auto px-4", children: [
|
|
1019
|
+
return /* @__PURE__ */ jsx6("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs6("div", { className: "container mx-auto px-4", children: [
|
|
1007
1020
|
/* @__PURE__ */ jsxs6("div", { className: "text-center mb-12", children: [
|
|
1008
1021
|
/* @__PURE__ */ jsx6("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1009
1022
|
subtitle && /* @__PURE__ */ jsx6("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
@@ -1123,18 +1136,18 @@ function FAQ({
|
|
|
1123
1136
|
}
|
|
1124
1137
|
};
|
|
1125
1138
|
if (variant === "cards") {
|
|
1126
|
-
return /* @__PURE__ */ jsx7("section", { id, className: `py-16 ${className}`, children: /* @__PURE__ */ jsxs7("div", { className: "container mx-auto px-4", children: [
|
|
1139
|
+
return /* @__PURE__ */ jsx7("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs7("div", { className: "container mx-auto px-4", children: [
|
|
1127
1140
|
/* @__PURE__ */ jsx7(SectionHeader2, { title, subtitle }),
|
|
1128
1141
|
/* @__PURE__ */ jsx7("div", { className: "grid md:grid-cols-2 gap-6 max-w-4xl mx-auto", children: items.map((item) => /* @__PURE__ */ jsx7(FAQCard, { item }, item.id || item.question)) })
|
|
1129
1142
|
] }) });
|
|
1130
1143
|
}
|
|
1131
1144
|
if (variant === "simple") {
|
|
1132
|
-
return /* @__PURE__ */ jsx7("section", { id, className: `py-16 ${className}`, children: /* @__PURE__ */ jsxs7("div", { className: "container mx-auto px-4", children: [
|
|
1145
|
+
return /* @__PURE__ */ jsx7("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs7("div", { className: "container mx-auto px-4", children: [
|
|
1133
1146
|
/* @__PURE__ */ jsx7(SectionHeader2, { title, subtitle }),
|
|
1134
1147
|
/* @__PURE__ */ jsx7("div", { className: "max-w-3xl mx-auto space-y-8", children: items.map((item) => /* @__PURE__ */ jsx7(FAQSimple, { item }, item.id || item.question)) })
|
|
1135
1148
|
] }) });
|
|
1136
1149
|
}
|
|
1137
|
-
return /* @__PURE__ */ jsx7("section", { id, className: `py-16 bg-gray-50 ${className}`, children: /* @__PURE__ */ jsxs7("div", { className: "container mx-auto px-4", children: [
|
|
1150
|
+
return /* @__PURE__ */ jsx7("section", { id, className: `py-16 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ jsxs7("div", { className: "container mx-auto px-4", children: [
|
|
1138
1151
|
/* @__PURE__ */ jsx7(SectionHeader2, { title, subtitle }),
|
|
1139
1152
|
/* @__PURE__ */ jsx7("div", { className: "max-w-3xl mx-auto", children: items.map((item, index) => /* @__PURE__ */ jsx7(
|
|
1140
1153
|
FAQAccordionItem,
|
|
@@ -1346,53 +1359,694 @@ function Gallery({
|
|
|
1346
1359
|
columns = 3,
|
|
1347
1360
|
variant = "grid",
|
|
1348
1361
|
showCaptions = true,
|
|
1362
|
+
enableLightbox = true,
|
|
1349
1363
|
limit,
|
|
1350
|
-
className = ""
|
|
1364
|
+
className = "",
|
|
1365
|
+
id = "gallery"
|
|
1351
1366
|
}) {
|
|
1352
1367
|
const displayedItems = limit ? items.slice(0, limit) : items;
|
|
1353
1368
|
const [failedImages, setFailedImages] = useState3(/* @__PURE__ */ new Set());
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1369
|
+
const [lightboxIndex, setLightboxIndex] = useState3(null);
|
|
1370
|
+
const handleImageError = (id2) => {
|
|
1371
|
+
setFailedImages((prev) => new Set(prev).add(id2));
|
|
1356
1372
|
};
|
|
1357
1373
|
const gridCols = {
|
|
1358
1374
|
2: "grid-cols-1 sm:grid-cols-2",
|
|
1359
1375
|
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
1360
1376
|
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
|
1361
1377
|
};
|
|
1362
|
-
|
|
1363
|
-
(
|
|
1364
|
-
|
|
1365
|
-
|
|
1378
|
+
const openLightbox = (index) => {
|
|
1379
|
+
if (enableLightbox) {
|
|
1380
|
+
setLightboxIndex(index);
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
const closeLightbox = () => setLightboxIndex(null);
|
|
1384
|
+
const goToPrevious = () => {
|
|
1385
|
+
if (lightboxIndex !== null) {
|
|
1386
|
+
setLightboxIndex(lightboxIndex === 0 ? displayedItems.length - 1 : lightboxIndex - 1);
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
const goToNext = () => {
|
|
1390
|
+
if (lightboxIndex !== null) {
|
|
1391
|
+
setLightboxIndex(lightboxIndex === displayedItems.length - 1 ? 0 : lightboxIndex + 1);
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
return /* @__PURE__ */ jsxs9("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: [
|
|
1395
|
+
/* @__PURE__ */ jsxs9("div", { className: "container mx-auto px-4", children: [
|
|
1396
|
+
(title || subtitle) && /* @__PURE__ */ jsxs9("div", { className: "text-center mb-12", children: [
|
|
1397
|
+
title && /* @__PURE__ */ jsx9("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1398
|
+
subtitle && /* @__PURE__ */ jsx9("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1399
|
+
] }),
|
|
1400
|
+
/* @__PURE__ */ jsx9("div", { className: `grid gap-4 ${gridCols[columns]}`, children: displayedItems.map((item, index) => {
|
|
1401
|
+
const itemId = item.id || item.imageUrl;
|
|
1402
|
+
const hasFailed = failedImages.has(itemId);
|
|
1403
|
+
return /* @__PURE__ */ jsx9(
|
|
1404
|
+
"div",
|
|
1405
|
+
{
|
|
1406
|
+
className: `group relative aspect-square overflow-hidden rounded-lg bg-gray-100 ${enableLightbox ? "cursor-pointer" : ""}`,
|
|
1407
|
+
onClick: () => openLightbox(index),
|
|
1408
|
+
role: enableLightbox ? "button" : void 0,
|
|
1409
|
+
tabIndex: enableLightbox ? 0 : void 0,
|
|
1410
|
+
onKeyDown: enableLightbox ? (e) => e.key === "Enter" && openLightbox(index) : void 0,
|
|
1411
|
+
children: hasFailed ? /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsx9("span", { children: "Image unavailable" }) }) : /* @__PURE__ */ jsxs9(Fragment, { children: [
|
|
1412
|
+
/* @__PURE__ */ jsx9(
|
|
1413
|
+
"img",
|
|
1414
|
+
{
|
|
1415
|
+
src: item.imageUrl,
|
|
1416
|
+
alt: item.alt || "",
|
|
1417
|
+
loading: "lazy",
|
|
1418
|
+
className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105",
|
|
1419
|
+
onError: () => handleImageError(itemId)
|
|
1420
|
+
}
|
|
1421
|
+
),
|
|
1422
|
+
enableLightbox && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center", children: /* @__PURE__ */ jsx9("svg", { className: "w-10 h-10 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7" }) }) }),
|
|
1423
|
+
showCaptions && item.caption && !enableLightbox && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300", children: /* @__PURE__ */ jsx9("div", { className: "absolute bottom-0 left-0 right-0 p-4", children: /* @__PURE__ */ jsx9("p", { className: "text-white text-sm", children: item.caption }) }) })
|
|
1424
|
+
] })
|
|
1425
|
+
},
|
|
1426
|
+
itemId
|
|
1427
|
+
);
|
|
1428
|
+
}) })
|
|
1366
1429
|
] }),
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1430
|
+
enableLightbox && lightboxIndex !== null && /* @__PURE__ */ jsxs9(
|
|
1431
|
+
"div",
|
|
1432
|
+
{
|
|
1433
|
+
className: "fixed inset-0 z-50 bg-black/90 flex items-center justify-center",
|
|
1434
|
+
onClick: closeLightbox,
|
|
1435
|
+
children: [
|
|
1436
|
+
/* @__PURE__ */ jsx9(
|
|
1437
|
+
"button",
|
|
1438
|
+
{
|
|
1439
|
+
onClick: closeLightbox,
|
|
1440
|
+
className: "absolute top-4 right-4 text-white hover:text-gray-300 transition-colors z-10",
|
|
1441
|
+
"aria-label": "Close lightbox",
|
|
1442
|
+
children: /* @__PURE__ */ jsx9("svg", { className: "w-8 h-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
1443
|
+
}
|
|
1444
|
+
),
|
|
1445
|
+
/* @__PURE__ */ jsx9(
|
|
1446
|
+
"button",
|
|
1447
|
+
{
|
|
1448
|
+
onClick: (e) => {
|
|
1449
|
+
e.stopPropagation();
|
|
1450
|
+
goToPrevious();
|
|
1451
|
+
},
|
|
1452
|
+
className: "absolute left-4 top-1/2 -translate-y-1/2 text-white hover:text-gray-300 transition-colors z-10",
|
|
1453
|
+
"aria-label": "Previous image",
|
|
1454
|
+
children: /* @__PURE__ */ jsx9("svg", { className: "w-10 h-10", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
|
|
1455
|
+
}
|
|
1456
|
+
),
|
|
1457
|
+
/* @__PURE__ */ jsx9(
|
|
1458
|
+
"button",
|
|
1459
|
+
{
|
|
1460
|
+
onClick: (e) => {
|
|
1461
|
+
e.stopPropagation();
|
|
1462
|
+
goToNext();
|
|
1463
|
+
},
|
|
1464
|
+
className: "absolute right-4 top-1/2 -translate-y-1/2 text-white hover:text-gray-300 transition-colors z-10",
|
|
1465
|
+
"aria-label": "Next image",
|
|
1466
|
+
children: /* @__PURE__ */ jsx9("svg", { className: "w-10 h-10", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx9("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
|
|
1467
|
+
}
|
|
1468
|
+
),
|
|
1469
|
+
/* @__PURE__ */ jsxs9(
|
|
1470
|
+
"div",
|
|
1471
|
+
{
|
|
1472
|
+
className: "max-w-5xl max-h-[85vh] mx-4",
|
|
1473
|
+
onClick: (e) => e.stopPropagation(),
|
|
1474
|
+
children: [
|
|
1475
|
+
/* @__PURE__ */ jsx9(
|
|
1476
|
+
"img",
|
|
1477
|
+
{
|
|
1478
|
+
src: displayedItems[lightboxIndex].imageUrl,
|
|
1479
|
+
alt: displayedItems[lightboxIndex].alt || "",
|
|
1480
|
+
className: "max-w-full max-h-[85vh] object-contain"
|
|
1481
|
+
}
|
|
1482
|
+
),
|
|
1483
|
+
showCaptions && displayedItems[lightboxIndex].caption && /* @__PURE__ */ jsx9("p", { className: "text-white text-center mt-4 text-lg", children: displayedItems[lightboxIndex].caption })
|
|
1484
|
+
]
|
|
1485
|
+
}
|
|
1486
|
+
),
|
|
1487
|
+
/* @__PURE__ */ jsxs9("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 text-white text-sm", children: [
|
|
1488
|
+
lightboxIndex + 1,
|
|
1489
|
+
" / ",
|
|
1490
|
+
displayedItems.length
|
|
1386
1491
|
] })
|
|
1492
|
+
]
|
|
1493
|
+
}
|
|
1494
|
+
)
|
|
1495
|
+
] });
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// src/components/sections/Menu.tsx
|
|
1499
|
+
import { useState as useState4 } from "react";
|
|
1500
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1501
|
+
function Menu({
|
|
1502
|
+
items,
|
|
1503
|
+
title = "Our Menu",
|
|
1504
|
+
subtitle,
|
|
1505
|
+
showCategories = true,
|
|
1506
|
+
showImages = true,
|
|
1507
|
+
showDietary = true,
|
|
1508
|
+
variant = "cards",
|
|
1509
|
+
columns = 2,
|
|
1510
|
+
className = "",
|
|
1511
|
+
id = "menu"
|
|
1512
|
+
}) {
|
|
1513
|
+
const categories = showCategories ? [...new Set(items.filter((item) => item.category).map((item) => item.category))] : [];
|
|
1514
|
+
const [activeCategory, setActiveCategory] = useState4(
|
|
1515
|
+
categories.length > 0 ? categories[0] : null
|
|
1516
|
+
);
|
|
1517
|
+
const displayedItems = activeCategory ? items.filter((item) => item.category === activeCategory) : items;
|
|
1518
|
+
const sortedItems = [...displayedItems].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
1519
|
+
const gridCols = {
|
|
1520
|
+
1: "max-w-2xl mx-auto",
|
|
1521
|
+
2: "md:grid-cols-2",
|
|
1522
|
+
3: "md:grid-cols-2 lg:grid-cols-3"
|
|
1523
|
+
};
|
|
1524
|
+
return /* @__PURE__ */ jsx10("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs10("div", { className: "container mx-auto px-4", children: [
|
|
1525
|
+
/* @__PURE__ */ jsxs10("div", { className: "text-center mb-12", children: [
|
|
1526
|
+
/* @__PURE__ */ jsx10("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1527
|
+
subtitle && /* @__PURE__ */ jsx10("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1528
|
+
] }),
|
|
1529
|
+
showCategories && categories.length > 1 && /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap justify-center gap-2 mb-8", children: categories.map((category) => /* @__PURE__ */ jsx10(
|
|
1530
|
+
"button",
|
|
1531
|
+
{
|
|
1532
|
+
onClick: () => setActiveCategory(category),
|
|
1533
|
+
className: `px-4 py-2 rounded-full text-sm font-medium transition-colors ${activeCategory === category ? "bg-primary-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
|
|
1534
|
+
children: category
|
|
1535
|
+
},
|
|
1536
|
+
category
|
|
1537
|
+
)) }),
|
|
1538
|
+
variant === "list" ? /* @__PURE__ */ jsx10("div", { className: "max-w-3xl mx-auto divide-y divide-gray-200", children: sortedItems.map((item) => /* @__PURE__ */ jsx10(
|
|
1539
|
+
MenuListItem,
|
|
1540
|
+
{
|
|
1541
|
+
item,
|
|
1542
|
+
showImage: showImages,
|
|
1543
|
+
showDietary
|
|
1544
|
+
},
|
|
1545
|
+
item.id || item.name
|
|
1546
|
+
)) }) : variant === "compact" ? /* @__PURE__ */ jsx10("div", { className: `grid gap-4 ${gridCols[columns]}`, children: sortedItems.map((item) => /* @__PURE__ */ jsx10(
|
|
1547
|
+
MenuCompactItem,
|
|
1548
|
+
{
|
|
1549
|
+
item,
|
|
1550
|
+
showDietary
|
|
1551
|
+
},
|
|
1552
|
+
item.id || item.name
|
|
1553
|
+
)) }) : /* @__PURE__ */ jsx10("div", { className: `grid gap-6 ${gridCols[columns]}`, children: sortedItems.map((item) => /* @__PURE__ */ jsx10(
|
|
1554
|
+
MenuCard,
|
|
1555
|
+
{
|
|
1556
|
+
item,
|
|
1557
|
+
showImage: showImages,
|
|
1558
|
+
showDietary
|
|
1559
|
+
},
|
|
1560
|
+
item.id || item.name
|
|
1561
|
+
)) })
|
|
1562
|
+
] }) });
|
|
1563
|
+
}
|
|
1564
|
+
function MenuCard({
|
|
1565
|
+
item,
|
|
1566
|
+
showImage,
|
|
1567
|
+
showDietary
|
|
1568
|
+
}) {
|
|
1569
|
+
return /* @__PURE__ */ jsxs10("div", { className: "bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow", children: [
|
|
1570
|
+
showImage && item.imageUrl && /* @__PURE__ */ jsxs10("div", { className: "aspect-video relative overflow-hidden", children: [
|
|
1571
|
+
/* @__PURE__ */ jsx10(
|
|
1572
|
+
"img",
|
|
1573
|
+
{
|
|
1574
|
+
src: item.imageUrl,
|
|
1575
|
+
alt: item.name,
|
|
1576
|
+
className: "w-full h-full object-cover"
|
|
1577
|
+
}
|
|
1578
|
+
),
|
|
1579
|
+
item.featured && /* @__PURE__ */ jsx10("span", { className: "absolute top-2 left-2 px-2 py-1 bg-primary-600 text-white text-xs font-medium rounded", children: "Featured" })
|
|
1580
|
+
] }),
|
|
1581
|
+
/* @__PURE__ */ jsxs10("div", { className: "p-4", children: [
|
|
1582
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex justify-between items-start mb-2", children: [
|
|
1583
|
+
/* @__PURE__ */ jsx10("h3", { className: "text-lg font-semibold text-gray-900", children: item.name }),
|
|
1584
|
+
/* @__PURE__ */ jsx10(PriceDisplay, { item })
|
|
1585
|
+
] }),
|
|
1586
|
+
item.description && /* @__PURE__ */ jsx10("p", { className: "text-gray-600 text-sm mb-3", children: item.description }),
|
|
1587
|
+
showDietary && item.dietary && item.dietary.length > 0 && /* @__PURE__ */ jsx10(DietaryBadges, { dietary: item.dietary }),
|
|
1588
|
+
!item.available && /* @__PURE__ */ jsx10("span", { className: "inline-block mt-2 px-2 py-1 bg-gray-100 text-gray-500 text-xs rounded", children: "Currently unavailable" })
|
|
1589
|
+
] })
|
|
1590
|
+
] });
|
|
1591
|
+
}
|
|
1592
|
+
function MenuListItem({
|
|
1593
|
+
item,
|
|
1594
|
+
showImage,
|
|
1595
|
+
showDietary
|
|
1596
|
+
}) {
|
|
1597
|
+
return /* @__PURE__ */ jsxs10("div", { className: "py-4 flex gap-4", children: [
|
|
1598
|
+
showImage && item.imageUrl && /* @__PURE__ */ jsx10("div", { className: "w-20 h-20 flex-shrink-0 rounded-lg overflow-hidden", children: /* @__PURE__ */ jsx10(
|
|
1599
|
+
"img",
|
|
1600
|
+
{
|
|
1601
|
+
src: item.imageUrl,
|
|
1602
|
+
alt: item.name,
|
|
1603
|
+
className: "w-full h-full object-cover"
|
|
1604
|
+
}
|
|
1605
|
+
) }),
|
|
1606
|
+
/* @__PURE__ */ jsx10("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs10("div", { className: "flex justify-between items-start", children: [
|
|
1607
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
1608
|
+
/* @__PURE__ */ jsxs10("h3", { className: "text-lg font-semibold text-gray-900", children: [
|
|
1609
|
+
item.name,
|
|
1610
|
+
item.featured && /* @__PURE__ */ jsx10("span", { className: "ml-2 px-2 py-0.5 bg-primary-100 text-primary-700 text-xs font-medium rounded", children: "Popular" })
|
|
1611
|
+
] }),
|
|
1612
|
+
item.description && /* @__PURE__ */ jsx10("p", { className: "text-gray-600 text-sm mt-1", children: item.description }),
|
|
1613
|
+
showDietary && item.dietary && item.dietary.length > 0 && /* @__PURE__ */ jsx10("div", { className: "mt-2", children: /* @__PURE__ */ jsx10(DietaryBadges, { dietary: item.dietary }) })
|
|
1614
|
+
] }),
|
|
1615
|
+
/* @__PURE__ */ jsx10(PriceDisplay, { item })
|
|
1616
|
+
] }) })
|
|
1617
|
+
] });
|
|
1618
|
+
}
|
|
1619
|
+
function MenuCompactItem({
|
|
1620
|
+
item,
|
|
1621
|
+
showDietary
|
|
1622
|
+
}) {
|
|
1623
|
+
return /* @__PURE__ */ jsxs10("div", { className: "flex justify-between items-center py-2 border-b border-gray-100 last:border-0", children: [
|
|
1624
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
|
|
1625
|
+
/* @__PURE__ */ jsx10("span", { className: "font-medium text-gray-900", children: item.name }),
|
|
1626
|
+
item.featured && /* @__PURE__ */ jsx10("span", { className: "w-2 h-2 bg-primary-500 rounded-full", title: "Popular" }),
|
|
1627
|
+
showDietary && item.dietary && item.dietary.length > 0 && /* @__PURE__ */ jsxs10("span", { className: "text-xs text-gray-500", children: [
|
|
1628
|
+
"(",
|
|
1629
|
+
item.dietary.join(", "),
|
|
1630
|
+
")"
|
|
1631
|
+
] })
|
|
1632
|
+
] }),
|
|
1633
|
+
/* @__PURE__ */ jsx10(PriceDisplay, { item, compact: true })
|
|
1634
|
+
] });
|
|
1635
|
+
}
|
|
1636
|
+
function PriceDisplay({ item, compact = false }) {
|
|
1637
|
+
if (item.priceNote) {
|
|
1638
|
+
return /* @__PURE__ */ jsx10("span", { className: `text-primary-600 font-medium ${compact ? "text-sm" : ""}`, children: item.priceNote });
|
|
1639
|
+
}
|
|
1640
|
+
if (item.price) {
|
|
1641
|
+
return /* @__PURE__ */ jsxs10("span", { className: `text-primary-600 font-medium ${compact ? "text-sm" : ""}`, children: [
|
|
1642
|
+
"$",
|
|
1643
|
+
item.price.toFixed(2)
|
|
1644
|
+
] });
|
|
1645
|
+
}
|
|
1646
|
+
return null;
|
|
1647
|
+
}
|
|
1648
|
+
function DietaryBadges({ dietary }) {
|
|
1649
|
+
const dietaryIcons = {
|
|
1650
|
+
vegetarian: "V",
|
|
1651
|
+
vegan: "VG",
|
|
1652
|
+
"gluten-free": "GF",
|
|
1653
|
+
"dairy-free": "DF",
|
|
1654
|
+
"nut-free": "NF",
|
|
1655
|
+
spicy: "\u{1F336}",
|
|
1656
|
+
halal: "H",
|
|
1657
|
+
kosher: "K"
|
|
1658
|
+
};
|
|
1659
|
+
return /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-1", children: dietary.map((diet) => /* @__PURE__ */ jsx10(
|
|
1660
|
+
"span",
|
|
1661
|
+
{
|
|
1662
|
+
className: "px-1.5 py-0.5 bg-green-100 text-green-700 text-xs font-medium rounded",
|
|
1663
|
+
title: diet,
|
|
1664
|
+
children: dietaryIcons[diet.toLowerCase()] || diet
|
|
1665
|
+
},
|
|
1666
|
+
diet
|
|
1667
|
+
)) });
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// src/components/sections/TrustBadges.tsx
|
|
1671
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1672
|
+
function TrustBadges({
|
|
1673
|
+
badges,
|
|
1674
|
+
title = "Certifications & Affiliations",
|
|
1675
|
+
subtitle,
|
|
1676
|
+
variant = "grid",
|
|
1677
|
+
columns = 4,
|
|
1678
|
+
showDescriptions = false,
|
|
1679
|
+
className = "",
|
|
1680
|
+
id = "certifications"
|
|
1681
|
+
}) {
|
|
1682
|
+
const sortedBadges = [...badges].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
1683
|
+
const gridCols = {
|
|
1684
|
+
3: "grid-cols-2 sm:grid-cols-3",
|
|
1685
|
+
4: "grid-cols-2 sm:grid-cols-4",
|
|
1686
|
+
5: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5",
|
|
1687
|
+
6: "grid-cols-3 sm:grid-cols-6"
|
|
1688
|
+
};
|
|
1689
|
+
if (variant === "inline") {
|
|
1690
|
+
return /* @__PURE__ */ jsx11("section", { id, className: `py-12 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ jsxs11("div", { className: "container mx-auto px-4", children: [
|
|
1691
|
+
title && /* @__PURE__ */ jsx11("p", { className: "text-center text-sm text-gray-500 mb-6", children: title }),
|
|
1692
|
+
/* @__PURE__ */ jsx11("div", { className: "flex flex-wrap justify-center items-center gap-8 md:gap-12", children: sortedBadges.map((badge) => /* @__PURE__ */ jsx11(InlineBadge, { badge }, badge.id || badge.name)) })
|
|
1693
|
+
] }) });
|
|
1694
|
+
}
|
|
1695
|
+
if (variant === "cards") {
|
|
1696
|
+
return /* @__PURE__ */ jsx11("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs11("div", { className: "container mx-auto px-4", children: [
|
|
1697
|
+
/* @__PURE__ */ jsxs11("div", { className: "text-center mb-12", children: [
|
|
1698
|
+
/* @__PURE__ */ jsx11("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1699
|
+
subtitle && /* @__PURE__ */ jsx11("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1700
|
+
] }),
|
|
1701
|
+
/* @__PURE__ */ jsx11("div", { className: `grid gap-6 ${gridCols[columns]}`, children: sortedBadges.map((badge) => /* @__PURE__ */ jsx11(
|
|
1702
|
+
CardBadge,
|
|
1703
|
+
{
|
|
1704
|
+
badge,
|
|
1705
|
+
showDescription: showDescriptions
|
|
1387
1706
|
},
|
|
1388
|
-
|
|
1389
|
-
)
|
|
1390
|
-
}) })
|
|
1707
|
+
badge.id || badge.name
|
|
1708
|
+
)) })
|
|
1709
|
+
] }) });
|
|
1710
|
+
}
|
|
1711
|
+
return /* @__PURE__ */ jsx11("section", { id, className: `py-16 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ jsxs11("div", { className: "container mx-auto px-4", children: [
|
|
1712
|
+
/* @__PURE__ */ jsxs11("div", { className: "text-center mb-12", children: [
|
|
1713
|
+
/* @__PURE__ */ jsx11("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1714
|
+
subtitle && /* @__PURE__ */ jsx11("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1715
|
+
] }),
|
|
1716
|
+
/* @__PURE__ */ jsx11("div", { className: `grid gap-8 ${gridCols[columns]}`, children: sortedBadges.map((badge) => /* @__PURE__ */ jsx11(
|
|
1717
|
+
GridBadge,
|
|
1718
|
+
{
|
|
1719
|
+
badge,
|
|
1720
|
+
showDescription: showDescriptions
|
|
1721
|
+
},
|
|
1722
|
+
badge.id || badge.name
|
|
1723
|
+
)) })
|
|
1391
1724
|
] }) });
|
|
1392
1725
|
}
|
|
1726
|
+
function InlineBadge({ badge }) {
|
|
1727
|
+
const Wrapper = badge.link ? "a" : "div";
|
|
1728
|
+
const wrapperProps = badge.link ? { href: badge.link, target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1729
|
+
return /* @__PURE__ */ jsx11(
|
|
1730
|
+
Wrapper,
|
|
1731
|
+
{
|
|
1732
|
+
...wrapperProps,
|
|
1733
|
+
className: "flex items-center opacity-70 hover:opacity-100 transition-opacity",
|
|
1734
|
+
title: badge.name,
|
|
1735
|
+
children: badge.imageUrl ? /* @__PURE__ */ jsx11(
|
|
1736
|
+
"img",
|
|
1737
|
+
{
|
|
1738
|
+
src: badge.imageUrl,
|
|
1739
|
+
alt: badge.name,
|
|
1740
|
+
className: "h-10 md:h-12 w-auto object-contain grayscale hover:grayscale-0 transition-all"
|
|
1741
|
+
}
|
|
1742
|
+
) : /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-gray-600", children: [
|
|
1743
|
+
badge.icon && /* @__PURE__ */ jsx11(BadgeIcon, { icon: badge.icon, className: "w-8 h-8" }),
|
|
1744
|
+
/* @__PURE__ */ jsx11("span", { className: "text-sm font-medium", children: badge.name })
|
|
1745
|
+
] })
|
|
1746
|
+
}
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
function GridBadge({
|
|
1750
|
+
badge,
|
|
1751
|
+
showDescription
|
|
1752
|
+
}) {
|
|
1753
|
+
const Wrapper = badge.link ? "a" : "div";
|
|
1754
|
+
const wrapperProps = badge.link ? { href: badge.link, target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1755
|
+
return /* @__PURE__ */ jsxs11(
|
|
1756
|
+
Wrapper,
|
|
1757
|
+
{
|
|
1758
|
+
...wrapperProps,
|
|
1759
|
+
className: "flex flex-col items-center text-center group",
|
|
1760
|
+
children: [
|
|
1761
|
+
/* @__PURE__ */ jsx11("div", { className: "w-20 h-20 mb-3 flex items-center justify-center", children: badge.imageUrl ? /* @__PURE__ */ jsx11(
|
|
1762
|
+
"img",
|
|
1763
|
+
{
|
|
1764
|
+
src: badge.imageUrl,
|
|
1765
|
+
alt: badge.name,
|
|
1766
|
+
className: "max-w-full max-h-full object-contain group-hover:scale-110 transition-transform"
|
|
1767
|
+
}
|
|
1768
|
+
) : /* @__PURE__ */ jsx11(
|
|
1769
|
+
BadgeIcon,
|
|
1770
|
+
{
|
|
1771
|
+
icon: badge.icon || "certificate",
|
|
1772
|
+
className: "w-16 h-16 text-primary-600 group-hover:scale-110 transition-transform"
|
|
1773
|
+
}
|
|
1774
|
+
) }),
|
|
1775
|
+
/* @__PURE__ */ jsx11("h3", { className: "font-semibold text-gray-900 text-sm", children: badge.name }),
|
|
1776
|
+
badge.year && /* @__PURE__ */ jsxs11("span", { className: "text-xs text-gray-500", children: [
|
|
1777
|
+
"Since ",
|
|
1778
|
+
badge.year
|
|
1779
|
+
] }),
|
|
1780
|
+
showDescription && badge.description && /* @__PURE__ */ jsx11("p", { className: "text-sm text-gray-600 mt-2", children: badge.description })
|
|
1781
|
+
]
|
|
1782
|
+
}
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1785
|
+
function CardBadge({
|
|
1786
|
+
badge,
|
|
1787
|
+
showDescription
|
|
1788
|
+
}) {
|
|
1789
|
+
const Wrapper = badge.link ? "a" : "div";
|
|
1790
|
+
const wrapperProps = badge.link ? { href: badge.link, target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1791
|
+
return /* @__PURE__ */ jsxs11(
|
|
1792
|
+
Wrapper,
|
|
1793
|
+
{
|
|
1794
|
+
...wrapperProps,
|
|
1795
|
+
className: "bg-white rounded-lg shadow p-6 flex flex-col items-center text-center hover:shadow-md transition-shadow",
|
|
1796
|
+
children: [
|
|
1797
|
+
/* @__PURE__ */ jsx11("div", { className: "w-16 h-16 mb-4 flex items-center justify-center", children: badge.imageUrl ? /* @__PURE__ */ jsx11(
|
|
1798
|
+
"img",
|
|
1799
|
+
{
|
|
1800
|
+
src: badge.imageUrl,
|
|
1801
|
+
alt: badge.name,
|
|
1802
|
+
className: "max-w-full max-h-full object-contain"
|
|
1803
|
+
}
|
|
1804
|
+
) : /* @__PURE__ */ jsx11(BadgeIcon, { icon: badge.icon || "certificate", className: "w-12 h-12 text-primary-600" }) }),
|
|
1805
|
+
/* @__PURE__ */ jsx11("h3", { className: "font-semibold text-gray-900", children: badge.name }),
|
|
1806
|
+
badge.year && /* @__PURE__ */ jsxs11("span", { className: "text-xs text-gray-500 mt-1", children: [
|
|
1807
|
+
"Since ",
|
|
1808
|
+
badge.year
|
|
1809
|
+
] }),
|
|
1810
|
+
showDescription && badge.description && /* @__PURE__ */ jsx11("p", { className: "text-sm text-gray-600 mt-2", children: badge.description })
|
|
1811
|
+
]
|
|
1812
|
+
}
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
function BadgeIcon({ icon, className = "" }) {
|
|
1816
|
+
const icons = {
|
|
1817
|
+
certificate: /* @__PURE__ */ jsx11("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" }) }),
|
|
1818
|
+
shield: /* @__PURE__ */ jsx11("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" }) }),
|
|
1819
|
+
star: /* @__PURE__ */ jsx11("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" }) }),
|
|
1820
|
+
award: /* @__PURE__ */ jsx11("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" }) }),
|
|
1821
|
+
check: /* @__PURE__ */ jsx11("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
1822
|
+
verified: /* @__PURE__ */ jsx11("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" }) })
|
|
1823
|
+
};
|
|
1824
|
+
return icons[icon] || icons.certificate;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// src/components/sections/BeforeAfter.tsx
|
|
1828
|
+
import { useState as useState5, useRef, useCallback } from "react";
|
|
1829
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1830
|
+
function BeforeAfter({
|
|
1831
|
+
items,
|
|
1832
|
+
title = "Our Work",
|
|
1833
|
+
subtitle = "See the transformation",
|
|
1834
|
+
columns = 2,
|
|
1835
|
+
variant = "slider",
|
|
1836
|
+
showCategories = true,
|
|
1837
|
+
className = "",
|
|
1838
|
+
id = "portfolio"
|
|
1839
|
+
}) {
|
|
1840
|
+
const categories = showCategories ? [...new Set(items.filter((item) => item.category).map((item) => item.category))] : [];
|
|
1841
|
+
const [activeCategory, setActiveCategory] = useState5(null);
|
|
1842
|
+
const displayedItems = activeCategory ? items.filter((item) => item.category === activeCategory) : items;
|
|
1843
|
+
const sortedItems = [...displayedItems].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
1844
|
+
const gridCols = {
|
|
1845
|
+
1: "max-w-2xl mx-auto",
|
|
1846
|
+
2: "md:grid-cols-2",
|
|
1847
|
+
3: "md:grid-cols-2 lg:grid-cols-3"
|
|
1848
|
+
};
|
|
1849
|
+
return /* @__PURE__ */ jsx12("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ jsxs12("div", { className: "container mx-auto px-4", children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs12("div", { className: "text-center mb-12", children: [
|
|
1851
|
+
/* @__PURE__ */ jsx12("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1852
|
+
subtitle && /* @__PURE__ */ jsx12("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1853
|
+
] }),
|
|
1854
|
+
showCategories && categories.length > 1 && /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap justify-center gap-2 mb-8", children: [
|
|
1855
|
+
/* @__PURE__ */ jsx12(
|
|
1856
|
+
"button",
|
|
1857
|
+
{
|
|
1858
|
+
onClick: () => setActiveCategory(null),
|
|
1859
|
+
className: `px-4 py-2 rounded-full text-sm font-medium transition-colors ${activeCategory === null ? "bg-primary-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
|
|
1860
|
+
children: "All"
|
|
1861
|
+
}
|
|
1862
|
+
),
|
|
1863
|
+
categories.map((category) => /* @__PURE__ */ jsx12(
|
|
1864
|
+
"button",
|
|
1865
|
+
{
|
|
1866
|
+
onClick: () => setActiveCategory(category),
|
|
1867
|
+
className: `px-4 py-2 rounded-full text-sm font-medium transition-colors ${activeCategory === category ? "bg-primary-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
|
|
1868
|
+
children: category
|
|
1869
|
+
},
|
|
1870
|
+
category
|
|
1871
|
+
))
|
|
1872
|
+
] }),
|
|
1873
|
+
/* @__PURE__ */ jsx12("div", { className: `grid gap-8 ${gridCols[columns]}`, children: sortedItems.map((item) => /* @__PURE__ */ jsx12(
|
|
1874
|
+
BeforeAfterCard,
|
|
1875
|
+
{
|
|
1876
|
+
item,
|
|
1877
|
+
variant
|
|
1878
|
+
},
|
|
1879
|
+
item.id || `${item.beforeImageUrl}-${item.afterImageUrl}`
|
|
1880
|
+
)) })
|
|
1881
|
+
] }) });
|
|
1882
|
+
}
|
|
1883
|
+
function BeforeAfterCard({
|
|
1884
|
+
item,
|
|
1885
|
+
variant
|
|
1886
|
+
}) {
|
|
1887
|
+
if (variant === "side-by-side") {
|
|
1888
|
+
return /* @__PURE__ */ jsx12(SideBySideCard, { item });
|
|
1889
|
+
}
|
|
1890
|
+
if (variant === "stacked") {
|
|
1891
|
+
return /* @__PURE__ */ jsx12(StackedCard, { item });
|
|
1892
|
+
}
|
|
1893
|
+
return /* @__PURE__ */ jsx12(SliderCard, { item });
|
|
1894
|
+
}
|
|
1895
|
+
function SliderCard({ item }) {
|
|
1896
|
+
const [sliderPosition, setSliderPosition] = useState5(50);
|
|
1897
|
+
const containerRef = useRef(null);
|
|
1898
|
+
const isDragging = useRef(false);
|
|
1899
|
+
const handleMove = useCallback((clientX) => {
|
|
1900
|
+
if (!containerRef.current) return;
|
|
1901
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
1902
|
+
const x = clientX - rect.left;
|
|
1903
|
+
const percentage = Math.max(0, Math.min(100, x / rect.width * 100));
|
|
1904
|
+
setSliderPosition(percentage);
|
|
1905
|
+
}, []);
|
|
1906
|
+
const handleMouseDown = () => {
|
|
1907
|
+
isDragging.current = true;
|
|
1908
|
+
};
|
|
1909
|
+
const handleMouseUp = () => {
|
|
1910
|
+
isDragging.current = false;
|
|
1911
|
+
};
|
|
1912
|
+
const handleMouseMove = (e) => {
|
|
1913
|
+
if (isDragging.current) {
|
|
1914
|
+
handleMove(e.clientX);
|
|
1915
|
+
}
|
|
1916
|
+
};
|
|
1917
|
+
const handleTouchMove = (e) => {
|
|
1918
|
+
handleMove(e.touches[0].clientX);
|
|
1919
|
+
};
|
|
1920
|
+
return /* @__PURE__ */ jsxs12("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: [
|
|
1921
|
+
/* @__PURE__ */ jsxs12(
|
|
1922
|
+
"div",
|
|
1923
|
+
{
|
|
1924
|
+
ref: containerRef,
|
|
1925
|
+
className: "relative aspect-[4/3] cursor-ew-resize select-none overflow-hidden",
|
|
1926
|
+
onMouseDown: handleMouseDown,
|
|
1927
|
+
onMouseUp: handleMouseUp,
|
|
1928
|
+
onMouseLeave: handleMouseUp,
|
|
1929
|
+
onMouseMove: handleMouseMove,
|
|
1930
|
+
onTouchMove: handleTouchMove,
|
|
1931
|
+
children: [
|
|
1932
|
+
/* @__PURE__ */ jsx12(
|
|
1933
|
+
"img",
|
|
1934
|
+
{
|
|
1935
|
+
src: item.afterImageUrl,
|
|
1936
|
+
alt: item.afterAlt || "After",
|
|
1937
|
+
className: "absolute inset-0 w-full h-full object-cover"
|
|
1938
|
+
}
|
|
1939
|
+
),
|
|
1940
|
+
/* @__PURE__ */ jsx12(
|
|
1941
|
+
"div",
|
|
1942
|
+
{
|
|
1943
|
+
className: "absolute inset-0 overflow-hidden",
|
|
1944
|
+
style: { width: `${sliderPosition}%` },
|
|
1945
|
+
children: /* @__PURE__ */ jsx12(
|
|
1946
|
+
"img",
|
|
1947
|
+
{
|
|
1948
|
+
src: item.beforeImageUrl,
|
|
1949
|
+
alt: item.beforeAlt || "Before",
|
|
1950
|
+
className: "absolute inset-0 w-full h-full object-cover",
|
|
1951
|
+
style: { width: containerRef.current?.offsetWidth || "100%" }
|
|
1952
|
+
}
|
|
1953
|
+
)
|
|
1954
|
+
}
|
|
1955
|
+
),
|
|
1956
|
+
/* @__PURE__ */ jsx12(
|
|
1957
|
+
"div",
|
|
1958
|
+
{
|
|
1959
|
+
className: "absolute top-0 bottom-0 w-1 bg-white shadow-lg cursor-ew-resize",
|
|
1960
|
+
style: { left: `${sliderPosition}%`, transform: "translateX(-50%)" },
|
|
1961
|
+
children: /* @__PURE__ */ jsx12("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center", children: /* @__PURE__ */ jsx12("svg", { className: "w-6 h-6 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx12("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 9l4-4 4 4m0 6l-4 4-4-4" }) }) })
|
|
1962
|
+
}
|
|
1963
|
+
),
|
|
1964
|
+
/* @__PURE__ */ jsx12("div", { className: "absolute bottom-4 left-4 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "Before" }),
|
|
1965
|
+
/* @__PURE__ */ jsx12("div", { className: "absolute bottom-4 right-4 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "After" })
|
|
1966
|
+
]
|
|
1967
|
+
}
|
|
1968
|
+
),
|
|
1969
|
+
(item.title || item.description) && /* @__PURE__ */ jsxs12("div", { className: "p-4", children: [
|
|
1970
|
+
item.title && /* @__PURE__ */ jsx12("h3", { className: "text-lg font-semibold text-gray-900 mb-1", children: item.title }),
|
|
1971
|
+
item.description && /* @__PURE__ */ jsx12("p", { className: "text-gray-600 text-sm", children: item.description })
|
|
1972
|
+
] })
|
|
1973
|
+
] });
|
|
1974
|
+
}
|
|
1975
|
+
function SideBySideCard({ item }) {
|
|
1976
|
+
return /* @__PURE__ */ jsxs12("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: [
|
|
1977
|
+
/* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-2", children: [
|
|
1978
|
+
/* @__PURE__ */ jsxs12("div", { className: "relative aspect-square", children: [
|
|
1979
|
+
/* @__PURE__ */ jsx12(
|
|
1980
|
+
"img",
|
|
1981
|
+
{
|
|
1982
|
+
src: item.beforeImageUrl,
|
|
1983
|
+
alt: item.beforeAlt || "Before",
|
|
1984
|
+
className: "w-full h-full object-cover"
|
|
1985
|
+
}
|
|
1986
|
+
),
|
|
1987
|
+
/* @__PURE__ */ jsx12("span", { className: "absolute bottom-2 left-2 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "Before" })
|
|
1988
|
+
] }),
|
|
1989
|
+
/* @__PURE__ */ jsxs12("div", { className: "relative aspect-square", children: [
|
|
1990
|
+
/* @__PURE__ */ jsx12(
|
|
1991
|
+
"img",
|
|
1992
|
+
{
|
|
1993
|
+
src: item.afterImageUrl,
|
|
1994
|
+
alt: item.afterAlt || "After",
|
|
1995
|
+
className: "w-full h-full object-cover"
|
|
1996
|
+
}
|
|
1997
|
+
),
|
|
1998
|
+
/* @__PURE__ */ jsx12("span", { className: "absolute bottom-2 right-2 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "After" })
|
|
1999
|
+
] })
|
|
2000
|
+
] }),
|
|
2001
|
+
(item.title || item.description) && /* @__PURE__ */ jsxs12("div", { className: "p-4", children: [
|
|
2002
|
+
item.title && /* @__PURE__ */ jsx12("h3", { className: "text-lg font-semibold text-gray-900 mb-1", children: item.title }),
|
|
2003
|
+
item.description && /* @__PURE__ */ jsx12("p", { className: "text-gray-600 text-sm", children: item.description })
|
|
2004
|
+
] })
|
|
2005
|
+
] });
|
|
2006
|
+
}
|
|
2007
|
+
function StackedCard({ item }) {
|
|
2008
|
+
const [showAfter, setShowAfter] = useState5(false);
|
|
2009
|
+
return /* @__PURE__ */ jsxs12("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: [
|
|
2010
|
+
/* @__PURE__ */ jsxs12(
|
|
2011
|
+
"div",
|
|
2012
|
+
{
|
|
2013
|
+
className: "relative aspect-[4/3] cursor-pointer",
|
|
2014
|
+
onMouseEnter: () => setShowAfter(true),
|
|
2015
|
+
onMouseLeave: () => setShowAfter(false),
|
|
2016
|
+
onClick: () => setShowAfter(!showAfter),
|
|
2017
|
+
children: [
|
|
2018
|
+
/* @__PURE__ */ jsx12(
|
|
2019
|
+
"img",
|
|
2020
|
+
{
|
|
2021
|
+
src: item.beforeImageUrl,
|
|
2022
|
+
alt: item.beforeAlt || "Before",
|
|
2023
|
+
className: `absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${showAfter ? "opacity-0" : "opacity-100"}`
|
|
2024
|
+
}
|
|
2025
|
+
),
|
|
2026
|
+
/* @__PURE__ */ jsx12(
|
|
2027
|
+
"img",
|
|
2028
|
+
{
|
|
2029
|
+
src: item.afterImageUrl,
|
|
2030
|
+
alt: item.afterAlt || "After",
|
|
2031
|
+
className: `absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${showAfter ? "opacity-100" : "opacity-0"}`
|
|
2032
|
+
}
|
|
2033
|
+
),
|
|
2034
|
+
/* @__PURE__ */ jsxs12("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 px-3 py-1 bg-black/60 text-white text-sm rounded", children: [
|
|
2035
|
+
showAfter ? "After" : "Before",
|
|
2036
|
+
" (hover to toggle)"
|
|
2037
|
+
] })
|
|
2038
|
+
]
|
|
2039
|
+
}
|
|
2040
|
+
),
|
|
2041
|
+
(item.title || item.description) && /* @__PURE__ */ jsxs12("div", { className: "p-4", children: [
|
|
2042
|
+
item.title && /* @__PURE__ */ jsx12("h3", { className: "text-lg font-semibold text-gray-900 mb-1", children: item.title }),
|
|
2043
|
+
item.description && /* @__PURE__ */ jsx12("p", { className: "text-gray-600 text-sm", children: item.description })
|
|
2044
|
+
] })
|
|
2045
|
+
] });
|
|
2046
|
+
}
|
|
1393
2047
|
|
|
1394
2048
|
// src/components/ui/Button.tsx
|
|
1395
|
-
import { Fragment as Fragment2, jsx as
|
|
2049
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1396
2050
|
function getButtonClasses(props) {
|
|
1397
2051
|
const { variant = "primary", size = "md", fullWidth, disabled } = props;
|
|
1398
2052
|
const baseClasses = [
|
|
@@ -1434,16 +2088,16 @@ function Button({
|
|
|
1434
2088
|
...props
|
|
1435
2089
|
}) {
|
|
1436
2090
|
const classes = getButtonClasses({ variant, size, fullWidth, disabled: disabled || loading });
|
|
1437
|
-
return /* @__PURE__ */
|
|
2091
|
+
return /* @__PURE__ */ jsx13(
|
|
1438
2092
|
"button",
|
|
1439
2093
|
{
|
|
1440
2094
|
className: `${classes} ${className}`,
|
|
1441
2095
|
disabled: disabled || loading,
|
|
1442
2096
|
...props,
|
|
1443
|
-
children: loading ? /* @__PURE__ */
|
|
1444
|
-
leftIcon && /* @__PURE__ */
|
|
2097
|
+
children: loading ? /* @__PURE__ */ jsx13(LoadingSpinner, { size }) : /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
2098
|
+
leftIcon && /* @__PURE__ */ jsx13("span", { className: "mr-2", children: leftIcon }),
|
|
1445
2099
|
children,
|
|
1446
|
-
rightIcon && /* @__PURE__ */
|
|
2100
|
+
rightIcon && /* @__PURE__ */ jsx13("span", { className: "ml-2", children: rightIcon })
|
|
1447
2101
|
] })
|
|
1448
2102
|
}
|
|
1449
2103
|
);
|
|
@@ -1454,7 +2108,7 @@ function LoadingSpinner({ size }) {
|
|
|
1454
2108
|
md: "w-5 h-5",
|
|
1455
2109
|
lg: "w-6 h-6"
|
|
1456
2110
|
};
|
|
1457
|
-
return /* @__PURE__ */
|
|
2111
|
+
return /* @__PURE__ */ jsxs13(
|
|
1458
2112
|
"svg",
|
|
1459
2113
|
{
|
|
1460
2114
|
className: `animate-spin ${sizeClasses[size || "md"]}`,
|
|
@@ -1462,7 +2116,7 @@ function LoadingSpinner({ size }) {
|
|
|
1462
2116
|
fill: "none",
|
|
1463
2117
|
viewBox: "0 0 24 24",
|
|
1464
2118
|
children: [
|
|
1465
|
-
/* @__PURE__ */
|
|
2119
|
+
/* @__PURE__ */ jsx13(
|
|
1466
2120
|
"circle",
|
|
1467
2121
|
{
|
|
1468
2122
|
className: "opacity-25",
|
|
@@ -1473,7 +2127,7 @@ function LoadingSpinner({ size }) {
|
|
|
1473
2127
|
strokeWidth: "4"
|
|
1474
2128
|
}
|
|
1475
2129
|
),
|
|
1476
|
-
/* @__PURE__ */
|
|
2130
|
+
/* @__PURE__ */ jsx13(
|
|
1477
2131
|
"path",
|
|
1478
2132
|
{
|
|
1479
2133
|
className: "opacity-75",
|
|
@@ -1496,15 +2150,15 @@ function ButtonLink({
|
|
|
1496
2150
|
...props
|
|
1497
2151
|
}) {
|
|
1498
2152
|
const classes = getButtonClasses({ variant, size, fullWidth, disabled: false });
|
|
1499
|
-
return /* @__PURE__ */
|
|
1500
|
-
leftIcon && /* @__PURE__ */
|
|
2153
|
+
return /* @__PURE__ */ jsxs13("a", { className: `${classes} ${className}`, ...props, children: [
|
|
2154
|
+
leftIcon && /* @__PURE__ */ jsx13("span", { className: "mr-2", children: leftIcon }),
|
|
1501
2155
|
children,
|
|
1502
|
-
rightIcon && /* @__PURE__ */
|
|
2156
|
+
rightIcon && /* @__PURE__ */ jsx13("span", { className: "ml-2", children: rightIcon })
|
|
1503
2157
|
] });
|
|
1504
2158
|
}
|
|
1505
2159
|
|
|
1506
2160
|
// src/components/ui/Card.tsx
|
|
1507
|
-
import { jsx as
|
|
2161
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1508
2162
|
function getCardClasses(props) {
|
|
1509
2163
|
const { variant = "default", padding = "md", hover = false } = props;
|
|
1510
2164
|
const baseClasses = ["rounded-lg", "overflow-hidden"];
|
|
@@ -1537,7 +2191,7 @@ function Card({
|
|
|
1537
2191
|
...props
|
|
1538
2192
|
}) {
|
|
1539
2193
|
const classes = getCardClasses({ variant, padding, hover });
|
|
1540
|
-
return /* @__PURE__ */
|
|
2194
|
+
return /* @__PURE__ */ jsx14("div", { className: `${classes} ${className}`, ...props, children });
|
|
1541
2195
|
}
|
|
1542
2196
|
function CardHeader({
|
|
1543
2197
|
title,
|
|
@@ -1547,13 +2201,13 @@ function CardHeader({
|
|
|
1547
2201
|
className = "",
|
|
1548
2202
|
...props
|
|
1549
2203
|
}) {
|
|
1550
|
-
return /* @__PURE__ */
|
|
1551
|
-
/* @__PURE__ */
|
|
1552
|
-
title && /* @__PURE__ */
|
|
1553
|
-
subtitle && /* @__PURE__ */
|
|
2204
|
+
return /* @__PURE__ */ jsxs14("div", { className: `flex items-start justify-between ${className}`, ...props, children: [
|
|
2205
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
2206
|
+
title && /* @__PURE__ */ jsx14("h3", { className: "text-lg font-semibold text-gray-900", children: title }),
|
|
2207
|
+
subtitle && /* @__PURE__ */ jsx14("p", { className: "mt-1 text-sm text-gray-500", children: subtitle }),
|
|
1554
2208
|
children
|
|
1555
2209
|
] }),
|
|
1556
|
-
action && /* @__PURE__ */
|
|
2210
|
+
action && /* @__PURE__ */ jsx14("div", { className: "flex-shrink-0 ml-4", children: action })
|
|
1557
2211
|
] });
|
|
1558
2212
|
}
|
|
1559
2213
|
function CardBody({
|
|
@@ -1561,14 +2215,14 @@ function CardBody({
|
|
|
1561
2215
|
className = "",
|
|
1562
2216
|
...props
|
|
1563
2217
|
}) {
|
|
1564
|
-
return /* @__PURE__ */
|
|
2218
|
+
return /* @__PURE__ */ jsx14("div", { className: `mt-4 ${className}`, ...props, children });
|
|
1565
2219
|
}
|
|
1566
2220
|
function CardFooter({
|
|
1567
2221
|
children,
|
|
1568
2222
|
className = "",
|
|
1569
2223
|
...props
|
|
1570
2224
|
}) {
|
|
1571
|
-
return /* @__PURE__ */
|
|
2225
|
+
return /* @__PURE__ */ jsx14("div", { className: `mt-4 pt-4 border-t border-gray-100 ${className}`, ...props, children });
|
|
1572
2226
|
}
|
|
1573
2227
|
function CardImage({
|
|
1574
2228
|
aspectRatio = "video",
|
|
@@ -1582,7 +2236,7 @@ function CardImage({
|
|
|
1582
2236
|
wide: "aspect-[2/1]",
|
|
1583
2237
|
auto: ""
|
|
1584
2238
|
};
|
|
1585
|
-
return /* @__PURE__ */
|
|
2239
|
+
return /* @__PURE__ */ jsx14("div", { className: `-m-5 mb-4 ${aspectClasses[aspectRatio]} overflow-hidden`, children: /* @__PURE__ */ jsx14(
|
|
1586
2240
|
"img",
|
|
1587
2241
|
{
|
|
1588
2242
|
className: `w-full h-full object-cover ${className}`,
|
|
@@ -1591,7 +2245,205 @@ function CardImage({
|
|
|
1591
2245
|
}
|
|
1592
2246
|
) });
|
|
1593
2247
|
}
|
|
2248
|
+
|
|
2249
|
+
// src/components/ui/AnimatedSection.tsx
|
|
2250
|
+
import React6, { useEffect, useRef as useRef2, useState as useState6 } from "react";
|
|
2251
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2252
|
+
function AnimatedSection({
|
|
2253
|
+
children,
|
|
2254
|
+
animation = "fade-up",
|
|
2255
|
+
delay = 0,
|
|
2256
|
+
duration = 600,
|
|
2257
|
+
threshold = 0.1,
|
|
2258
|
+
once = true,
|
|
2259
|
+
className = "",
|
|
2260
|
+
as: Component = "div"
|
|
2261
|
+
}) {
|
|
2262
|
+
const ref = useRef2(null);
|
|
2263
|
+
const [isVisible, setIsVisible] = useState6(false);
|
|
2264
|
+
useEffect(() => {
|
|
2265
|
+
const observer = new IntersectionObserver(
|
|
2266
|
+
([entry]) => {
|
|
2267
|
+
if (entry.isIntersecting) {
|
|
2268
|
+
setIsVisible(true);
|
|
2269
|
+
if (once && ref.current) {
|
|
2270
|
+
observer.unobserve(ref.current);
|
|
2271
|
+
}
|
|
2272
|
+
} else if (!once) {
|
|
2273
|
+
setIsVisible(false);
|
|
2274
|
+
}
|
|
2275
|
+
},
|
|
2276
|
+
{ threshold }
|
|
2277
|
+
);
|
|
2278
|
+
if (ref.current) {
|
|
2279
|
+
observer.observe(ref.current);
|
|
2280
|
+
}
|
|
2281
|
+
return () => {
|
|
2282
|
+
if (ref.current) {
|
|
2283
|
+
observer.unobserve(ref.current);
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
}, [threshold, once]);
|
|
2287
|
+
const getAnimationStyles = () => {
|
|
2288
|
+
const baseStyles = {
|
|
2289
|
+
transition: `opacity ${duration}ms ease-out, transform ${duration}ms ease-out`,
|
|
2290
|
+
transitionDelay: `${delay}ms`
|
|
2291
|
+
};
|
|
2292
|
+
if (!isVisible) {
|
|
2293
|
+
switch (animation) {
|
|
2294
|
+
case "fade-up":
|
|
2295
|
+
return { ...baseStyles, opacity: 0, transform: "translateY(30px)" };
|
|
2296
|
+
case "fade-down":
|
|
2297
|
+
return { ...baseStyles, opacity: 0, transform: "translateY(-30px)" };
|
|
2298
|
+
case "fade-left":
|
|
2299
|
+
return { ...baseStyles, opacity: 0, transform: "translateX(30px)" };
|
|
2300
|
+
case "fade-right":
|
|
2301
|
+
return { ...baseStyles, opacity: 0, transform: "translateX(-30px)" };
|
|
2302
|
+
case "zoom":
|
|
2303
|
+
return { ...baseStyles, opacity: 0, transform: "scale(0.95)" };
|
|
2304
|
+
case "none":
|
|
2305
|
+
return {};
|
|
2306
|
+
default:
|
|
2307
|
+
return { ...baseStyles, opacity: 0, transform: "translateY(30px)" };
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
return { ...baseStyles, opacity: 1, transform: "translateY(0) translateX(0) scale(1)" };
|
|
2311
|
+
};
|
|
2312
|
+
if (animation === "none") {
|
|
2313
|
+
return /* @__PURE__ */ jsx15(Component, { className, children });
|
|
2314
|
+
}
|
|
2315
|
+
return /* @__PURE__ */ jsx15(Component, { ref, style: getAnimationStyles(), className, children });
|
|
2316
|
+
}
|
|
2317
|
+
function StaggerContainer({
|
|
2318
|
+
children,
|
|
2319
|
+
staggerDelay = 100,
|
|
2320
|
+
animation = "fade-up",
|
|
2321
|
+
duration = 600,
|
|
2322
|
+
className = ""
|
|
2323
|
+
}) {
|
|
2324
|
+
return /* @__PURE__ */ jsx15("div", { className, children: React6.Children.map(children, (child, index) => /* @__PURE__ */ jsx15(
|
|
2325
|
+
AnimatedSection,
|
|
2326
|
+
{
|
|
2327
|
+
animation,
|
|
2328
|
+
delay: index * staggerDelay,
|
|
2329
|
+
duration,
|
|
2330
|
+
children: child
|
|
2331
|
+
}
|
|
2332
|
+
)) });
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
// src/components/ui/FloatingClaimBanner.tsx
|
|
2336
|
+
import { useState as useState7, useEffect as useEffect2 } from "react";
|
|
2337
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2338
|
+
function FloatingClaimBanner({
|
|
2339
|
+
contactEmail = "andrew@whotookmy.com",
|
|
2340
|
+
siteUrl,
|
|
2341
|
+
businessName,
|
|
2342
|
+
position = "bottom-right",
|
|
2343
|
+
showDelay = 3e3,
|
|
2344
|
+
dismissible = true,
|
|
2345
|
+
message = "Is this your business?",
|
|
2346
|
+
buttonText = "Claim This Site",
|
|
2347
|
+
className = ""
|
|
2348
|
+
}) {
|
|
2349
|
+
const [isVisible, setIsVisible] = useState7(false);
|
|
2350
|
+
const [isDismissed, setIsDismissed] = useState7(false);
|
|
2351
|
+
useEffect2(() => {
|
|
2352
|
+
const dismissed = sessionStorage.getItem("claimBannerDismissed");
|
|
2353
|
+
if (dismissed) {
|
|
2354
|
+
setIsDismissed(true);
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
const timer = setTimeout(() => {
|
|
2358
|
+
setIsVisible(true);
|
|
2359
|
+
}, showDelay);
|
|
2360
|
+
return () => clearTimeout(timer);
|
|
2361
|
+
}, [showDelay]);
|
|
2362
|
+
const handleDismiss = () => {
|
|
2363
|
+
setIsVisible(false);
|
|
2364
|
+
setIsDismissed(true);
|
|
2365
|
+
sessionStorage.setItem("claimBannerDismissed", "true");
|
|
2366
|
+
};
|
|
2367
|
+
const handleClaim = () => {
|
|
2368
|
+
const url = siteUrl || (typeof window !== "undefined" ? window.location.hostname : "unknown");
|
|
2369
|
+
const subject = encodeURIComponent(`Claim my site - ${url}`);
|
|
2370
|
+
const body = encodeURIComponent(
|
|
2371
|
+
`Hi,
|
|
2372
|
+
|
|
2373
|
+
I am the owner of ${businessName || "this business"} and I would like to claim my website at ${url}.
|
|
2374
|
+
|
|
2375
|
+
Please contact me to discuss.
|
|
2376
|
+
|
|
2377
|
+
Thank you!`
|
|
2378
|
+
);
|
|
2379
|
+
window.location.href = `mailto:${contactEmail}?subject=${subject}&body=${body}`;
|
|
2380
|
+
};
|
|
2381
|
+
if (isDismissed || !isVisible) {
|
|
2382
|
+
return null;
|
|
2383
|
+
}
|
|
2384
|
+
const positionClasses = {
|
|
2385
|
+
"bottom-right": "bottom-4 right-4",
|
|
2386
|
+
"bottom-left": "bottom-4 left-4",
|
|
2387
|
+
"bottom-center": "bottom-4 left-1/2 -translate-x-1/2"
|
|
2388
|
+
};
|
|
2389
|
+
return /* @__PURE__ */ jsxs15(
|
|
2390
|
+
"div",
|
|
2391
|
+
{
|
|
2392
|
+
className: `
|
|
2393
|
+
fixed z-50 ${positionClasses[position]}
|
|
2394
|
+
animate-slide-up
|
|
2395
|
+
${className}
|
|
2396
|
+
`,
|
|
2397
|
+
style: {
|
|
2398
|
+
animation: "slideUp 0.5s ease-out"
|
|
2399
|
+
},
|
|
2400
|
+
children: [
|
|
2401
|
+
/* @__PURE__ */ jsx16("div", { className: "bg-white rounded-lg shadow-2xl border border-gray-200 p-4 max-w-sm", children: /* @__PURE__ */ jsxs15("div", { className: "flex items-start gap-3", children: [
|
|
2402
|
+
/* @__PURE__ */ jsx16("div", { className: "flex-shrink-0 w-10 h-10 bg-primary-100 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx16("svg", { className: "w-5 h-5 text-primary-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx16("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }) }),
|
|
2403
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-0", children: [
|
|
2404
|
+
/* @__PURE__ */ jsx16("p", { className: "text-sm font-medium text-gray-900", children: message }),
|
|
2405
|
+
/* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-500 mt-1", children: "We built this site for you. Claim it today!" }),
|
|
2406
|
+
/* @__PURE__ */ jsxs15(
|
|
2407
|
+
"button",
|
|
2408
|
+
{
|
|
2409
|
+
onClick: handleClaim,
|
|
2410
|
+
className: "mt-3 w-full inline-flex items-center justify-center px-4 py-2 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700 transition-colors",
|
|
2411
|
+
children: [
|
|
2412
|
+
/* @__PURE__ */ jsx16("svg", { className: "w-4 h-4 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx16("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" }) }),
|
|
2413
|
+
buttonText
|
|
2414
|
+
]
|
|
2415
|
+
}
|
|
2416
|
+
)
|
|
2417
|
+
] }),
|
|
2418
|
+
dismissible && /* @__PURE__ */ jsx16(
|
|
2419
|
+
"button",
|
|
2420
|
+
{
|
|
2421
|
+
onClick: handleDismiss,
|
|
2422
|
+
className: "flex-shrink-0 text-gray-400 hover:text-gray-600 transition-colors",
|
|
2423
|
+
"aria-label": "Dismiss",
|
|
2424
|
+
children: /* @__PURE__ */ jsx16("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx16("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
2425
|
+
}
|
|
2426
|
+
)
|
|
2427
|
+
] }) }),
|
|
2428
|
+
/* @__PURE__ */ jsx16("style", { jsx: true, children: `
|
|
2429
|
+
@keyframes slideUp {
|
|
2430
|
+
from {
|
|
2431
|
+
opacity: 0;
|
|
2432
|
+
transform: translateY(20px);
|
|
2433
|
+
}
|
|
2434
|
+
to {
|
|
2435
|
+
opacity: 1;
|
|
2436
|
+
transform: translateY(0);
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
` })
|
|
2440
|
+
]
|
|
2441
|
+
}
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
1594
2444
|
export {
|
|
2445
|
+
AnimatedSection,
|
|
2446
|
+
BeforeAfter,
|
|
1595
2447
|
BriefcaseIcon,
|
|
1596
2448
|
Button,
|
|
1597
2449
|
ButtonLink,
|
|
@@ -1607,6 +2459,7 @@ export {
|
|
|
1607
2459
|
ClockIcon,
|
|
1608
2460
|
FAQ,
|
|
1609
2461
|
FacebookIcon,
|
|
2462
|
+
FloatingClaimBanner,
|
|
1610
2463
|
Footer,
|
|
1611
2464
|
Gallery,
|
|
1612
2465
|
Header,
|
|
@@ -1617,13 +2470,16 @@ export {
|
|
|
1617
2470
|
InstagramIcon,
|
|
1618
2471
|
MailIcon,
|
|
1619
2472
|
MapPinIcon,
|
|
2473
|
+
Menu,
|
|
1620
2474
|
MenuIcon,
|
|
1621
2475
|
PhoneIcon,
|
|
1622
2476
|
ScissorsIcon,
|
|
1623
2477
|
Services,
|
|
1624
2478
|
SparklesIcon,
|
|
2479
|
+
StaggerContainer,
|
|
1625
2480
|
StarIcon,
|
|
1626
2481
|
Testimonials,
|
|
2482
|
+
TrustBadges,
|
|
1627
2483
|
UtensilsIcon,
|
|
1628
2484
|
WrenchIcon,
|
|
1629
2485
|
XIcon,
|