@sonordev/agency-site-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BeforeAfterSection-6QUJOBO2.js +176 -0
- package/dist/BeforeAfterSection-6QUJOBO2.js.map +1 -0
- package/dist/BeforeAfterSection-DVAWWE4K.cjs +181 -0
- package/dist/BeforeAfterSection-DVAWWE4K.cjs.map +1 -0
- package/dist/CTASection-4JKLXEUF.cjs +111 -0
- package/dist/CTASection-4JKLXEUF.cjs.map +1 -0
- package/dist/CTASection-BJA72XIL.js +106 -0
- package/dist/CTASection-BJA72XIL.js.map +1 -0
- package/dist/ChallengesSection-GEQGVSJN.js +180 -0
- package/dist/ChallengesSection-GEQGVSJN.js.map +1 -0
- package/dist/ChallengesSection-IZ3DHECS.cjs +182 -0
- package/dist/ChallengesSection-IZ3DHECS.cjs.map +1 -0
- package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs +209 -0
- package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs.map +1 -0
- package/dist/ConversionFunnelSection-D3GE4NKE.js +203 -0
- package/dist/ConversionFunnelSection-D3GE4NKE.js.map +1 -0
- package/dist/DetailsSection-FB763FS7.js +135 -0
- package/dist/DetailsSection-FB763FS7.js.map +1 -0
- package/dist/DetailsSection-OACJFGH7.cjs +137 -0
- package/dist/DetailsSection-OACJFGH7.cjs.map +1 -0
- package/dist/FeatureSpotlightSection-B7P3JGNL.js +205 -0
- package/dist/FeatureSpotlightSection-B7P3JGNL.js.map +1 -0
- package/dist/FeatureSpotlightSection-WRHXS7TU.cjs +210 -0
- package/dist/FeatureSpotlightSection-WRHXS7TU.cjs.map +1 -0
- package/dist/GallerySection-VMKORC47.js +218 -0
- package/dist/GallerySection-VMKORC47.js.map +1 -0
- package/dist/GallerySection-WJ4PQDBI.cjs +219 -0
- package/dist/GallerySection-WJ4PQDBI.cjs.map +1 -0
- package/dist/MetricsTimelineSection-4L6DUHJ5.cjs +258 -0
- package/dist/MetricsTimelineSection-4L6DUHJ5.cjs.map +1 -0
- package/dist/MetricsTimelineSection-6BT5GNFV.js +253 -0
- package/dist/MetricsTimelineSection-6BT5GNFV.js.map +1 -0
- package/dist/ResultsSection-DFUJ5U6M.js +93 -0
- package/dist/ResultsSection-DFUJ5U6M.js.map +1 -0
- package/dist/ResultsSection-XLGMMQKY.cjs +95 -0
- package/dist/ResultsSection-XLGMMQKY.cjs.map +1 -0
- package/dist/ServicesSection-D5V3Q4GR.js +118 -0
- package/dist/ServicesSection-D5V3Q4GR.js.map +1 -0
- package/dist/ServicesSection-WJMGK2MF.cjs +120 -0
- package/dist/ServicesSection-WJMGK2MF.cjs.map +1 -0
- package/dist/StrategySection-3ED3QW4R.cjs +180 -0
- package/dist/StrategySection-3ED3QW4R.cjs.map +1 -0
- package/dist/StrategySection-VUWMIYYP.js +175 -0
- package/dist/StrategySection-VUWMIYYP.js.map +1 -0
- package/dist/TeamSection-DZVSNZE6.cjs +112 -0
- package/dist/TeamSection-DZVSNZE6.cjs.map +1 -0
- package/dist/TeamSection-HGKFW6PQ.js +107 -0
- package/dist/TeamSection-HGKFW6PQ.js.map +1 -0
- package/dist/TechStackSection-OCUYG4XT.js +90 -0
- package/dist/TechStackSection-OCUYG4XT.js.map +1 -0
- package/dist/TechStackSection-VKJK4KQB.cjs +91 -0
- package/dist/TechStackSection-VKJK4KQB.cjs.map +1 -0
- package/dist/TestimonialSection-6RGSMXQB.js +122 -0
- package/dist/TestimonialSection-6RGSMXQB.js.map +1 -0
- package/dist/TestimonialSection-XPTFUQIN.cjs +124 -0
- package/dist/TestimonialSection-XPTFUQIN.cjs.map +1 -0
- package/dist/VideoSection-4A2HC6K6.js +117 -0
- package/dist/VideoSection-4A2HC6K6.js.map +1 -0
- package/dist/VideoSection-G3DFS7UH.cjs +118 -0
- package/dist/VideoSection-G3DFS7UH.cjs.map +1 -0
- package/dist/chunk-2VNNFAG6.js +415 -0
- package/dist/chunk-2VNNFAG6.js.map +1 -0
- package/dist/chunk-2Y4O3LWM.js +53 -0
- package/dist/chunk-2Y4O3LWM.js.map +1 -0
- package/dist/chunk-5FKOLIV6.cjs +221 -0
- package/dist/chunk-5FKOLIV6.cjs.map +1 -0
- package/dist/chunk-7CFFAKDM.js +74 -0
- package/dist/chunk-7CFFAKDM.js.map +1 -0
- package/dist/chunk-A4I4IK7V.js +69 -0
- package/dist/chunk-A4I4IK7V.js.map +1 -0
- package/dist/chunk-IKBK7HYX.cjs +79 -0
- package/dist/chunk-IKBK7HYX.cjs.map +1 -0
- package/dist/chunk-KEOHORIH.cjs +79 -0
- package/dist/chunk-KEOHORIH.cjs.map +1 -0
- package/dist/chunk-NAS4K5UR.cjs +139 -0
- package/dist/chunk-NAS4K5UR.cjs.map +1 -0
- package/dist/chunk-QBLWP25X.cjs +73 -0
- package/dist/chunk-QBLWP25X.cjs.map +1 -0
- package/dist/chunk-QIC6JFFD.js +210 -0
- package/dist/chunk-QIC6JFFD.js.map +1 -0
- package/dist/chunk-TAPNXT7X.cjs +422 -0
- package/dist/chunk-TAPNXT7X.cjs.map +1 -0
- package/dist/chunk-XCKXHK44.js +15 -0
- package/dist/chunk-XCKXHK44.js.map +1 -0
- package/dist/chunk-XMC4DN6G.js +131 -0
- package/dist/chunk-XMC4DN6G.js.map +1 -0
- package/dist/chunk-XONXEFJY.cjs +58 -0
- package/dist/chunk-XONXEFJY.cjs.map +1 -0
- package/dist/chunk-XQNJED46.cjs +19 -0
- package/dist/chunk-XQNJED46.cjs.map +1 -0
- package/dist/chunk-YB4B3OMC.js +74 -0
- package/dist/chunk-YB4B3OMC.js.map +1 -0
- package/dist/index.cjs +271 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +137 -0
- package/dist/index.d.ts +137 -0
- package/dist/index.js +197 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/index.cjs +13 -0
- package/dist/layout/index.cjs.map +1 -0
- package/dist/layout/index.d.cts +54 -0
- package/dist/layout/index.d.ts +54 -0
- package/dist/layout/index.js +4 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/portfolio/client.cjs +18 -0
- package/dist/portfolio/client.cjs.map +1 -0
- package/dist/portfolio/client.d.cts +97 -0
- package/dist/portfolio/client.d.ts +97 -0
- package/dist/portfolio/client.js +6 -0
- package/dist/portfolio/client.js.map +1 -0
- package/dist/portfolio/index.cjs +41 -0
- package/dist/portfolio/index.cjs.map +1 -0
- package/dist/portfolio/index.d.cts +12 -0
- package/dist/portfolio/index.d.ts +12 -0
- package/dist/portfolio/index.js +8 -0
- package/dist/portfolio/index.js.map +1 -0
- package/dist/portfolio/sections.cjs +20 -0
- package/dist/portfolio/sections.cjs.map +1 -0
- package/dist/portfolio/sections.d.cts +42 -0
- package/dist/portfolio/sections.d.ts +42 -0
- package/dist/portfolio/sections.js +4 -0
- package/dist/portfolio/sections.js.map +1 -0
- package/dist/portfolio/server.cjs +141 -0
- package/dist/portfolio/server.cjs.map +1 -0
- package/dist/portfolio/server.d.cts +68 -0
- package/dist/portfolio/server.d.ts +68 -0
- package/dist/portfolio/server.js +134 -0
- package/dist/portfolio/server.js.map +1 -0
- package/dist/types-BMUhBhWx.d.cts +346 -0
- package/dist/types-BMUhBhWx.d.ts +346 -0
- package/package.json +71 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var gsap = require('gsap');
|
|
5
|
+
var ScrollTrigger = require('gsap/ScrollTrigger');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var gsap__default = /*#__PURE__*/_interopDefault(gsap);
|
|
11
|
+
|
|
12
|
+
// src/portfolio/components/primitives/ScrollReveal.tsx
|
|
13
|
+
function ScrollReveal({
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
delay = 0,
|
|
17
|
+
duration = 0.8,
|
|
18
|
+
y = 40,
|
|
19
|
+
x = 0,
|
|
20
|
+
opacity = 0,
|
|
21
|
+
scale,
|
|
22
|
+
stagger,
|
|
23
|
+
once = true,
|
|
24
|
+
threshold = 0.15
|
|
25
|
+
}) {
|
|
26
|
+
const ref = react.useRef(null);
|
|
27
|
+
react.useEffect(() => {
|
|
28
|
+
gsap__default.default.registerPlugin(ScrollTrigger.ScrollTrigger);
|
|
29
|
+
const el = ref.current;
|
|
30
|
+
if (!el) return;
|
|
31
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
32
|
+
if (prefersReducedMotion) {
|
|
33
|
+
gsap__default.default.set(el.children.length > 0 && stagger ? el.children : el, {
|
|
34
|
+
opacity: 1,
|
|
35
|
+
y: 0,
|
|
36
|
+
x: 0,
|
|
37
|
+
scale: 1
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const targets = stagger && el.children.length > 0 ? el.children : el;
|
|
42
|
+
const fromVars = {
|
|
43
|
+
opacity,
|
|
44
|
+
y,
|
|
45
|
+
x
|
|
46
|
+
};
|
|
47
|
+
if (scale !== void 0) {
|
|
48
|
+
fromVars.scale = scale;
|
|
49
|
+
}
|
|
50
|
+
const toVars = {
|
|
51
|
+
opacity: 1,
|
|
52
|
+
y: 0,
|
|
53
|
+
x: 0,
|
|
54
|
+
duration,
|
|
55
|
+
delay,
|
|
56
|
+
ease: "power2.out",
|
|
57
|
+
scrollTrigger: {
|
|
58
|
+
trigger: el,
|
|
59
|
+
start: `top ${(1 - threshold) * 100}%`,
|
|
60
|
+
toggleActions: once ? "play none none none" : "play reverse play reverse"
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
if (scale !== void 0) {
|
|
64
|
+
toVars.scale = 1;
|
|
65
|
+
}
|
|
66
|
+
if (stagger) {
|
|
67
|
+
toVars.stagger = stagger;
|
|
68
|
+
}
|
|
69
|
+
const ctx = gsap__default.default.context(() => {
|
|
70
|
+
gsap__default.default.fromTo(targets, fromVars, toVars);
|
|
71
|
+
}, el);
|
|
72
|
+
return () => ctx.revert();
|
|
73
|
+
}, [delay, duration, y, x, opacity, scale, stagger, once, threshold]);
|
|
74
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className, children });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.ScrollReveal = ScrollReveal;
|
|
78
|
+
//# sourceMappingURL=chunk-IKBK7HYX.cjs.map
|
|
79
|
+
//# sourceMappingURL=chunk-IKBK7HYX.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/portfolio/components/primitives/ScrollReveal.tsx"],"names":["useRef","useEffect","gsap","ScrollTrigger","jsx"],"mappings":";;;;;;;;;;;;AAoBe,SAAR,YAAA,CAA8B;AAAA,EACnC,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA,GAAQ,CAAA;AAAA,EACR,QAAA,GAAW,GAAA;AAAA,EACX,CAAA,GAAI,EAAA;AAAA,EACJ,CAAA,GAAI,CAAA;AAAA,EACJ,OAAA,GAAU,CAAA;AAAA,EACV,KAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA,GAAO,IAAA;AAAA,EACP,SAAA,GAAY;AACd,CAAA,EAAsB;AACpB,EAAA,MAAM,GAAA,GAAMA,aAAuB,IAAI,CAAA;AAEvC,EAAAC,eAAA,CAAU,MAAM;AACd,IAAAC,qBAAA,CAAK,eAAeC,2BAAa,CAAA;AAEjC,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AAGT,IAAA,MAAM,oBAAA,GAAuB,MAAA,CAAO,UAAA,CAAW,kCAAkC,CAAA,CAAE,OAAA;AACnF,IAAA,IAAI,oBAAA,EAAsB;AACxB,MAAAD,qBAAA,CAAK,GAAA,CAAI,GAAG,QAAA,CAAS,MAAA,GAAS,KAAK,OAAA,GAAU,EAAA,CAAG,WAAW,EAAA,EAAI;AAAA,QAC7D,OAAA,EAAS,CAAA;AAAA,QACT,CAAA,EAAG,CAAA;AAAA,QACH,CAAA,EAAG,CAAA;AAAA,QACH,KAAA,EAAO;AAAA,OACR,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAU,OAAA,IAAW,EAAA,CAAG,SAAS,MAAA,GAAS,CAAA,GAAI,GAAG,QAAA,GAAW,EAAA;AAElE,IAAA,MAAM,QAAA,GAA2B;AAAA,MAC/B,OAAA;AAAA,MACA,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,QAAA,CAAS,KAAA,GAAQ,KAAA;AAAA,IACnB;AAEA,IAAA,MAAM,MAAA,GAAyB;AAAA,MAC7B,OAAA,EAAS,CAAA;AAAA,MACT,CAAA,EAAG,CAAA;AAAA,MACH,CAAA,EAAG,CAAA;AAAA,MACH,QAAA;AAAA,MACA,KAAA;AAAA,MACA,IAAA,EAAM,YAAA;AAAA,MACN,aAAA,EAAe;AAAA,QACb,OAAA,EAAS,EAAA;AAAA,QACT,KAAA,EAAO,CAAA,IAAA,EAAA,CAAQ,CAAA,GAAI,SAAA,IAAa,GAAG,CAAA,CAAA,CAAA;AAAA,QACnC,aAAA,EAAe,OAAO,qBAAA,GAAwB;AAAA;AAChD,KACF;AACA,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,MAAA,CAAO,KAAA,GAAQ,CAAA;AAAA,IACjB;AACA,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAA,CAAO,OAAA,GAAU,OAAA;AAAA,IACnB;AAEA,IAAA,MAAM,GAAA,GAAMA,qBAAA,CAAK,OAAA,CAAQ,MAAM;AAC7B,MAAAA,qBAAA,CAAK,MAAA,CAAO,OAAA,EAAS,QAAA,EAAU,MAAM,CAAA;AAAA,IACvC,GAAG,EAAE,CAAA;AAEL,IAAA,OAAO,MAAM,IAAI,MAAA,EAAO;AAAA,EAC1B,CAAA,EAAG,CAAC,KAAA,EAAO,QAAA,EAAU,CAAA,EAAG,CAAA,EAAG,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,IAAA,EAAM,SAAS,CAAC,CAAA;AAEpE,EAAA,uBACEE,cAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAU,SAAA,EACZ,QAAA,EACH,CAAA;AAEJ","file":"chunk-IKBK7HYX.cjs","sourcesContent":["'use client';\n\nimport React, { useRef, useEffect } from 'react';\nimport gsap from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ninterface ScrollRevealProps {\n children: React.ReactNode;\n className?: string;\n delay?: number;\n duration?: number;\n y?: number;\n x?: number;\n opacity?: number;\n scale?: number;\n stagger?: number;\n once?: boolean;\n threshold?: number;\n}\n\nexport default function ScrollReveal({\n children,\n className,\n delay = 0,\n duration = 0.8,\n y = 40,\n x = 0,\n opacity = 0,\n scale,\n stagger,\n once = true,\n threshold = 0.15,\n}: ScrollRevealProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n gsap.registerPlugin(ScrollTrigger);\n\n const el = ref.current;\n if (!el) return;\n\n // Respect prefers-reduced-motion\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) {\n gsap.set(el.children.length > 0 && stagger ? el.children : el, {\n opacity: 1,\n y: 0,\n x: 0,\n scale: 1,\n });\n return;\n }\n\n const targets = stagger && el.children.length > 0 ? el.children : el;\n\n const fromVars: gsap.TweenVars = {\n opacity,\n y,\n x,\n };\n if (scale !== undefined) {\n fromVars.scale = scale;\n }\n\n const toVars: gsap.TweenVars = {\n opacity: 1,\n y: 0,\n x: 0,\n duration,\n delay,\n ease: 'power2.out',\n scrollTrigger: {\n trigger: el,\n start: `top ${(1 - threshold) * 100}%`,\n toggleActions: once ? 'play none none none' : 'play reverse play reverse',\n },\n };\n if (scale !== undefined) {\n toVars.scale = 1;\n }\n if (stagger) {\n toVars.stagger = stagger;\n }\n\n const ctx = gsap.context(() => {\n gsap.fromTo(targets, fromVars, toVars);\n }, el);\n\n return () => ctx.revert();\n }, [delay, duration, y, x, opacity, scale, stagger, once, threshold]);\n\n return (\n <div ref={ref} className={className}>\n {children}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var gsap = require('gsap');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var gsap__default = /*#__PURE__*/_interopDefault(gsap);
|
|
10
|
+
|
|
11
|
+
// src/portfolio/components/primitives/GlassCard.tsx
|
|
12
|
+
var paddingMap = {
|
|
13
|
+
sm: "16px",
|
|
14
|
+
md: "24px",
|
|
15
|
+
lg: "32px"
|
|
16
|
+
};
|
|
17
|
+
function GlassCard({
|
|
18
|
+
children,
|
|
19
|
+
className = "",
|
|
20
|
+
hover = true,
|
|
21
|
+
padding = "md",
|
|
22
|
+
onClick
|
|
23
|
+
}) {
|
|
24
|
+
const ref = react.useRef(null);
|
|
25
|
+
const handleMouseEnter = react.useCallback(() => {
|
|
26
|
+
if (!hover || !ref.current) return;
|
|
27
|
+
gsap__default.default.to(ref.current, {
|
|
28
|
+
y: -6,
|
|
29
|
+
scale: 1.015,
|
|
30
|
+
duration: 0.3,
|
|
31
|
+
ease: "power2.out"
|
|
32
|
+
});
|
|
33
|
+
gsap__default.default.to(ref.current, {
|
|
34
|
+
boxShadow: "0 20px 40px -12px rgba(0,0,0,0.25), 0 0 0 1px var(--sk-primary, #6366f1)",
|
|
35
|
+
duration: 0.3,
|
|
36
|
+
ease: "power2.out"
|
|
37
|
+
});
|
|
38
|
+
}, [hover]);
|
|
39
|
+
const handleMouseLeave = react.useCallback(() => {
|
|
40
|
+
if (!hover || !ref.current) return;
|
|
41
|
+
gsap__default.default.to(ref.current, {
|
|
42
|
+
y: 0,
|
|
43
|
+
scale: 1,
|
|
44
|
+
duration: 0.3,
|
|
45
|
+
ease: "power2.out"
|
|
46
|
+
});
|
|
47
|
+
gsap__default.default.to(ref.current, {
|
|
48
|
+
boxShadow: "0 4px 16px -4px rgba(0,0,0,0.1), 0 0 0 1px var(--sk-border, rgba(255,255,255,0.1))",
|
|
49
|
+
duration: 0.3,
|
|
50
|
+
ease: "power2.out"
|
|
51
|
+
});
|
|
52
|
+
}, [hover]);
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
ref,
|
|
57
|
+
className,
|
|
58
|
+
onClick,
|
|
59
|
+
onMouseEnter: handleMouseEnter,
|
|
60
|
+
onMouseLeave: handleMouseLeave,
|
|
61
|
+
style: {
|
|
62
|
+
padding: paddingMap[padding],
|
|
63
|
+
background: "var(--sk-surface, rgba(255,255,255,0.05))",
|
|
64
|
+
backdropFilter: "blur(16px)",
|
|
65
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
66
|
+
border: "1px solid var(--sk-border, rgba(255,255,255,0.1))",
|
|
67
|
+
borderRadius: "16px",
|
|
68
|
+
boxShadow: "0 4px 16px -4px rgba(0,0,0,0.1), 0 0 0 1px var(--sk-border, rgba(255,255,255,0.1))",
|
|
69
|
+
cursor: onClick ? "pointer" : void 0,
|
|
70
|
+
willChange: hover ? "transform" : void 0
|
|
71
|
+
},
|
|
72
|
+
children
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.GlassCard = GlassCard;
|
|
78
|
+
//# sourceMappingURL=chunk-KEOHORIH.cjs.map
|
|
79
|
+
//# sourceMappingURL=chunk-KEOHORIH.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/portfolio/components/primitives/GlassCard.tsx"],"names":["useRef","useCallback","gsap","jsx"],"mappings":";;;;;;;;;;;AAaA,IAAM,UAAA,GAAa;AAAA,EACjB,EAAA,EAAI,MAAA;AAAA,EACJ,EAAA,EAAI,MAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAEe,SAAR,SAAA,CAA2B;AAAA,EAChC,QAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,KAAA,GAAQ,IAAA;AAAA,EACR,OAAA,GAAU,IAAA;AAAA,EACV;AACF,CAAA,EAAmB;AACjB,EAAA,MAAM,GAAA,GAAMA,aAAuB,IAAI,CAAA;AAEvC,EAAA,MAAM,gBAAA,GAAmBC,kBAAY,MAAM;AACzC,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,GAAA,CAAI,OAAA,EAAS;AAC5B,IAAAC,qBAAA,CAAK,EAAA,CAAG,IAAI,OAAA,EAAS;AAAA,MACnB,CAAA,EAAG,EAAA;AAAA,MACH,KAAA,EAAO,KAAA;AAAA,MACP,QAAA,EAAU,GAAA;AAAA,MACV,IAAA,EAAM;AAAA,KACP,CAAA;AACD,IAAAA,qBAAA,CAAK,EAAA,CAAG,IAAI,OAAA,EAAS;AAAA,MACnB,SAAA,EAAW,0EAAA;AAAA,MACX,QAAA,EAAU,GAAA;AAAA,MACV,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,MAAM,gBAAA,GAAmBD,kBAAY,MAAM;AACzC,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,GAAA,CAAI,OAAA,EAAS;AAC5B,IAAAC,qBAAA,CAAK,EAAA,CAAG,IAAI,OAAA,EAAS;AAAA,MACnB,CAAA,EAAG,CAAA;AAAA,MACH,KAAA,EAAO,CAAA;AAAA,MACP,QAAA,EAAU,GAAA;AAAA,MACV,IAAA,EAAM;AAAA,KACP,CAAA;AACD,IAAAA,qBAAA,CAAK,EAAA,CAAG,IAAI,OAAA,EAAS;AAAA,MACnB,SAAA,EAAW,oFAAA;AAAA,MACX,QAAA,EAAU,GAAA;AAAA,MACV,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,uBACEC,cAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAA,EAAc,gBAAA;AAAA,MACd,YAAA,EAAc,gBAAA;AAAA,MACd,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,WAAW,OAAO,CAAA;AAAA,QAC3B,UAAA,EAAY,2CAAA;AAAA,QACZ,cAAA,EAAgB,YAAA;AAAA,QAChB,oBAAA,EAAsB,YAAA;AAAA,QACtB,MAAA,EAAQ,mDAAA;AAAA,QACR,YAAA,EAAc,MAAA;AAAA,QACd,SAAA,EAAW,oFAAA;AAAA,QACX,MAAA,EAAQ,UAAU,SAAA,GAAY,MAAA;AAAA,QAC9B,UAAA,EAAY,QAAQ,WAAA,GAAc;AAAA,OACpC;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ","file":"chunk-KEOHORIH.cjs","sourcesContent":["'use client';\n\nimport React, { useRef, useCallback } from 'react';\nimport gsap from 'gsap';\n\ninterface GlassCardProps {\n children: React.ReactNode;\n className?: string;\n hover?: boolean;\n padding?: 'sm' | 'md' | 'lg';\n onClick?: () => void;\n}\n\nconst paddingMap = {\n sm: '16px',\n md: '24px',\n lg: '32px',\n} as const;\n\nexport default function GlassCard({\n children,\n className = '',\n hover = true,\n padding = 'md',\n onClick,\n}: GlassCardProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n const handleMouseEnter = useCallback(() => {\n if (!hover || !ref.current) return;\n gsap.to(ref.current, {\n y: -6,\n scale: 1.015,\n duration: 0.3,\n ease: 'power2.out',\n });\n gsap.to(ref.current, {\n boxShadow: '0 20px 40px -12px rgba(0,0,0,0.25), 0 0 0 1px var(--sk-primary, #6366f1)',\n duration: 0.3,\n ease: 'power2.out',\n });\n }, [hover]);\n\n const handleMouseLeave = useCallback(() => {\n if (!hover || !ref.current) return;\n gsap.to(ref.current, {\n y: 0,\n scale: 1,\n duration: 0.3,\n ease: 'power2.out',\n });\n gsap.to(ref.current, {\n boxShadow: '0 4px 16px -4px rgba(0,0,0,0.1), 0 0 0 1px var(--sk-border, rgba(255,255,255,0.1))',\n duration: 0.3,\n ease: 'power2.out',\n });\n }, [hover]);\n\n return (\n <div\n ref={ref}\n className={className}\n onClick={onClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n style={{\n padding: paddingMap[padding],\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n border: '1px solid var(--sk-border, rgba(255,255,255,0.1))',\n borderRadius: '16px',\n boxShadow: '0 4px 16px -4px rgba(0,0,0,0.1), 0 0 0 1px var(--sk-border, rgba(255,255,255,0.1))',\n cursor: onClick ? 'pointer' : undefined,\n willChange: hover ? 'transform' : undefined,\n }}\n >\n {children}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkKEOHORIH_cjs = require('./chunk-KEOHORIH.cjs');
|
|
4
|
+
var chunkIKBK7HYX_cjs = require('./chunk-IKBK7HYX.cjs');
|
|
5
|
+
var Image = require('next/image');
|
|
6
|
+
var Link = require('next/link');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var Image__default = /*#__PURE__*/_interopDefault(Image);
|
|
12
|
+
var Link__default = /*#__PURE__*/_interopDefault(Link);
|
|
13
|
+
|
|
14
|
+
function PortfolioCard({ item, className = "" }) {
|
|
15
|
+
const thumbnailSrc = item.hero_screenshots?.desktop || item.hero_image;
|
|
16
|
+
const kpis = Array.isArray(item.kpis) ? item.kpis : [];
|
|
17
|
+
const services = Array.isArray(item.services) ? item.services : [];
|
|
18
|
+
const kpiPreview = kpis.slice(0, 3);
|
|
19
|
+
const servicePreview = services.slice(0, 3);
|
|
20
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: `/portfolio/${item.slug}`, className: `block group ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(chunkKEOHORIH_cjs.GlassCard, { padding: "sm", hover: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
21
|
+
thumbnailSrc && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: "relative w-full overflow-hidden rounded-xl",
|
|
25
|
+
style: { aspectRatio: "16/10" },
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28
|
+
Image__default.default,
|
|
29
|
+
{
|
|
30
|
+
src: thumbnailSrc,
|
|
31
|
+
alt: item.hero_image_alt || item.title,
|
|
32
|
+
fill: true,
|
|
33
|
+
sizes: "(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw",
|
|
34
|
+
className: "object-cover object-top transition-transform duration-500 group-hover:scale-105"
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
38
|
+
"span",
|
|
39
|
+
{
|
|
40
|
+
className: "absolute top-3 left-3 px-3 py-1 rounded-full text-[11px] uppercase tracking-wider font-semibold",
|
|
41
|
+
style: {
|
|
42
|
+
background: "color-mix(in srgb, var(--sk-primary, #6366f1) 80%, transparent)",
|
|
43
|
+
color: "#ffffff",
|
|
44
|
+
backdropFilter: "blur(8px)"
|
|
45
|
+
},
|
|
46
|
+
children: item.category
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
),
|
|
52
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 px-2 pb-2", children: [
|
|
53
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54
|
+
"h3",
|
|
55
|
+
{
|
|
56
|
+
className: "text-lg font-semibold line-clamp-1",
|
|
57
|
+
style: { color: "var(--sk-text-primary, #ffffff)" },
|
|
58
|
+
children: item.title
|
|
59
|
+
}
|
|
60
|
+
),
|
|
61
|
+
item.subtitle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
62
|
+
"p",
|
|
63
|
+
{
|
|
64
|
+
className: "text-sm line-clamp-2",
|
|
65
|
+
style: { color: "var(--sk-text-tertiary, #71717a)" },
|
|
66
|
+
children: item.subtitle
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
kpiPreview.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 mt-1", children: kpiPreview.map((kpi, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
className: "text-base font-bold",
|
|
74
|
+
style: { color: "var(--sk-primary, #6366f1)" },
|
|
75
|
+
children: [
|
|
76
|
+
kpi.prefix || "",
|
|
77
|
+
kpi.value.toLocaleString(),
|
|
78
|
+
kpi.suffix
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
83
|
+
"span",
|
|
84
|
+
{
|
|
85
|
+
className: "text-[10px] uppercase tracking-wider font-medium",
|
|
86
|
+
style: { color: "var(--sk-text-tertiary, #71717a)" },
|
|
87
|
+
children: kpi.label
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
] }, i)) }),
|
|
91
|
+
servicePreview.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-1.5 mt-2", children: [
|
|
92
|
+
servicePreview.map((service) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
93
|
+
"span",
|
|
94
|
+
{
|
|
95
|
+
className: "px-2.5 py-0.5 rounded-full text-[11px] font-medium",
|
|
96
|
+
style: {
|
|
97
|
+
background: "var(--sk-surface, rgba(255,255,255,0.05))",
|
|
98
|
+
color: "var(--sk-text-secondary, #a1a1aa)",
|
|
99
|
+
border: "1px solid var(--sk-border, rgba(255,255,255,0.1))"
|
|
100
|
+
},
|
|
101
|
+
children: service
|
|
102
|
+
},
|
|
103
|
+
service
|
|
104
|
+
)),
|
|
105
|
+
services.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
106
|
+
"span",
|
|
107
|
+
{
|
|
108
|
+
className: "px-2.5 py-0.5 rounded-full text-[11px] font-medium",
|
|
109
|
+
style: {
|
|
110
|
+
background: "var(--sk-surface, rgba(255,255,255,0.05))",
|
|
111
|
+
color: "var(--sk-text-tertiary, #71717a)"
|
|
112
|
+
},
|
|
113
|
+
children: [
|
|
114
|
+
"+",
|
|
115
|
+
item.services.length - 3
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
] })
|
|
120
|
+
] })
|
|
121
|
+
] }) }) });
|
|
122
|
+
}
|
|
123
|
+
function PortfolioGrid({ items, className = "" }) {
|
|
124
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkIKBK7HYX_cjs.ScrollReveal, { stagger: 0.1, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
125
|
+
"div",
|
|
126
|
+
{
|
|
127
|
+
className: `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 ${className}`,
|
|
128
|
+
style: {
|
|
129
|
+
gap: "var(--sk-grid-gap, 24px)"
|
|
130
|
+
},
|
|
131
|
+
children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(PortfolioCard, { item }, item.id))
|
|
132
|
+
}
|
|
133
|
+
) });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
exports.PortfolioCard = PortfolioCard;
|
|
137
|
+
exports.PortfolioGrid = PortfolioGrid;
|
|
138
|
+
//# sourceMappingURL=chunk-NAS4K5UR.cjs.map
|
|
139
|
+
//# sourceMappingURL=chunk-NAS4K5UR.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/portfolio/components/PortfolioCard.tsx","../src/portfolio/components/PortfolioGrid.tsx"],"names":["jsx","Link","GlassCard","jsxs","Image","ScrollReveal"],"mappings":"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","file":"chunk-NAS4K5UR.cjs","sourcesContent":["'use client';\n\nimport React from 'react';\nimport Image from 'next/image';\nimport Link from 'next/link';\nimport type { PortfolioItem } from '../../types';\nimport GlassCard from './primitives/GlassCard';\n\ninterface PortfolioCardProps {\n item: PortfolioItem;\n className?: string;\n}\n\nexport default function PortfolioCard({ item, className = '' }: PortfolioCardProps) {\n // Use hero_screenshots desktop as thumbnail, fall back to hero_image\n const thumbnailSrc = item.hero_screenshots?.desktop || item.hero_image;\n const kpis = Array.isArray(item.kpis) ? item.kpis : [];\n const services = Array.isArray(item.services) ? item.services : [];\n const kpiPreview = kpis.slice(0, 3);\n const servicePreview = services.slice(0, 3);\n\n return (\n <Link href={`/portfolio/${item.slug}`} className={`block group ${className}`}>\n <GlassCard padding=\"sm\" hover>\n <div className=\"flex flex-col gap-4\">\n {/* Thumbnail */}\n {thumbnailSrc && (\n <div\n className=\"relative w-full overflow-hidden rounded-xl\"\n style={{ aspectRatio: '16/10' }}\n >\n <Image\n src={thumbnailSrc}\n alt={item.hero_image_alt || item.title}\n fill\n sizes=\"(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw\"\n className=\"object-cover object-top transition-transform duration-500 group-hover:scale-105\"\n />\n {/* Category badge */}\n <span\n className=\"absolute top-3 left-3 px-3 py-1 rounded-full text-[11px] uppercase tracking-wider font-semibold\"\n style={{\n background: 'color-mix(in srgb, var(--sk-primary, #6366f1) 80%, transparent)',\n color: '#ffffff',\n backdropFilter: 'blur(8px)',\n }}\n >\n {item.category}\n </span>\n </div>\n )}\n\n {/* Text content */}\n <div className=\"flex flex-col gap-2 px-2 pb-2\">\n <h3\n className=\"text-lg font-semibold line-clamp-1\"\n style={{ color: 'var(--sk-text-primary, #ffffff)' }}\n >\n {item.title}\n </h3>\n\n {item.subtitle && (\n <p\n className=\"text-sm line-clamp-2\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {item.subtitle}\n </p>\n )}\n\n {/* KPI preview */}\n {kpiPreview.length > 0 && (\n <div className=\"flex items-center gap-4 mt-1\">\n {kpiPreview.map((kpi, i) => (\n <div key={i} className=\"flex flex-col\">\n <span\n className=\"text-base font-bold\"\n style={{ color: 'var(--sk-primary, #6366f1)' }}\n >\n {kpi.prefix || ''}\n {kpi.value.toLocaleString()}\n {kpi.suffix}\n </span>\n <span\n className=\"text-[10px] uppercase tracking-wider font-medium\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {kpi.label}\n </span>\n </div>\n ))}\n </div>\n )}\n\n {/* Services tags */}\n {servicePreview.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5 mt-2\">\n {servicePreview.map((service) => (\n <span\n key={service}\n className=\"px-2.5 py-0.5 rounded-full text-[11px] font-medium\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: 'var(--sk-text-secondary, #a1a1aa)',\n border: '1px solid var(--sk-border, rgba(255,255,255,0.1))',\n }}\n >\n {service}\n </span>\n ))}\n {services.length > 3 && (\n <span\n className=\"px-2.5 py-0.5 rounded-full text-[11px] font-medium\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: 'var(--sk-text-tertiary, #71717a)',\n }}\n >\n +{item.services.length - 3}\n </span>\n )}\n </div>\n )}\n </div>\n </div>\n </GlassCard>\n </Link>\n );\n}\n","'use client';\n\nimport React from 'react';\nimport type { PortfolioItem } from '../../types';\nimport ScrollReveal from './primitives/ScrollReveal';\nimport PortfolioCard from './PortfolioCard';\n\ninterface PortfolioGridProps {\n items: PortfolioItem[];\n className?: string;\n}\n\nexport default function PortfolioGrid({ items, className = '' }: PortfolioGridProps) {\n return (\n <ScrollReveal stagger={0.1}>\n <div\n className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 ${className}`}\n style={{\n gap: 'var(--sk-grid-gap, 24px)',\n }}\n >\n {items.map((item) => (\n <PortfolioCard key={item.id} item={item} />\n ))}\n </div>\n </ScrollReveal>\n );\n}\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/shared/api.ts
|
|
4
|
+
var DEFAULT_API_URL = "https://api.sonor.io";
|
|
5
|
+
var DEFAULT_REVALIDATE = 3600;
|
|
6
|
+
function getApiConfig() {
|
|
7
|
+
const apiKey = process.env.SONOR_API_KEY || process.env.UPTRADE_API_KEY || "";
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
console.warn(
|
|
10
|
+
"[@sonordev/agency-site-kit] Missing SONOR_API_KEY environment variable. Portfolio data will not be fetched."
|
|
11
|
+
);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const apiUrl = (process.env.SONOR_API_URL || process.env.NEXT_PUBLIC_SONOR_API_URL || DEFAULT_API_URL).replace(/\/+$/, "");
|
|
15
|
+
return { apiKey, apiUrl };
|
|
16
|
+
}
|
|
17
|
+
async function apiFetch(path, options = {}) {
|
|
18
|
+
const {
|
|
19
|
+
revalidate = DEFAULT_REVALIDATE,
|
|
20
|
+
headers: extraHeaders = {},
|
|
21
|
+
apiKey: overrideApiKey,
|
|
22
|
+
apiUrl: overrideApiUrl,
|
|
23
|
+
...fetchOptions
|
|
24
|
+
} = options;
|
|
25
|
+
let apiKey = overrideApiKey || "";
|
|
26
|
+
let apiUrl = overrideApiUrl || "";
|
|
27
|
+
if (!apiKey || !apiUrl) {
|
|
28
|
+
const config = getApiConfig();
|
|
29
|
+
if (!config) return null;
|
|
30
|
+
if (!apiKey) apiKey = config.apiKey;
|
|
31
|
+
if (!apiUrl) apiUrl = config.apiUrl;
|
|
32
|
+
}
|
|
33
|
+
const url = `${apiUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
...fetchOptions,
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
"x-api-key": apiKey,
|
|
40
|
+
...extraHeaders
|
|
41
|
+
},
|
|
42
|
+
next: { revalidate }
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
console.warn(
|
|
46
|
+
`[@sonordev/agency-site-kit] API request failed: ${response.status} ${response.statusText} \u2014 ${url}`
|
|
47
|
+
);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const data = await response.json();
|
|
51
|
+
return data;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn(
|
|
54
|
+
`[@sonordev/agency-site-kit] API request error for ${url}:`,
|
|
55
|
+
error instanceof Error ? error.message : error
|
|
56
|
+
);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function apiGet(path, revalidate, apiKey, apiUrl) {
|
|
61
|
+
return apiFetch(path, {
|
|
62
|
+
method: "GET",
|
|
63
|
+
revalidate,
|
|
64
|
+
apiKey,
|
|
65
|
+
apiUrl
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
exports.apiFetch = apiFetch;
|
|
70
|
+
exports.apiGet = apiGet;
|
|
71
|
+
exports.getApiConfig = getApiConfig;
|
|
72
|
+
//# sourceMappingURL=chunk-QBLWP25X.cjs.map
|
|
73
|
+
//# sourceMappingURL=chunk-QBLWP25X.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/api.ts"],"names":[],"mappings":";;;AAOA,IAAM,eAAA,GAAkB,sBAAA;AACxB,IAAM,kBAAA,GAAqB,IAAA;AAYpB,SAAS,YAAA,GAAiC;AAC/C,EAAA,MAAM,SACJ,OAAA,CAAQ,GAAA,CAAI,aAAA,IACZ,OAAA,CAAQ,IAAI,eAAA,IACZ,EAAA;AAEF,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KAEF;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAA,CACJ,OAAA,CAAQ,GAAA,CAAI,aAAA,IACZ,OAAA,CAAQ,IAAI,yBAAA,IACZ,eAAA,EACA,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AAEpB,EAAA,OAAO,EAAE,QAAQ,MAAA,EAAO;AAC1B;AAqBA,eAAsB,QAAA,CACpB,IAAA,EACA,OAAA,GAA2B,EAAC,EACT;AACnB,EAAA,MAAM;AAAA,IACJ,UAAA,GAAa,kBAAA;AAAA,IACb,OAAA,EAAS,eAAe,EAAC;AAAA,IACzB,MAAA,EAAQ,cAAA;AAAA,IACR,MAAA,EAAQ,cAAA;AAAA,IACR,GAAG;AAAA,GACL,GAAI,OAAA;AAEJ,EAAA,IAAI,SAAS,cAAA,IAAkB,EAAA;AAC/B,EAAA,IAAI,SAAS,cAAA,IAAkB,EAAA;AAE/B,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,EAAQ;AACtB,IAAA,MAAM,SAAS,YAAA,EAAa;AAC5B,IAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,IAAA,IAAI,CAAC,MAAA,EAAQ,MAAA,GAAS,MAAA,CAAO,MAAA;AAC7B,IAAA,IAAI,CAAC,MAAA,EAAQ,MAAA,GAAS,MAAA,CAAO,MAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,MAAM,CAAA,EAAG,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,IAAA,GAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA,CAAA;AAEhE,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,MAChC,GAAG,YAAA;AAAA,MACH,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,WAAA,EAAa,MAAA;AAAA,QACb,GAAG;AAAA,OACL;AAAA,MACA,IAAA,EAAM,EAAE,UAAA;AAAW,KACL,CAAA;AAEhB,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,mDAAmD,QAAA,CAAS,MAAM,IAAI,QAAA,CAAS,UAAU,WAAM,GAAG,CAAA;AAAA,OACpG;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,qDAAqD,GAAG,CAAA,CAAA,CAAA;AAAA,MACxD,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU;AAAA,KAC3C;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAUA,eAAsB,MAAA,CACpB,IAAA,EACA,UAAA,EACA,MAAA,EACA,MAAA,EACmB;AACnB,EAAA,OAAO,SAAY,IAAA,EAAM;AAAA,IACvB,MAAA,EAAQ,KAAA;AAAA,IACR,UAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH","file":"chunk-QBLWP25X.cjs","sourcesContent":["/**\n * @sonordev/agency-site-kit — API fetch utilities\n *\n * Server-side only. Reads SONOR_API_KEY and SONOR_API_URL from process.env.\n * Uses Next.js ISR caching via `next: { revalidate }` on every fetch.\n */\n\nconst DEFAULT_API_URL = 'https://api.sonor.io';\nconst DEFAULT_REVALIDATE = 3600; // 1 hour\n\ninterface ApiConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n/**\n * Reads API configuration from environment variables.\n * Falls back to UPTRADE_API_KEY for legacy compatibility.\n * Returns null if no API key is found.\n */\nexport function getApiConfig(): ApiConfig | null {\n const apiKey =\n process.env.SONOR_API_KEY ||\n process.env.UPTRADE_API_KEY ||\n '';\n\n if (!apiKey) {\n console.warn(\n '[@sonordev/agency-site-kit] Missing SONOR_API_KEY environment variable. ' +\n 'Portfolio data will not be fetched.',\n );\n return null;\n }\n\n const apiUrl = (\n process.env.SONOR_API_URL ||\n process.env.NEXT_PUBLIC_SONOR_API_URL ||\n DEFAULT_API_URL\n ).replace(/\\/+$/, '');\n\n return { apiKey, apiUrl };\n}\n\ninterface ApiFetchOptions extends Omit<RequestInit, 'headers'> {\n /** ISR revalidation interval in seconds. Defaults to 3600. Set to 0 for no caching. */\n revalidate?: number;\n /** Additional headers to merge with defaults */\n headers?: Record<string, string>;\n /** Override API key (e.g. passed from layout props) */\n apiKey?: string;\n /** Override API URL */\n apiUrl?: string;\n}\n\n/**\n * Fetch wrapper for the public portfolio API.\n *\n * - Injects `x-api-key` header automatically\n * - Parses JSON responses\n * - Uses Next.js ISR caching via `next.revalidate`\n * - Returns null on failure instead of throwing\n */\nexport async function apiFetch<T>(\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n const {\n revalidate = DEFAULT_REVALIDATE,\n headers: extraHeaders = {},\n apiKey: overrideApiKey,\n apiUrl: overrideApiUrl,\n ...fetchOptions\n } = options;\n\n let apiKey = overrideApiKey || '';\n let apiUrl = overrideApiUrl || '';\n\n if (!apiKey || !apiUrl) {\n const config = getApiConfig();\n if (!config) return null;\n if (!apiKey) apiKey = config.apiKey;\n if (!apiUrl) apiUrl = config.apiUrl;\n }\n\n const url = `${apiUrl}${path.startsWith('/') ? path : `/${path}`}`;\n\n try {\n const response = await fetch(url, {\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n ...extraHeaders,\n },\n next: { revalidate },\n } as RequestInit);\n\n if (!response.ok) {\n console.warn(\n `[@sonordev/agency-site-kit] API request failed: ${response.status} ${response.statusText} — ${url}`,\n );\n return null;\n }\n\n const data = await response.json();\n return data as T;\n } catch (error) {\n console.warn(\n `[@sonordev/agency-site-kit] API request error for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\n/**\n * GET shorthand for apiFetch.\n *\n * @param path API path (e.g. \"/api/public/portfolio/items\")\n * @param revalidate ISR revalidation in seconds (default 3600)\n * @param apiKey Optional override API key\n * @param apiUrl Optional override API URL\n */\nexport async function apiGet<T>(\n path: string,\n revalidate?: number,\n apiKey?: string,\n apiUrl?: string,\n): Promise<T | null> {\n return apiFetch<T>(path, {\n method: 'GET',\n revalidate,\n apiKey,\n apiUrl,\n });\n}\n"]}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { apiGet } from './chunk-A4I4IK7V.js';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
// src/shared/brand.ts
|
|
5
|
+
function hexToRgb(hex) {
|
|
6
|
+
if (!hex || typeof hex !== "string") return null;
|
|
7
|
+
const cleaned = hex.replace(/^#/, "");
|
|
8
|
+
let r;
|
|
9
|
+
let g;
|
|
10
|
+
let b;
|
|
11
|
+
if (cleaned.length === 3) {
|
|
12
|
+
r = parseInt(cleaned[0] + cleaned[0], 16);
|
|
13
|
+
g = parseInt(cleaned[1] + cleaned[1], 16);
|
|
14
|
+
b = parseInt(cleaned[2] + cleaned[2], 16);
|
|
15
|
+
} else if (cleaned.length === 6) {
|
|
16
|
+
r = parseInt(cleaned.substring(0, 2), 16);
|
|
17
|
+
g = parseInt(cleaned.substring(2, 4), 16);
|
|
18
|
+
b = parseInt(cleaned.substring(4, 6), 16);
|
|
19
|
+
} else {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
|
|
23
|
+
return `${r}, ${g}, ${b}`;
|
|
24
|
+
}
|
|
25
|
+
var DEFAULT_BRAND_CONFIG = {
|
|
26
|
+
primary: "#6366f1",
|
|
27
|
+
secondary: "#8b5cf6",
|
|
28
|
+
background: "#ffffff",
|
|
29
|
+
backgroundElevated: "#f8fafc",
|
|
30
|
+
surface: "#f1f5f9",
|
|
31
|
+
surfaceHover: "#e2e8f0",
|
|
32
|
+
surfaceBorder: "#e2e8f0",
|
|
33
|
+
textPrimary: "#0f172a",
|
|
34
|
+
textSecondary: "#475569",
|
|
35
|
+
textTertiary: "#94a3b8",
|
|
36
|
+
fontHeading: "'Inter', system-ui, -apple-system, sans-serif",
|
|
37
|
+
fontBody: "'Inter', system-ui, -apple-system, sans-serif",
|
|
38
|
+
radius: {
|
|
39
|
+
sm: "0.375rem",
|
|
40
|
+
md: "0.5rem",
|
|
41
|
+
lg: "0.75rem"
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var DEFAULT_DARK_MODE = {
|
|
45
|
+
background: "#0f172a",
|
|
46
|
+
backgroundElevated: "#1e293b",
|
|
47
|
+
surface: "#1e293b",
|
|
48
|
+
surfaceHover: "#334155",
|
|
49
|
+
surfaceBorder: "#334155",
|
|
50
|
+
textPrimary: "#f8fafc",
|
|
51
|
+
textSecondary: "#cbd5e1",
|
|
52
|
+
textTertiary: "#64748b"
|
|
53
|
+
};
|
|
54
|
+
function buildVariables(config) {
|
|
55
|
+
const primaryRgb = hexToRgb(config.primary) || "99, 102, 241";
|
|
56
|
+
const secondaryRgb = hexToRgb(config.secondary) || "139, 92, 246";
|
|
57
|
+
const radius = config.radius || DEFAULT_BRAND_CONFIG.radius;
|
|
58
|
+
return [
|
|
59
|
+
// Primary tokens (--sk-*)
|
|
60
|
+
`--sk-primary: ${config.primary};`,
|
|
61
|
+
`--sk-primary-rgb: ${primaryRgb};`,
|
|
62
|
+
`--sk-secondary: ${config.secondary};`,
|
|
63
|
+
`--sk-secondary-rgb: ${secondaryRgb};`,
|
|
64
|
+
`--sk-bg: ${config.background};`,
|
|
65
|
+
`--sk-bg-elevated: ${config.backgroundElevated};`,
|
|
66
|
+
`--sk-surface: ${config.surface};`,
|
|
67
|
+
`--sk-surface-hover: ${config.surfaceHover};`,
|
|
68
|
+
`--sk-border: ${config.surfaceBorder};`,
|
|
69
|
+
`--sk-text-primary: ${config.textPrimary};`,
|
|
70
|
+
`--sk-text-secondary: ${config.textSecondary};`,
|
|
71
|
+
`--sk-text-tertiary: ${config.textTertiary};`,
|
|
72
|
+
`--sk-radius-sm: ${radius.sm};`,
|
|
73
|
+
`--sk-radius: ${radius.md};`,
|
|
74
|
+
`--sk-radius-lg: ${radius.lg};`,
|
|
75
|
+
`--sk-font-heading: ${config.fontHeading};`,
|
|
76
|
+
`--sk-font: ${config.fontBody};`
|
|
77
|
+
].join("\n ");
|
|
78
|
+
}
|
|
79
|
+
function buildDarkVariables(base) {
|
|
80
|
+
const dark = {
|
|
81
|
+
...DEFAULT_DARK_MODE,
|
|
82
|
+
...base.darkMode,
|
|
83
|
+
// Primary/secondary carry over unless explicitly overridden
|
|
84
|
+
primary: base.darkMode?.primary || base.primary,
|
|
85
|
+
secondary: base.darkMode?.secondary || base.secondary
|
|
86
|
+
};
|
|
87
|
+
const primaryRgb = hexToRgb(dark.primary) || hexToRgb(base.primary) || "99, 102, 241";
|
|
88
|
+
const secondaryRgb = hexToRgb(dark.secondary) || hexToRgb(base.secondary) || "139, 92, 246";
|
|
89
|
+
return [
|
|
90
|
+
// Primary tokens (--sk-*)
|
|
91
|
+
`--sk-primary: ${dark.primary};`,
|
|
92
|
+
`--sk-primary-rgb: ${primaryRgb};`,
|
|
93
|
+
`--sk-secondary: ${dark.secondary};`,
|
|
94
|
+
`--sk-secondary-rgb: ${secondaryRgb};`,
|
|
95
|
+
`--sk-bg: ${dark.background || DEFAULT_DARK_MODE.background};`,
|
|
96
|
+
`--sk-bg-elevated: ${dark.backgroundElevated || DEFAULT_DARK_MODE.backgroundElevated};`,
|
|
97
|
+
`--sk-surface: ${dark.surface || DEFAULT_DARK_MODE.surface};`,
|
|
98
|
+
`--sk-surface-hover: ${dark.surfaceHover || DEFAULT_DARK_MODE.surfaceHover};`,
|
|
99
|
+
`--sk-border: ${dark.surfaceBorder || DEFAULT_DARK_MODE.surfaceBorder};`,
|
|
100
|
+
`--sk-text-primary: ${dark.textPrimary || DEFAULT_DARK_MODE.textPrimary};`,
|
|
101
|
+
`--sk-text-secondary: ${dark.textSecondary || DEFAULT_DARK_MODE.textSecondary};`,
|
|
102
|
+
`--sk-text-tertiary: ${dark.textTertiary || DEFAULT_DARK_MODE.textTertiary};`
|
|
103
|
+
].join("\n ");
|
|
104
|
+
}
|
|
105
|
+
function generateBrandCSS(config) {
|
|
106
|
+
const merged = {
|
|
107
|
+
...DEFAULT_BRAND_CONFIG,
|
|
108
|
+
...config,
|
|
109
|
+
radius: {
|
|
110
|
+
...DEFAULT_BRAND_CONFIG.radius,
|
|
111
|
+
...config.radius
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const lightVars = buildVariables(merged);
|
|
115
|
+
const darkVars = buildDarkVariables(merged);
|
|
116
|
+
return `
|
|
117
|
+
:root, [data-theme="light"] {
|
|
118
|
+
${lightVars}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[data-theme="dark"] {
|
|
122
|
+
${darkVars}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (prefers-color-scheme: dark) {
|
|
126
|
+
:root:not([data-theme="light"]) {
|
|
127
|
+
${darkVars}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`.trim();
|
|
131
|
+
}
|
|
132
|
+
function mergeBrandConfig(partial) {
|
|
133
|
+
return {
|
|
134
|
+
...DEFAULT_BRAND_CONFIG,
|
|
135
|
+
...partial,
|
|
136
|
+
radius: {
|
|
137
|
+
...DEFAULT_BRAND_CONFIG.radius,
|
|
138
|
+
...partial.radius
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/layout/AgencySiteKitLayout.tsx
|
|
144
|
+
function portalApiOrigin(apiUrl) {
|
|
145
|
+
try {
|
|
146
|
+
return new URL(apiUrl.replace(/\/$/, "")).origin;
|
|
147
|
+
} catch {
|
|
148
|
+
return "https://api.sonor.io";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function fetchBrandConfig(apiKey, apiUrl) {
|
|
152
|
+
const response = await apiGet(
|
|
153
|
+
"/api/public/portfolio/config",
|
|
154
|
+
3600,
|
|
155
|
+
apiKey,
|
|
156
|
+
apiUrl
|
|
157
|
+
);
|
|
158
|
+
if (response?.brand) {
|
|
159
|
+
return mergeBrandConfig(response.brand);
|
|
160
|
+
}
|
|
161
|
+
return mergeBrandConfig({});
|
|
162
|
+
}
|
|
163
|
+
async function AgencySiteKitLayout({
|
|
164
|
+
children,
|
|
165
|
+
apiKey,
|
|
166
|
+
apiUrl,
|
|
167
|
+
brandConfig: brandConfigOverride,
|
|
168
|
+
theme,
|
|
169
|
+
className
|
|
170
|
+
}) {
|
|
171
|
+
const brandConfig = brandConfigOverride ? mergeBrandConfig(brandConfigOverride) : await fetchBrandConfig(apiKey, apiUrl);
|
|
172
|
+
const cssString = generateBrandCSS(brandConfig);
|
|
173
|
+
const resolvedApiUrl = apiUrl ?? process.env.SONOR_API_URL ?? process.env.UPTRADE_API_URL ?? "https://api.sonor.io";
|
|
174
|
+
const preconnectHref = portalApiOrigin(resolvedApiUrl);
|
|
175
|
+
return React.createElement(
|
|
176
|
+
React.Fragment,
|
|
177
|
+
null,
|
|
178
|
+
React.createElement("link", {
|
|
179
|
+
rel: "preconnect",
|
|
180
|
+
href: preconnectHref,
|
|
181
|
+
crossOrigin: "anonymous"
|
|
182
|
+
}),
|
|
183
|
+
React.createElement("link", {
|
|
184
|
+
rel: "dns-prefetch",
|
|
185
|
+
href: preconnectHref
|
|
186
|
+
}),
|
|
187
|
+
React.createElement("style", {
|
|
188
|
+
dangerouslySetInnerHTML: { __html: cssString },
|
|
189
|
+
"data-agency-site-kit": "brand"
|
|
190
|
+
}),
|
|
191
|
+
React.createElement(
|
|
192
|
+
"div",
|
|
193
|
+
{
|
|
194
|
+
"data-theme": theme || void 0,
|
|
195
|
+
"data-agency-site-kit": "root",
|
|
196
|
+
className,
|
|
197
|
+
style: {
|
|
198
|
+
fontFamily: "var(--sk-font)",
|
|
199
|
+
color: "var(--sk-text-primary)",
|
|
200
|
+
backgroundColor: "var(--sk-bg)"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
children
|
|
204
|
+
)
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export { AgencySiteKitLayout, DEFAULT_BRAND_CONFIG, DEFAULT_DARK_MODE, generateBrandCSS, hexToRgb, mergeBrandConfig };
|
|
209
|
+
//# sourceMappingURL=chunk-QIC6JFFD.js.map
|
|
210
|
+
//# sourceMappingURL=chunk-QIC6JFFD.js.map
|