@aws505/sheetsite 1.0.1 → 1.0.3
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 +823 -72
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +808 -72
- package/dist/components/index.mjs.map +1 -1
- package/dist/index.js +194 -74
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +184 -74
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +12 -0
- package/src/components/layout/Footer.tsx +4 -4
- package/src/components/layout/Header.tsx +4 -4
- package/src/components/sections/BeforeAfter.tsx +345 -0
- package/src/components/sections/FAQ.tsx +5 -3
- package/src/components/sections/Gallery.tsx +104 -4
- package/src/components/sections/Hours.tsx +5 -3
- package/src/components/sections/Menu.tsx +312 -0
- package/src/components/sections/Services.tsx +5 -3
- package/src/components/sections/Testimonials.tsx +3 -1
- package/src/components/sections/TrustBadges.tsx +283 -0
- package/src/components/ui/AnimatedSection.tsx +136 -0
package/dist/components/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
9
|
var __export = (target, all) => {
|
|
8
10
|
for (var name in all)
|
|
@@ -16,11 +18,21 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/components/index.ts
|
|
22
32
|
var components_exports = {};
|
|
23
33
|
__export(components_exports, {
|
|
34
|
+
AnimatedSection: () => AnimatedSection,
|
|
35
|
+
BeforeAfter: () => BeforeAfter,
|
|
24
36
|
BriefcaseIcon: () => BriefcaseIcon,
|
|
25
37
|
Button: () => Button,
|
|
26
38
|
ButtonLink: () => ButtonLink,
|
|
@@ -46,13 +58,16 @@ __export(components_exports, {
|
|
|
46
58
|
InstagramIcon: () => InstagramIcon,
|
|
47
59
|
MailIcon: () => MailIcon,
|
|
48
60
|
MapPinIcon: () => MapPinIcon,
|
|
61
|
+
Menu: () => Menu,
|
|
49
62
|
MenuIcon: () => MenuIcon,
|
|
50
63
|
PhoneIcon: () => PhoneIcon,
|
|
51
64
|
ScissorsIcon: () => ScissorsIcon,
|
|
52
65
|
Services: () => Services,
|
|
53
66
|
SparklesIcon: () => SparklesIcon,
|
|
67
|
+
StaggerContainer: () => StaggerContainer,
|
|
54
68
|
StarIcon: () => StarIcon,
|
|
55
69
|
Testimonials: () => Testimonials,
|
|
70
|
+
TrustBadges: () => TrustBadges,
|
|
56
71
|
UtensilsIcon: () => UtensilsIcon,
|
|
57
72
|
WrenchIcon: () => WrenchIcon,
|
|
58
73
|
XIcon: () => XIcon,
|
|
@@ -535,10 +550,10 @@ function Icon({ name, ...props }) {
|
|
|
535
550
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
536
551
|
var defaultNavigation = [
|
|
537
552
|
{ label: "Home", href: "/" },
|
|
538
|
-
{ label: "Services", href: "
|
|
539
|
-
{ label: "
|
|
540
|
-
{ label: "
|
|
541
|
-
{ label: "
|
|
553
|
+
{ label: "Services", href: "/#services" },
|
|
554
|
+
{ label: "Hours", href: "/#hours" },
|
|
555
|
+
{ label: "Reviews", href: "/#reviews" },
|
|
556
|
+
{ label: "FAQ", href: "/#faq" }
|
|
542
557
|
];
|
|
543
558
|
function Header({
|
|
544
559
|
business,
|
|
@@ -631,10 +646,10 @@ function Header({
|
|
|
631
646
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
632
647
|
var defaultQuickLinks = [
|
|
633
648
|
{ label: "Home", href: "/" },
|
|
634
|
-
{ label: "Services", href: "
|
|
635
|
-
{ label: "
|
|
636
|
-
{ label: "
|
|
637
|
-
{ label: "
|
|
649
|
+
{ label: "Services", href: "/#services" },
|
|
650
|
+
{ label: "Hours", href: "/#hours" },
|
|
651
|
+
{ label: "Reviews", href: "/#reviews" },
|
|
652
|
+
{ label: "FAQ", href: "/#faq" }
|
|
638
653
|
];
|
|
639
654
|
var dayNames = {
|
|
640
655
|
monday: "Mon",
|
|
@@ -948,7 +963,8 @@ function Services({
|
|
|
948
963
|
showIcons = true,
|
|
949
964
|
variant = "cards",
|
|
950
965
|
limit,
|
|
951
|
-
className = ""
|
|
966
|
+
className = "",
|
|
967
|
+
id = "services"
|
|
952
968
|
}) {
|
|
953
969
|
const displayedServices = limit ? services.slice(0, limit) : services;
|
|
954
970
|
const gridCols = {
|
|
@@ -957,7 +973,7 @@ function Services({
|
|
|
957
973
|
4: "md:grid-cols-2 lg:grid-cols-4"
|
|
958
974
|
};
|
|
959
975
|
if (variant === "list") {
|
|
960
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { className: `py-16 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
961
977
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SectionHeader, { title, subtitle }),
|
|
962
978
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "max-w-3xl mx-auto divide-y divide-gray-200", children: displayedServices.map((service) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
963
979
|
ServiceListItem,
|
|
@@ -971,7 +987,7 @@ function Services({
|
|
|
971
987
|
] }) });
|
|
972
988
|
}
|
|
973
989
|
if (variant === "minimal") {
|
|
974
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { className: `py-16 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
990
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
975
991
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SectionHeader, { title, subtitle }),
|
|
976
992
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `grid gap-6 ${gridCols[columns]}`, children: displayedServices.map((service) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
977
993
|
ServiceMinimalCard,
|
|
@@ -983,7 +999,7 @@ function Services({
|
|
|
983
999
|
)) })
|
|
984
1000
|
] }) });
|
|
985
1001
|
}
|
|
986
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { className: `py-16 bg-gray-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1002
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { id, className: `py-16 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
987
1003
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SectionHeader, { title, subtitle }),
|
|
988
1004
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `grid gap-6 ${gridCols[columns]}`, children: displayedServices.map((service) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
989
1005
|
ServiceCard,
|
|
@@ -1057,7 +1073,8 @@ function Testimonials({
|
|
|
1057
1073
|
showRatings = true,
|
|
1058
1074
|
variant = "cards",
|
|
1059
1075
|
limit,
|
|
1060
|
-
className = ""
|
|
1076
|
+
className = "",
|
|
1077
|
+
id = "reviews"
|
|
1061
1078
|
}) {
|
|
1062
1079
|
const displayedTestimonials = limit ? testimonials.slice(0, limit) : testimonials;
|
|
1063
1080
|
const gridCols = {
|
|
@@ -1065,7 +1082,7 @@ function Testimonials({
|
|
|
1065
1082
|
2: "md:grid-cols-2 max-w-4xl mx-auto",
|
|
1066
1083
|
3: "md:grid-cols-2 lg:grid-cols-3"
|
|
1067
1084
|
};
|
|
1068
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("section", { className: `py-16 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1069
1086
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-center mb-12", children: [
|
|
1070
1087
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1071
1088
|
subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
@@ -1168,7 +1185,8 @@ function FAQ({
|
|
|
1168
1185
|
variant = "accordion",
|
|
1169
1186
|
defaultOpen = 0,
|
|
1170
1187
|
allowMultiple = false,
|
|
1171
|
-
className = ""
|
|
1188
|
+
className = "",
|
|
1189
|
+
id = "faq"
|
|
1172
1190
|
}) {
|
|
1173
1191
|
const initialOpen = Array.isArray(defaultOpen) ? defaultOpen : [defaultOpen];
|
|
1174
1192
|
const [openItems, setOpenItems] = (0, import_react2.useState)(initialOpen);
|
|
@@ -1184,18 +1202,18 @@ function FAQ({
|
|
|
1184
1202
|
}
|
|
1185
1203
|
};
|
|
1186
1204
|
if (variant === "cards") {
|
|
1187
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { className: `py-16 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1188
1206
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHeader2, { title, subtitle }),
|
|
1189
1207
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "grid md:grid-cols-2 gap-6 max-w-4xl mx-auto", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FAQCard, { item }, item.id || item.question)) })
|
|
1190
1208
|
] }) });
|
|
1191
1209
|
}
|
|
1192
1210
|
if (variant === "simple") {
|
|
1193
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { className: `py-16 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1194
1212
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHeader2, { title, subtitle }),
|
|
1195
1213
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "max-w-3xl mx-auto space-y-8", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FAQSimple, { item }, item.id || item.question)) })
|
|
1196
1214
|
] }) });
|
|
1197
1215
|
}
|
|
1198
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { className: `py-16 bg-gray-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1216
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { id, className: `py-16 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1199
1217
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHeader2, { title, subtitle }),
|
|
1200
1218
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "max-w-3xl mx-auto", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1201
1219
|
FAQAccordionItem,
|
|
@@ -1292,14 +1310,15 @@ function Hours({
|
|
|
1292
1310
|
highlightToday = true,
|
|
1293
1311
|
variant = "card",
|
|
1294
1312
|
timezone,
|
|
1295
|
-
className = ""
|
|
1313
|
+
className = "",
|
|
1314
|
+
id = "hours"
|
|
1296
1315
|
}) {
|
|
1297
1316
|
const todayDay = getTodayDay(timezone);
|
|
1298
1317
|
const sortedHours = [...hours].sort(
|
|
1299
1318
|
(a, b) => dayOrder.indexOf(a.day) - dayOrder.indexOf(b.day)
|
|
1300
1319
|
);
|
|
1301
1320
|
if (variant === "inline") {
|
|
1302
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `flex flex-wrap gap-4 ${className}`, children: sortedHours.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { id, className: `flex flex-wrap gap-4 ${className}`, children: sortedHours.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1303
1322
|
HoursInlineItem,
|
|
1304
1323
|
{
|
|
1305
1324
|
entry,
|
|
@@ -1309,7 +1328,7 @@ function Hours({
|
|
|
1309
1328
|
)) });
|
|
1310
1329
|
}
|
|
1311
1330
|
if (variant === "minimal") {
|
|
1312
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `space-y-1 ${className}`, children: sortedHours.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { id, className: `space-y-1 ${className}`, children: sortedHours.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1313
1332
|
HoursMinimalItem,
|
|
1314
1333
|
{
|
|
1315
1334
|
entry,
|
|
@@ -1318,7 +1337,7 @@ function Hours({
|
|
|
1318
1337
|
entry.day
|
|
1319
1338
|
)) });
|
|
1320
1339
|
}
|
|
1321
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `bg-white rounded-lg shadow p-6 ${className}`, children: [
|
|
1340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { id, className: `bg-white rounded-lg shadow p-6 ${className}`, children: [
|
|
1322
1341
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center mb-4", children: [
|
|
1323
1342
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ClockIcon, { size: 24, className: "text-primary-600 mr-2" }),
|
|
1324
1343
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "text-xl font-semibold text-gray-900", children: title })
|
|
@@ -1406,53 +1425,694 @@ function Gallery({
|
|
|
1406
1425
|
columns = 3,
|
|
1407
1426
|
variant = "grid",
|
|
1408
1427
|
showCaptions = true,
|
|
1428
|
+
enableLightbox = true,
|
|
1409
1429
|
limit,
|
|
1410
|
-
className = ""
|
|
1430
|
+
className = "",
|
|
1431
|
+
id = "gallery"
|
|
1411
1432
|
}) {
|
|
1412
1433
|
const displayedItems = limit ? items.slice(0, limit) : items;
|
|
1413
1434
|
const [failedImages, setFailedImages] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
|
|
1414
|
-
const
|
|
1415
|
-
|
|
1435
|
+
const [lightboxIndex, setLightboxIndex] = (0, import_react3.useState)(null);
|
|
1436
|
+
const handleImageError = (id2) => {
|
|
1437
|
+
setFailedImages((prev) => new Set(prev).add(id2));
|
|
1416
1438
|
};
|
|
1417
1439
|
const gridCols = {
|
|
1418
1440
|
2: "grid-cols-1 sm:grid-cols-2",
|
|
1419
1441
|
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
1420
1442
|
4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"
|
|
1421
1443
|
};
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1444
|
+
const openLightbox = (index) => {
|
|
1445
|
+
if (enableLightbox) {
|
|
1446
|
+
setLightboxIndex(index);
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
1449
|
+
const closeLightbox = () => setLightboxIndex(null);
|
|
1450
|
+
const goToPrevious = () => {
|
|
1451
|
+
if (lightboxIndex !== null) {
|
|
1452
|
+
setLightboxIndex(lightboxIndex === 0 ? displayedItems.length - 1 : lightboxIndex - 1);
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
const goToNext = () => {
|
|
1456
|
+
if (lightboxIndex !== null) {
|
|
1457
|
+
setLightboxIndex(lightboxIndex === displayedItems.length - 1 ? 0 : lightboxIndex + 1);
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: [
|
|
1461
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1462
|
+
(title || subtitle) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-center mb-12", children: [
|
|
1463
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1464
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1465
|
+
] }),
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `grid gap-4 ${gridCols[columns]}`, children: displayedItems.map((item, index) => {
|
|
1467
|
+
const itemId = item.id || item.imageUrl;
|
|
1468
|
+
const hasFailed = failedImages.has(itemId);
|
|
1469
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1470
|
+
"div",
|
|
1471
|
+
{
|
|
1472
|
+
className: `group relative aspect-square overflow-hidden rounded-lg bg-gray-100 ${enableLightbox ? "cursor-pointer" : ""}`,
|
|
1473
|
+
onClick: () => openLightbox(index),
|
|
1474
|
+
role: enableLightbox ? "button" : void 0,
|
|
1475
|
+
tabIndex: enableLightbox ? 0 : void 0,
|
|
1476
|
+
onKeyDown: enableLightbox ? (e) => e.key === "Enter" && openLightbox(index) : void 0,
|
|
1477
|
+
children: hasFailed ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Image unavailable" }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1479
|
+
"img",
|
|
1480
|
+
{
|
|
1481
|
+
src: item.imageUrl,
|
|
1482
|
+
alt: item.alt || "",
|
|
1483
|
+
loading: "lazy",
|
|
1484
|
+
className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105",
|
|
1485
|
+
onError: () => handleImageError(itemId)
|
|
1486
|
+
}
|
|
1487
|
+
),
|
|
1488
|
+
enableLightbox && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-10 h-10 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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" }) }) }),
|
|
1489
|
+
showCaptions && item.caption && !enableLightbox && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("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__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute bottom-0 left-0 right-0 p-4", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-white text-sm", children: item.caption }) }) })
|
|
1490
|
+
] })
|
|
1491
|
+
},
|
|
1492
|
+
itemId
|
|
1493
|
+
);
|
|
1494
|
+
}) })
|
|
1426
1495
|
] }),
|
|
1427
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1496
|
+
enableLightbox && lightboxIndex !== null && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1497
|
+
"div",
|
|
1498
|
+
{
|
|
1499
|
+
className: "fixed inset-0 z-50 bg-black/90 flex items-center justify-center",
|
|
1500
|
+
onClick: closeLightbox,
|
|
1501
|
+
children: [
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1503
|
+
"button",
|
|
1504
|
+
{
|
|
1505
|
+
onClick: closeLightbox,
|
|
1506
|
+
className: "absolute top-4 right-4 text-white hover:text-gray-300 transition-colors z-10",
|
|
1507
|
+
"aria-label": "Close lightbox",
|
|
1508
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-8 h-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
1509
|
+
}
|
|
1510
|
+
),
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1512
|
+
"button",
|
|
1513
|
+
{
|
|
1514
|
+
onClick: (e) => {
|
|
1515
|
+
e.stopPropagation();
|
|
1516
|
+
goToPrevious();
|
|
1517
|
+
},
|
|
1518
|
+
className: "absolute left-4 top-1/2 -translate-y-1/2 text-white hover:text-gray-300 transition-colors z-10",
|
|
1519
|
+
"aria-label": "Previous image",
|
|
1520
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-10 h-10", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
|
|
1521
|
+
}
|
|
1522
|
+
),
|
|
1523
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1524
|
+
"button",
|
|
1525
|
+
{
|
|
1526
|
+
onClick: (e) => {
|
|
1527
|
+
e.stopPropagation();
|
|
1528
|
+
goToNext();
|
|
1529
|
+
},
|
|
1530
|
+
className: "absolute right-4 top-1/2 -translate-y-1/2 text-white hover:text-gray-300 transition-colors z-10",
|
|
1531
|
+
"aria-label": "Next image",
|
|
1532
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { className: "w-10 h-10", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
|
|
1533
|
+
}
|
|
1534
|
+
),
|
|
1535
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1536
|
+
"div",
|
|
1537
|
+
{
|
|
1538
|
+
className: "max-w-5xl max-h-[85vh] mx-4",
|
|
1539
|
+
onClick: (e) => e.stopPropagation(),
|
|
1540
|
+
children: [
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1542
|
+
"img",
|
|
1543
|
+
{
|
|
1544
|
+
src: displayedItems[lightboxIndex].imageUrl,
|
|
1545
|
+
alt: displayedItems[lightboxIndex].alt || "",
|
|
1546
|
+
className: "max-w-full max-h-[85vh] object-contain"
|
|
1547
|
+
}
|
|
1548
|
+
),
|
|
1549
|
+
showCaptions && displayedItems[lightboxIndex].caption && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-white text-center mt-4 text-lg", children: displayedItems[lightboxIndex].caption })
|
|
1550
|
+
]
|
|
1551
|
+
}
|
|
1552
|
+
),
|
|
1553
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 text-white text-sm", children: [
|
|
1554
|
+
lightboxIndex + 1,
|
|
1555
|
+
" / ",
|
|
1556
|
+
displayedItems.length
|
|
1446
1557
|
] })
|
|
1558
|
+
]
|
|
1559
|
+
}
|
|
1560
|
+
)
|
|
1561
|
+
] });
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// src/components/sections/Menu.tsx
|
|
1565
|
+
var import_react4 = require("react");
|
|
1566
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1567
|
+
function Menu({
|
|
1568
|
+
items,
|
|
1569
|
+
title = "Our Menu",
|
|
1570
|
+
subtitle,
|
|
1571
|
+
showCategories = true,
|
|
1572
|
+
showImages = true,
|
|
1573
|
+
showDietary = true,
|
|
1574
|
+
variant = "cards",
|
|
1575
|
+
columns = 2,
|
|
1576
|
+
className = "",
|
|
1577
|
+
id = "menu"
|
|
1578
|
+
}) {
|
|
1579
|
+
const categories = showCategories ? [...new Set(items.filter((item) => item.category).map((item) => item.category))] : [];
|
|
1580
|
+
const [activeCategory, setActiveCategory] = (0, import_react4.useState)(
|
|
1581
|
+
categories.length > 0 ? categories[0] : null
|
|
1582
|
+
);
|
|
1583
|
+
const displayedItems = activeCategory ? items.filter((item) => item.category === activeCategory) : items;
|
|
1584
|
+
const sortedItems = [...displayedItems].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
1585
|
+
const gridCols = {
|
|
1586
|
+
1: "max-w-2xl mx-auto",
|
|
1587
|
+
2: "md:grid-cols-2",
|
|
1588
|
+
3: "md:grid-cols-2 lg:grid-cols-3"
|
|
1589
|
+
};
|
|
1590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-center mb-12", children: [
|
|
1592
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1593
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1594
|
+
] }),
|
|
1595
|
+
showCategories && categories.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap justify-center gap-2 mb-8", children: categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1596
|
+
"button",
|
|
1597
|
+
{
|
|
1598
|
+
onClick: () => setActiveCategory(category),
|
|
1599
|
+
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"}`,
|
|
1600
|
+
children: category
|
|
1601
|
+
},
|
|
1602
|
+
category
|
|
1603
|
+
)) }),
|
|
1604
|
+
variant === "list" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "max-w-3xl mx-auto divide-y divide-gray-200", children: sortedItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1605
|
+
MenuListItem,
|
|
1606
|
+
{
|
|
1607
|
+
item,
|
|
1608
|
+
showImage: showImages,
|
|
1609
|
+
showDietary
|
|
1610
|
+
},
|
|
1611
|
+
item.id || item.name
|
|
1612
|
+
)) }) : variant === "compact" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `grid gap-4 ${gridCols[columns]}`, children: sortedItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1613
|
+
MenuCompactItem,
|
|
1614
|
+
{
|
|
1615
|
+
item,
|
|
1616
|
+
showDietary
|
|
1617
|
+
},
|
|
1618
|
+
item.id || item.name
|
|
1619
|
+
)) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `grid gap-6 ${gridCols[columns]}`, children: sortedItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1620
|
+
MenuCard,
|
|
1621
|
+
{
|
|
1622
|
+
item,
|
|
1623
|
+
showImage: showImages,
|
|
1624
|
+
showDietary
|
|
1625
|
+
},
|
|
1626
|
+
item.id || item.name
|
|
1627
|
+
)) })
|
|
1628
|
+
] }) });
|
|
1629
|
+
}
|
|
1630
|
+
function MenuCard({
|
|
1631
|
+
item,
|
|
1632
|
+
showImage,
|
|
1633
|
+
showDietary
|
|
1634
|
+
}) {
|
|
1635
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow", children: [
|
|
1636
|
+
showImage && item.imageUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "aspect-video relative overflow-hidden", children: [
|
|
1637
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1638
|
+
"img",
|
|
1639
|
+
{
|
|
1640
|
+
src: item.imageUrl,
|
|
1641
|
+
alt: item.name,
|
|
1642
|
+
className: "w-full h-full object-cover"
|
|
1643
|
+
}
|
|
1644
|
+
),
|
|
1645
|
+
item.featured && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "absolute top-2 left-2 px-2 py-1 bg-primary-600 text-white text-xs font-medium rounded", children: "Featured" })
|
|
1646
|
+
] }),
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "p-4", children: [
|
|
1648
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between items-start mb-2", children: [
|
|
1649
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-lg font-semibold text-gray-900", children: item.name }),
|
|
1650
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PriceDisplay, { item })
|
|
1651
|
+
] }),
|
|
1652
|
+
item.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-gray-600 text-sm mb-3", children: item.description }),
|
|
1653
|
+
showDietary && item.dietary && item.dietary.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DietaryBadges, { dietary: item.dietary }),
|
|
1654
|
+
!item.available && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "inline-block mt-2 px-2 py-1 bg-gray-100 text-gray-500 text-xs rounded", children: "Currently unavailable" })
|
|
1655
|
+
] })
|
|
1656
|
+
] });
|
|
1657
|
+
}
|
|
1658
|
+
function MenuListItem({
|
|
1659
|
+
item,
|
|
1660
|
+
showImage,
|
|
1661
|
+
showDietary
|
|
1662
|
+
}) {
|
|
1663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "py-4 flex gap-4", children: [
|
|
1664
|
+
showImage && item.imageUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-20 h-20 flex-shrink-0 rounded-lg overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1665
|
+
"img",
|
|
1666
|
+
{
|
|
1667
|
+
src: item.imageUrl,
|
|
1668
|
+
alt: item.name,
|
|
1669
|
+
className: "w-full h-full object-cover"
|
|
1670
|
+
}
|
|
1671
|
+
) }),
|
|
1672
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between items-start", children: [
|
|
1673
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
1674
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h3", { className: "text-lg font-semibold text-gray-900", children: [
|
|
1675
|
+
item.name,
|
|
1676
|
+
item.featured && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ml-2 px-2 py-0.5 bg-primary-100 text-primary-700 text-xs font-medium rounded", children: "Popular" })
|
|
1677
|
+
] }),
|
|
1678
|
+
item.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-gray-600 text-sm mt-1", children: item.description }),
|
|
1679
|
+
showDietary && item.dietary && item.dietary.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DietaryBadges, { dietary: item.dietary }) })
|
|
1680
|
+
] }),
|
|
1681
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PriceDisplay, { item })
|
|
1682
|
+
] }) })
|
|
1683
|
+
] });
|
|
1684
|
+
}
|
|
1685
|
+
function MenuCompactItem({
|
|
1686
|
+
item,
|
|
1687
|
+
showDietary
|
|
1688
|
+
}) {
|
|
1689
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex justify-between items-center py-2 border-b border-gray-100 last:border-0", children: [
|
|
1690
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1691
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-medium text-gray-900", children: item.name }),
|
|
1692
|
+
item.featured && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "w-2 h-2 bg-primary-500 rounded-full", title: "Popular" }),
|
|
1693
|
+
showDietary && item.dietary && item.dietary.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-xs text-gray-500", children: [
|
|
1694
|
+
"(",
|
|
1695
|
+
item.dietary.join(", "),
|
|
1696
|
+
")"
|
|
1697
|
+
] })
|
|
1698
|
+
] }),
|
|
1699
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PriceDisplay, { item, compact: true })
|
|
1700
|
+
] });
|
|
1701
|
+
}
|
|
1702
|
+
function PriceDisplay({ item, compact = false }) {
|
|
1703
|
+
if (item.priceNote) {
|
|
1704
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `text-primary-600 font-medium ${compact ? "text-sm" : ""}`, children: item.priceNote });
|
|
1705
|
+
}
|
|
1706
|
+
if (item.price) {
|
|
1707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: `text-primary-600 font-medium ${compact ? "text-sm" : ""}`, children: [
|
|
1708
|
+
"$",
|
|
1709
|
+
item.price.toFixed(2)
|
|
1710
|
+
] });
|
|
1711
|
+
}
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1714
|
+
function DietaryBadges({ dietary }) {
|
|
1715
|
+
const dietaryIcons = {
|
|
1716
|
+
vegetarian: "V",
|
|
1717
|
+
vegan: "VG",
|
|
1718
|
+
"gluten-free": "GF",
|
|
1719
|
+
"dairy-free": "DF",
|
|
1720
|
+
"nut-free": "NF",
|
|
1721
|
+
spicy: "\u{1F336}",
|
|
1722
|
+
halal: "H",
|
|
1723
|
+
kosher: "K"
|
|
1724
|
+
};
|
|
1725
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-1", children: dietary.map((diet) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1726
|
+
"span",
|
|
1727
|
+
{
|
|
1728
|
+
className: "px-1.5 py-0.5 bg-green-100 text-green-700 text-xs font-medium rounded",
|
|
1729
|
+
title: diet,
|
|
1730
|
+
children: dietaryIcons[diet.toLowerCase()] || diet
|
|
1731
|
+
},
|
|
1732
|
+
diet
|
|
1733
|
+
)) });
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// src/components/sections/TrustBadges.tsx
|
|
1737
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1738
|
+
function TrustBadges({
|
|
1739
|
+
badges,
|
|
1740
|
+
title = "Certifications & Affiliations",
|
|
1741
|
+
subtitle,
|
|
1742
|
+
variant = "grid",
|
|
1743
|
+
columns = 4,
|
|
1744
|
+
showDescriptions = false,
|
|
1745
|
+
className = "",
|
|
1746
|
+
id = "certifications"
|
|
1747
|
+
}) {
|
|
1748
|
+
const sortedBadges = [...badges].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
1749
|
+
const gridCols = {
|
|
1750
|
+
3: "grid-cols-2 sm:grid-cols-3",
|
|
1751
|
+
4: "grid-cols-2 sm:grid-cols-4",
|
|
1752
|
+
5: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5",
|
|
1753
|
+
6: "grid-cols-3 sm:grid-cols-6"
|
|
1754
|
+
};
|
|
1755
|
+
if (variant === "inline") {
|
|
1756
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("section", { id, className: `py-12 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1757
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-center text-sm text-gray-500 mb-6", children: title }),
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex flex-wrap justify-center items-center gap-8 md:gap-12", children: sortedBadges.map((badge) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(InlineBadge, { badge }, badge.id || badge.name)) })
|
|
1759
|
+
] }) });
|
|
1760
|
+
}
|
|
1761
|
+
if (variant === "cards") {
|
|
1762
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1763
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center mb-12", children: [
|
|
1764
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1765
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1766
|
+
] }),
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: `grid gap-6 ${gridCols[columns]}`, children: sortedBadges.map((badge) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1768
|
+
CardBadge,
|
|
1769
|
+
{
|
|
1770
|
+
badge,
|
|
1771
|
+
showDescription: showDescriptions
|
|
1447
1772
|
},
|
|
1448
|
-
|
|
1449
|
-
)
|
|
1450
|
-
}) })
|
|
1773
|
+
badge.id || badge.name
|
|
1774
|
+
)) })
|
|
1775
|
+
] }) });
|
|
1776
|
+
}
|
|
1777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("section", { id, className: `py-16 scroll-mt-20 bg-gray-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1778
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center mb-12", children: [
|
|
1779
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1780
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1781
|
+
] }),
|
|
1782
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: `grid gap-8 ${gridCols[columns]}`, children: sortedBadges.map((badge) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1783
|
+
GridBadge,
|
|
1784
|
+
{
|
|
1785
|
+
badge,
|
|
1786
|
+
showDescription: showDescriptions
|
|
1787
|
+
},
|
|
1788
|
+
badge.id || badge.name
|
|
1789
|
+
)) })
|
|
1451
1790
|
] }) });
|
|
1452
1791
|
}
|
|
1792
|
+
function InlineBadge({ badge }) {
|
|
1793
|
+
const Wrapper = badge.link ? "a" : "div";
|
|
1794
|
+
const wrapperProps = badge.link ? { href: badge.link, target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1795
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1796
|
+
Wrapper,
|
|
1797
|
+
{
|
|
1798
|
+
...wrapperProps,
|
|
1799
|
+
className: "flex items-center opacity-70 hover:opacity-100 transition-opacity",
|
|
1800
|
+
title: badge.name,
|
|
1801
|
+
children: badge.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1802
|
+
"img",
|
|
1803
|
+
{
|
|
1804
|
+
src: badge.imageUrl,
|
|
1805
|
+
alt: badge.name,
|
|
1806
|
+
className: "h-10 md:h-12 w-auto object-contain grayscale hover:grayscale-0 transition-all"
|
|
1807
|
+
}
|
|
1808
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 text-gray-600", children: [
|
|
1809
|
+
badge.icon && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BadgeIcon, { icon: badge.icon, className: "w-8 h-8" }),
|
|
1810
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm font-medium", children: badge.name })
|
|
1811
|
+
] })
|
|
1812
|
+
}
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
function GridBadge({
|
|
1816
|
+
badge,
|
|
1817
|
+
showDescription
|
|
1818
|
+
}) {
|
|
1819
|
+
const Wrapper = badge.link ? "a" : "div";
|
|
1820
|
+
const wrapperProps = badge.link ? { href: badge.link, target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1822
|
+
Wrapper,
|
|
1823
|
+
{
|
|
1824
|
+
...wrapperProps,
|
|
1825
|
+
className: "flex flex-col items-center text-center group",
|
|
1826
|
+
children: [
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-20 h-20 mb-3 flex items-center justify-center", children: badge.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1828
|
+
"img",
|
|
1829
|
+
{
|
|
1830
|
+
src: badge.imageUrl,
|
|
1831
|
+
alt: badge.name,
|
|
1832
|
+
className: "max-w-full max-h-full object-contain group-hover:scale-110 transition-transform"
|
|
1833
|
+
}
|
|
1834
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1835
|
+
BadgeIcon,
|
|
1836
|
+
{
|
|
1837
|
+
icon: badge.icon || "certificate",
|
|
1838
|
+
className: "w-16 h-16 text-primary-600 group-hover:scale-110 transition-transform"
|
|
1839
|
+
}
|
|
1840
|
+
) }),
|
|
1841
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "font-semibold text-gray-900 text-sm", children: badge.name }),
|
|
1842
|
+
badge.year && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-xs text-gray-500", children: [
|
|
1843
|
+
"Since ",
|
|
1844
|
+
badge.year
|
|
1845
|
+
] }),
|
|
1846
|
+
showDescription && badge.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-gray-600 mt-2", children: badge.description })
|
|
1847
|
+
]
|
|
1848
|
+
}
|
|
1849
|
+
);
|
|
1850
|
+
}
|
|
1851
|
+
function CardBadge({
|
|
1852
|
+
badge,
|
|
1853
|
+
showDescription
|
|
1854
|
+
}) {
|
|
1855
|
+
const Wrapper = badge.link ? "a" : "div";
|
|
1856
|
+
const wrapperProps = badge.link ? { href: badge.link, target: "_blank", rel: "noopener noreferrer" } : {};
|
|
1857
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1858
|
+
Wrapper,
|
|
1859
|
+
{
|
|
1860
|
+
...wrapperProps,
|
|
1861
|
+
className: "bg-white rounded-lg shadow p-6 flex flex-col items-center text-center hover:shadow-md transition-shadow",
|
|
1862
|
+
children: [
|
|
1863
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-16 h-16 mb-4 flex items-center justify-center", children: badge.imageUrl ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1864
|
+
"img",
|
|
1865
|
+
{
|
|
1866
|
+
src: badge.imageUrl,
|
|
1867
|
+
alt: badge.name,
|
|
1868
|
+
className: "max-w-full max-h-full object-contain"
|
|
1869
|
+
}
|
|
1870
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BadgeIcon, { icon: badge.icon || "certificate", className: "w-12 h-12 text-primary-600" }) }),
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "font-semibold text-gray-900", children: badge.name }),
|
|
1872
|
+
badge.year && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-xs text-gray-500 mt-1", children: [
|
|
1873
|
+
"Since ",
|
|
1874
|
+
badge.year
|
|
1875
|
+
] }),
|
|
1876
|
+
showDescription && badge.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-gray-600 mt-2", children: badge.description })
|
|
1877
|
+
]
|
|
1878
|
+
}
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
function BadgeIcon({ icon, className = "" }) {
|
|
1882
|
+
const icons = {
|
|
1883
|
+
certificate: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }) }),
|
|
1884
|
+
shield: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }) }),
|
|
1885
|
+
star: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }) }),
|
|
1886
|
+
award: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }) }),
|
|
1887
|
+
check: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }) }),
|
|
1888
|
+
verified: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { className, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" }) })
|
|
1889
|
+
};
|
|
1890
|
+
return icons[icon] || icons.certificate;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// src/components/sections/BeforeAfter.tsx
|
|
1894
|
+
var import_react5 = require("react");
|
|
1895
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1896
|
+
function BeforeAfter({
|
|
1897
|
+
items,
|
|
1898
|
+
title = "Our Work",
|
|
1899
|
+
subtitle = "See the transformation",
|
|
1900
|
+
columns = 2,
|
|
1901
|
+
variant = "slider",
|
|
1902
|
+
showCategories = true,
|
|
1903
|
+
className = "",
|
|
1904
|
+
id = "portfolio"
|
|
1905
|
+
}) {
|
|
1906
|
+
const categories = showCategories ? [...new Set(items.filter((item) => item.category).map((item) => item.category))] : [];
|
|
1907
|
+
const [activeCategory, setActiveCategory] = (0, import_react5.useState)(null);
|
|
1908
|
+
const displayedItems = activeCategory ? items.filter((item) => item.category === activeCategory) : items;
|
|
1909
|
+
const sortedItems = [...displayedItems].sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
1910
|
+
const gridCols = {
|
|
1911
|
+
1: "max-w-2xl mx-auto",
|
|
1912
|
+
2: "md:grid-cols-2",
|
|
1913
|
+
3: "md:grid-cols-2 lg:grid-cols-3"
|
|
1914
|
+
};
|
|
1915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("section", { id, className: `py-16 scroll-mt-20 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "container mx-auto px-4", children: [
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-center mb-12", children: [
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-gray-900 mb-4", children: title }),
|
|
1918
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-lg text-gray-600 max-w-2xl mx-auto", children: subtitle })
|
|
1919
|
+
] }),
|
|
1920
|
+
showCategories && categories.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap justify-center gap-2 mb-8", children: [
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1922
|
+
"button",
|
|
1923
|
+
{
|
|
1924
|
+
onClick: () => setActiveCategory(null),
|
|
1925
|
+
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"}`,
|
|
1926
|
+
children: "All"
|
|
1927
|
+
}
|
|
1928
|
+
),
|
|
1929
|
+
categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1930
|
+
"button",
|
|
1931
|
+
{
|
|
1932
|
+
onClick: () => setActiveCategory(category),
|
|
1933
|
+
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"}`,
|
|
1934
|
+
children: category
|
|
1935
|
+
},
|
|
1936
|
+
category
|
|
1937
|
+
))
|
|
1938
|
+
] }),
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: `grid gap-8 ${gridCols[columns]}`, children: sortedItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1940
|
+
BeforeAfterCard,
|
|
1941
|
+
{
|
|
1942
|
+
item,
|
|
1943
|
+
variant
|
|
1944
|
+
},
|
|
1945
|
+
item.id || `${item.beforeImageUrl}-${item.afterImageUrl}`
|
|
1946
|
+
)) })
|
|
1947
|
+
] }) });
|
|
1948
|
+
}
|
|
1949
|
+
function BeforeAfterCard({
|
|
1950
|
+
item,
|
|
1951
|
+
variant
|
|
1952
|
+
}) {
|
|
1953
|
+
if (variant === "side-by-side") {
|
|
1954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SideBySideCard, { item });
|
|
1955
|
+
}
|
|
1956
|
+
if (variant === "stacked") {
|
|
1957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StackedCard, { item });
|
|
1958
|
+
}
|
|
1959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SliderCard, { item });
|
|
1960
|
+
}
|
|
1961
|
+
function SliderCard({ item }) {
|
|
1962
|
+
const [sliderPosition, setSliderPosition] = (0, import_react5.useState)(50);
|
|
1963
|
+
const containerRef = (0, import_react5.useRef)(null);
|
|
1964
|
+
const isDragging = (0, import_react5.useRef)(false);
|
|
1965
|
+
const handleMove = (0, import_react5.useCallback)((clientX) => {
|
|
1966
|
+
if (!containerRef.current) return;
|
|
1967
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
1968
|
+
const x = clientX - rect.left;
|
|
1969
|
+
const percentage = Math.max(0, Math.min(100, x / rect.width * 100));
|
|
1970
|
+
setSliderPosition(percentage);
|
|
1971
|
+
}, []);
|
|
1972
|
+
const handleMouseDown = () => {
|
|
1973
|
+
isDragging.current = true;
|
|
1974
|
+
};
|
|
1975
|
+
const handleMouseUp = () => {
|
|
1976
|
+
isDragging.current = false;
|
|
1977
|
+
};
|
|
1978
|
+
const handleMouseMove = (e) => {
|
|
1979
|
+
if (isDragging.current) {
|
|
1980
|
+
handleMove(e.clientX);
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
const handleTouchMove = (e) => {
|
|
1984
|
+
handleMove(e.touches[0].clientX);
|
|
1985
|
+
};
|
|
1986
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: [
|
|
1987
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1988
|
+
"div",
|
|
1989
|
+
{
|
|
1990
|
+
ref: containerRef,
|
|
1991
|
+
className: "relative aspect-[4/3] cursor-ew-resize select-none overflow-hidden",
|
|
1992
|
+
onMouseDown: handleMouseDown,
|
|
1993
|
+
onMouseUp: handleMouseUp,
|
|
1994
|
+
onMouseLeave: handleMouseUp,
|
|
1995
|
+
onMouseMove: handleMouseMove,
|
|
1996
|
+
onTouchMove: handleTouchMove,
|
|
1997
|
+
children: [
|
|
1998
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1999
|
+
"img",
|
|
2000
|
+
{
|
|
2001
|
+
src: item.afterImageUrl,
|
|
2002
|
+
alt: item.afterAlt || "After",
|
|
2003
|
+
className: "absolute inset-0 w-full h-full object-cover"
|
|
2004
|
+
}
|
|
2005
|
+
),
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2007
|
+
"div",
|
|
2008
|
+
{
|
|
2009
|
+
className: "absolute inset-0 overflow-hidden",
|
|
2010
|
+
style: { width: `${sliderPosition}%` },
|
|
2011
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2012
|
+
"img",
|
|
2013
|
+
{
|
|
2014
|
+
src: item.beforeImageUrl,
|
|
2015
|
+
alt: item.beforeAlt || "Before",
|
|
2016
|
+
className: "absolute inset-0 w-full h-full object-cover",
|
|
2017
|
+
style: { width: containerRef.current?.offsetWidth || "100%" }
|
|
2018
|
+
}
|
|
2019
|
+
)
|
|
2020
|
+
}
|
|
2021
|
+
),
|
|
2022
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2023
|
+
"div",
|
|
2024
|
+
{
|
|
2025
|
+
className: "absolute top-0 bottom-0 w-1 bg-white shadow-lg cursor-ew-resize",
|
|
2026
|
+
style: { left: `${sliderPosition}%`, transform: "translateX(-50%)" },
|
|
2027
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("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__ */ (0, import_jsx_runtime12.jsx)("svg", { className: "w-6 h-6 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 9l4-4 4 4m0 6l-4 4-4-4" }) }) })
|
|
2028
|
+
}
|
|
2029
|
+
),
|
|
2030
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute bottom-4 left-4 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "Before" }),
|
|
2031
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute bottom-4 right-4 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "After" })
|
|
2032
|
+
]
|
|
2033
|
+
}
|
|
2034
|
+
),
|
|
2035
|
+
(item.title || item.description) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-4", children: [
|
|
2036
|
+
item.title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-gray-900 mb-1", children: item.title }),
|
|
2037
|
+
item.description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-gray-600 text-sm", children: item.description })
|
|
2038
|
+
] })
|
|
2039
|
+
] });
|
|
2040
|
+
}
|
|
2041
|
+
function SideBySideCard({ item }) {
|
|
2042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: [
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "grid grid-cols-2", children: [
|
|
2044
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative aspect-square", children: [
|
|
2045
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2046
|
+
"img",
|
|
2047
|
+
{
|
|
2048
|
+
src: item.beforeImageUrl,
|
|
2049
|
+
alt: item.beforeAlt || "Before",
|
|
2050
|
+
className: "w-full h-full object-cover"
|
|
2051
|
+
}
|
|
2052
|
+
),
|
|
2053
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "absolute bottom-2 left-2 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "Before" })
|
|
2054
|
+
] }),
|
|
2055
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative aspect-square", children: [
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2057
|
+
"img",
|
|
2058
|
+
{
|
|
2059
|
+
src: item.afterImageUrl,
|
|
2060
|
+
alt: item.afterAlt || "After",
|
|
2061
|
+
className: "w-full h-full object-cover"
|
|
2062
|
+
}
|
|
2063
|
+
),
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "absolute bottom-2 right-2 px-2 py-1 bg-black/60 text-white text-xs rounded", children: "After" })
|
|
2065
|
+
] })
|
|
2066
|
+
] }),
|
|
2067
|
+
(item.title || item.description) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-4", children: [
|
|
2068
|
+
item.title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-gray-900 mb-1", children: item.title }),
|
|
2069
|
+
item.description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-gray-600 text-sm", children: item.description })
|
|
2070
|
+
] })
|
|
2071
|
+
] });
|
|
2072
|
+
}
|
|
2073
|
+
function StackedCard({ item }) {
|
|
2074
|
+
const [showAfter, setShowAfter] = (0, import_react5.useState)(false);
|
|
2075
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: [
|
|
2076
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2077
|
+
"div",
|
|
2078
|
+
{
|
|
2079
|
+
className: "relative aspect-[4/3] cursor-pointer",
|
|
2080
|
+
onMouseEnter: () => setShowAfter(true),
|
|
2081
|
+
onMouseLeave: () => setShowAfter(false),
|
|
2082
|
+
onClick: () => setShowAfter(!showAfter),
|
|
2083
|
+
children: [
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2085
|
+
"img",
|
|
2086
|
+
{
|
|
2087
|
+
src: item.beforeImageUrl,
|
|
2088
|
+
alt: item.beforeAlt || "Before",
|
|
2089
|
+
className: `absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${showAfter ? "opacity-0" : "opacity-100"}`
|
|
2090
|
+
}
|
|
2091
|
+
),
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2093
|
+
"img",
|
|
2094
|
+
{
|
|
2095
|
+
src: item.afterImageUrl,
|
|
2096
|
+
alt: item.afterAlt || "After",
|
|
2097
|
+
className: `absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${showAfter ? "opacity-100" : "opacity-0"}`
|
|
2098
|
+
}
|
|
2099
|
+
),
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("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: [
|
|
2101
|
+
showAfter ? "After" : "Before",
|
|
2102
|
+
" (hover to toggle)"
|
|
2103
|
+
] })
|
|
2104
|
+
]
|
|
2105
|
+
}
|
|
2106
|
+
),
|
|
2107
|
+
(item.title || item.description) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-4", children: [
|
|
2108
|
+
item.title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-gray-900 mb-1", children: item.title }),
|
|
2109
|
+
item.description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-gray-600 text-sm", children: item.description })
|
|
2110
|
+
] })
|
|
2111
|
+
] });
|
|
2112
|
+
}
|
|
1453
2113
|
|
|
1454
2114
|
// src/components/ui/Button.tsx
|
|
1455
|
-
var
|
|
2115
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1456
2116
|
function getButtonClasses(props) {
|
|
1457
2117
|
const { variant = "primary", size = "md", fullWidth, disabled } = props;
|
|
1458
2118
|
const baseClasses = [
|
|
@@ -1494,16 +2154,16 @@ function Button({
|
|
|
1494
2154
|
...props
|
|
1495
2155
|
}) {
|
|
1496
2156
|
const classes = getButtonClasses({ variant, size, fullWidth, disabled: disabled || loading });
|
|
1497
|
-
return /* @__PURE__ */ (0,
|
|
2157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1498
2158
|
"button",
|
|
1499
2159
|
{
|
|
1500
2160
|
className: `${classes} ${className}`,
|
|
1501
2161
|
disabled: disabled || loading,
|
|
1502
2162
|
...props,
|
|
1503
|
-
children: loading ? /* @__PURE__ */ (0,
|
|
1504
|
-
leftIcon && /* @__PURE__ */ (0,
|
|
2163
|
+
children: loading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LoadingSpinner, { size }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2164
|
+
leftIcon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "mr-2", children: leftIcon }),
|
|
1505
2165
|
children,
|
|
1506
|
-
rightIcon && /* @__PURE__ */ (0,
|
|
2166
|
+
rightIcon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ml-2", children: rightIcon })
|
|
1507
2167
|
] })
|
|
1508
2168
|
}
|
|
1509
2169
|
);
|
|
@@ -1514,7 +2174,7 @@ function LoadingSpinner({ size }) {
|
|
|
1514
2174
|
md: "w-5 h-5",
|
|
1515
2175
|
lg: "w-6 h-6"
|
|
1516
2176
|
};
|
|
1517
|
-
return /* @__PURE__ */ (0,
|
|
2177
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1518
2178
|
"svg",
|
|
1519
2179
|
{
|
|
1520
2180
|
className: `animate-spin ${sizeClasses[size || "md"]}`,
|
|
@@ -1522,7 +2182,7 @@ function LoadingSpinner({ size }) {
|
|
|
1522
2182
|
fill: "none",
|
|
1523
2183
|
viewBox: "0 0 24 24",
|
|
1524
2184
|
children: [
|
|
1525
|
-
/* @__PURE__ */ (0,
|
|
2185
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1526
2186
|
"circle",
|
|
1527
2187
|
{
|
|
1528
2188
|
className: "opacity-25",
|
|
@@ -1533,7 +2193,7 @@ function LoadingSpinner({ size }) {
|
|
|
1533
2193
|
strokeWidth: "4"
|
|
1534
2194
|
}
|
|
1535
2195
|
),
|
|
1536
|
-
/* @__PURE__ */ (0,
|
|
2196
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1537
2197
|
"path",
|
|
1538
2198
|
{
|
|
1539
2199
|
className: "opacity-75",
|
|
@@ -1556,15 +2216,15 @@ function ButtonLink({
|
|
|
1556
2216
|
...props
|
|
1557
2217
|
}) {
|
|
1558
2218
|
const classes = getButtonClasses({ variant, size, fullWidth, disabled: false });
|
|
1559
|
-
return /* @__PURE__ */ (0,
|
|
1560
|
-
leftIcon && /* @__PURE__ */ (0,
|
|
2219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("a", { className: `${classes} ${className}`, ...props, children: [
|
|
2220
|
+
leftIcon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "mr-2", children: leftIcon }),
|
|
1561
2221
|
children,
|
|
1562
|
-
rightIcon && /* @__PURE__ */ (0,
|
|
2222
|
+
rightIcon && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ml-2", children: rightIcon })
|
|
1563
2223
|
] });
|
|
1564
2224
|
}
|
|
1565
2225
|
|
|
1566
2226
|
// src/components/ui/Card.tsx
|
|
1567
|
-
var
|
|
2227
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1568
2228
|
function getCardClasses(props) {
|
|
1569
2229
|
const { variant = "default", padding = "md", hover = false } = props;
|
|
1570
2230
|
const baseClasses = ["rounded-lg", "overflow-hidden"];
|
|
@@ -1597,7 +2257,7 @@ function Card({
|
|
|
1597
2257
|
...props
|
|
1598
2258
|
}) {
|
|
1599
2259
|
const classes = getCardClasses({ variant, padding, hover });
|
|
1600
|
-
return /* @__PURE__ */ (0,
|
|
2260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `${classes} ${className}`, ...props, children });
|
|
1601
2261
|
}
|
|
1602
2262
|
function CardHeader({
|
|
1603
2263
|
title,
|
|
@@ -1607,13 +2267,13 @@ function CardHeader({
|
|
|
1607
2267
|
className = "",
|
|
1608
2268
|
...props
|
|
1609
2269
|
}) {
|
|
1610
|
-
return /* @__PURE__ */ (0,
|
|
1611
|
-
/* @__PURE__ */ (0,
|
|
1612
|
-
title && /* @__PURE__ */ (0,
|
|
1613
|
-
subtitle && /* @__PURE__ */ (0,
|
|
2270
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `flex items-start justify-between ${className}`, ...props, children: [
|
|
2271
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
2272
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: "text-lg font-semibold text-gray-900", children: title }),
|
|
2273
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-1 text-sm text-gray-500", children: subtitle }),
|
|
1614
2274
|
children
|
|
1615
2275
|
] }),
|
|
1616
|
-
action && /* @__PURE__ */ (0,
|
|
2276
|
+
action && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex-shrink-0 ml-4", children: action })
|
|
1617
2277
|
] });
|
|
1618
2278
|
}
|
|
1619
2279
|
function CardBody({
|
|
@@ -1621,14 +2281,14 @@ function CardBody({
|
|
|
1621
2281
|
className = "",
|
|
1622
2282
|
...props
|
|
1623
2283
|
}) {
|
|
1624
|
-
return /* @__PURE__ */ (0,
|
|
2284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `mt-4 ${className}`, ...props, children });
|
|
1625
2285
|
}
|
|
1626
2286
|
function CardFooter({
|
|
1627
2287
|
children,
|
|
1628
2288
|
className = "",
|
|
1629
2289
|
...props
|
|
1630
2290
|
}) {
|
|
1631
|
-
return /* @__PURE__ */ (0,
|
|
2291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `mt-4 pt-4 border-t border-gray-100 ${className}`, ...props, children });
|
|
1632
2292
|
}
|
|
1633
2293
|
function CardImage({
|
|
1634
2294
|
aspectRatio = "video",
|
|
@@ -1642,7 +2302,7 @@ function CardImage({
|
|
|
1642
2302
|
wide: "aspect-[2/1]",
|
|
1643
2303
|
auto: ""
|
|
1644
2304
|
};
|
|
1645
|
-
return /* @__PURE__ */ (0,
|
|
2305
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `-m-5 mb-4 ${aspectClasses[aspectRatio]} overflow-hidden`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1646
2306
|
"img",
|
|
1647
2307
|
{
|
|
1648
2308
|
className: `w-full h-full object-cover ${className}`,
|
|
@@ -1651,8 +2311,96 @@ function CardImage({
|
|
|
1651
2311
|
}
|
|
1652
2312
|
) });
|
|
1653
2313
|
}
|
|
2314
|
+
|
|
2315
|
+
// src/components/ui/AnimatedSection.tsx
|
|
2316
|
+
var import_react6 = __toESM(require("react"));
|
|
2317
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2318
|
+
function AnimatedSection({
|
|
2319
|
+
children,
|
|
2320
|
+
animation = "fade-up",
|
|
2321
|
+
delay = 0,
|
|
2322
|
+
duration = 600,
|
|
2323
|
+
threshold = 0.1,
|
|
2324
|
+
once = true,
|
|
2325
|
+
className = "",
|
|
2326
|
+
as: Component = "div"
|
|
2327
|
+
}) {
|
|
2328
|
+
const ref = (0, import_react6.useRef)(null);
|
|
2329
|
+
const [isVisible, setIsVisible] = (0, import_react6.useState)(false);
|
|
2330
|
+
(0, import_react6.useEffect)(() => {
|
|
2331
|
+
const observer = new IntersectionObserver(
|
|
2332
|
+
([entry]) => {
|
|
2333
|
+
if (entry.isIntersecting) {
|
|
2334
|
+
setIsVisible(true);
|
|
2335
|
+
if (once && ref.current) {
|
|
2336
|
+
observer.unobserve(ref.current);
|
|
2337
|
+
}
|
|
2338
|
+
} else if (!once) {
|
|
2339
|
+
setIsVisible(false);
|
|
2340
|
+
}
|
|
2341
|
+
},
|
|
2342
|
+
{ threshold }
|
|
2343
|
+
);
|
|
2344
|
+
if (ref.current) {
|
|
2345
|
+
observer.observe(ref.current);
|
|
2346
|
+
}
|
|
2347
|
+
return () => {
|
|
2348
|
+
if (ref.current) {
|
|
2349
|
+
observer.unobserve(ref.current);
|
|
2350
|
+
}
|
|
2351
|
+
};
|
|
2352
|
+
}, [threshold, once]);
|
|
2353
|
+
const getAnimationStyles = () => {
|
|
2354
|
+
const baseStyles = {
|
|
2355
|
+
transition: `opacity ${duration}ms ease-out, transform ${duration}ms ease-out`,
|
|
2356
|
+
transitionDelay: `${delay}ms`
|
|
2357
|
+
};
|
|
2358
|
+
if (!isVisible) {
|
|
2359
|
+
switch (animation) {
|
|
2360
|
+
case "fade-up":
|
|
2361
|
+
return { ...baseStyles, opacity: 0, transform: "translateY(30px)" };
|
|
2362
|
+
case "fade-down":
|
|
2363
|
+
return { ...baseStyles, opacity: 0, transform: "translateY(-30px)" };
|
|
2364
|
+
case "fade-left":
|
|
2365
|
+
return { ...baseStyles, opacity: 0, transform: "translateX(30px)" };
|
|
2366
|
+
case "fade-right":
|
|
2367
|
+
return { ...baseStyles, opacity: 0, transform: "translateX(-30px)" };
|
|
2368
|
+
case "zoom":
|
|
2369
|
+
return { ...baseStyles, opacity: 0, transform: "scale(0.95)" };
|
|
2370
|
+
case "none":
|
|
2371
|
+
return {};
|
|
2372
|
+
default:
|
|
2373
|
+
return { ...baseStyles, opacity: 0, transform: "translateY(30px)" };
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
return { ...baseStyles, opacity: 1, transform: "translateY(0) translateX(0) scale(1)" };
|
|
2377
|
+
};
|
|
2378
|
+
if (animation === "none") {
|
|
2379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Component, { className, children });
|
|
2380
|
+
}
|
|
2381
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Component, { ref, style: getAnimationStyles(), className, children });
|
|
2382
|
+
}
|
|
2383
|
+
function StaggerContainer({
|
|
2384
|
+
children,
|
|
2385
|
+
staggerDelay = 100,
|
|
2386
|
+
animation = "fade-up",
|
|
2387
|
+
duration = 600,
|
|
2388
|
+
className = ""
|
|
2389
|
+
}) {
|
|
2390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className, children: import_react6.default.Children.map(children, (child, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2391
|
+
AnimatedSection,
|
|
2392
|
+
{
|
|
2393
|
+
animation,
|
|
2394
|
+
delay: index * staggerDelay,
|
|
2395
|
+
duration,
|
|
2396
|
+
children: child
|
|
2397
|
+
}
|
|
2398
|
+
)) });
|
|
2399
|
+
}
|
|
1654
2400
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1655
2401
|
0 && (module.exports = {
|
|
2402
|
+
AnimatedSection,
|
|
2403
|
+
BeforeAfter,
|
|
1656
2404
|
BriefcaseIcon,
|
|
1657
2405
|
Button,
|
|
1658
2406
|
ButtonLink,
|
|
@@ -1678,13 +2426,16 @@ function CardImage({
|
|
|
1678
2426
|
InstagramIcon,
|
|
1679
2427
|
MailIcon,
|
|
1680
2428
|
MapPinIcon,
|
|
2429
|
+
Menu,
|
|
1681
2430
|
MenuIcon,
|
|
1682
2431
|
PhoneIcon,
|
|
1683
2432
|
ScissorsIcon,
|
|
1684
2433
|
Services,
|
|
1685
2434
|
SparklesIcon,
|
|
2435
|
+
StaggerContainer,
|
|
1686
2436
|
StarIcon,
|
|
1687
2437
|
Testimonials,
|
|
2438
|
+
TrustBadges,
|
|
1688
2439
|
UtensilsIcon,
|
|
1689
2440
|
WrenchIcon,
|
|
1690
2441
|
XIcon,
|