project-portfolio 2.0.1 → 2.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectCard.d.ts","sourceRoot":"","sources":["../src/ProjectCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAmC,MAAM,SAAS,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAA;AAE5C,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,2FAA2F;IAC3F,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAeD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,MAAM,EACN,eAAoB,EACpB,QAAQ,EACR,OAAgB,EAChB,QAAsB,GACvB,EAAE,gBAAgB,2CA6PlB"}
1
+ {"version":3,"file":"ProjectCard.d.ts","sourceRoot":"","sources":["../src/ProjectCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAmC,MAAM,SAAS,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAA;AAE5C,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,2FAA2F;IAC3F,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAeD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,MAAM,EACN,eAAoB,EACpB,QAAQ,EACR,OAAgB,EAChB,QAAsB,GACvB,EAAE,gBAAgB,2CAqQlB"}
@@ -19,8 +19,8 @@ export function ProjectCard({ project, schema, fieldOptionsMap = {}, priority, v
19
19
  const tagFields = schema
20
20
  .filter((f) => f.display_position === "tags" && f.type !== "number")
21
21
  .filter((f, i, arr) => arr.findIndex((x) => x.key === f.key) === i);
22
- // Numeric field for footer stat — the first visible number field (year completed)
23
- const numericField = schema.find((f) => f.type === "number" && f.display_position !== "hidden");
22
+ // Coverage/sq ft field for footer stat — matched by key or name regardless of type
23
+ const numericField = schema.find((f) => /sq|sqft|square|coverage/i.test(f.key + f.name));
24
24
  const locationField = schema.find((f) => f.type === "location");
25
25
  const badgeValue = badgeField
26
26
  ? parseSingleValue(project.custom_field_values[badgeField.key])
@@ -38,8 +38,13 @@ export function ProjectCard({ project, schema, fieldOptionsMap = {}, priority, v
38
38
  if (val === undefined || val === null || val === "")
39
39
  return null;
40
40
  const label = numericField.name.replace(/\s*\([^)]+\)/, "");
41
- const numVal = typeof val === "number" ? val : parseFloat(String(val));
42
- const formatted = isNaN(numVal) ? String(val) : String(Math.round(numVal));
41
+ // Coverage is a pre-formatted string from the API (e.g. "186,446 SF") render as-is.
42
+ // For numeric fields, format with commas.
43
+ const formatted = typeof val === "string"
44
+ ? val
45
+ : typeof val === "number"
46
+ ? Math.round(val).toLocaleString()
47
+ : String(val);
43
48
  return { label, formatted };
44
49
  })();
45
50
  const compactTags = tagFields.flatMap((field) => parseMultiValue(project.custom_field_values[field.key]));
@@ -120,7 +125,12 @@ export function ProjectCard({ project, schema, fieldOptionsMap = {}, priority, v
120
125
  if (vals.length === 0)
121
126
  return null;
122
127
  const optMap = (_a = fieldOptionsMap[field.key]) !== null && _a !== void 0 ? _a : {};
123
- return (_jsxs("div", { style: { marginBottom: "16px" }, children: [_jsx("p", { style: { fontSize: "10px", fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase", color: "#a1a1aa", marginBottom: "8px" }, children: "Systems Used:" }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: vals.map((val, i) => {
128
+ const hasOptions = Object.keys(optMap).length > 0;
129
+ // Filter out archived values — if optMap is populated and the value isn't in it, it's archived
130
+ const activeVals = hasOptions ? vals.filter((val) => optMap[val] !== undefined) : vals;
131
+ if (activeVals.length === 0)
132
+ return null;
133
+ return (_jsxs("div", { style: { marginBottom: "16px" }, children: [_jsx("p", { style: { fontSize: "10px", fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase", color: "#a1a1aa", marginBottom: "8px" }, children: "Systems Used:" }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: activeVals.map((val, i) => {
124
134
  var _a, _b, _c, _d;
125
135
  // Normalize: look up by id or label, always display the label
126
136
  const label = (_a = optMap[val]) !== null && _a !== void 0 ? _a : val;
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectDetail.d.ts","sourceRoot":"","sources":["../src/ProjectDetail.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAmDD,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,SAA0B,EAC1B,UAAe,GAChB,EAAE,kBAAkB,oDAyKpB"}
1
+ {"version":3,"file":"ProjectDetail.d.ts","sourceRoot":"","sources":["../src/ProjectDetail.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAmDD,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,SAA0B,EAC1B,UAAe,GAChB,EAAE,kBAAkB,oDAwPpB"}
@@ -38,7 +38,7 @@ const fetchProjectDetail = cache(async (apiBase, clientSlug, slug, revalidate) =
38
38
  });
39
39
  // ─── Component ───────────────────────────────────────────────────────────────
40
40
  export async function ProjectDetail({ slug, clientSlug, apiBase, backPath = "/projects", backLabel = "All Projects", revalidate = 60, }) {
41
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
41
+ var _a, _b, _c, _d, _e, _f, _g, _h;
42
42
  const { project, schema } = await fetchProjectDetail(apiBase, clientSlug, slug, revalidate);
43
43
  if (!project) {
44
44
  return (_jsx("div", { style: { textAlign: "center", padding: "6rem 1.5rem", fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }, children: _jsx("p", { style: { color: "#71717a", fontSize: "18px" }, children: "Project not found." }) }));
@@ -49,16 +49,8 @@ export async function ProjectDetail({ slug, clientSlug, apiBase, backPath = "/pr
49
49
  : ((_g = (_f = project.media) === null || _f === void 0 ? void 0 : _f.slice(1)) !== null && _g !== void 0 ? _g : []);
50
50
  const badgeField = schema.find((f) => f.display_position === "badge_overlay");
51
51
  const locationField = schema.find((f) => f.type === "location");
52
- // Year field: any number field whose key or name contains "year"
53
- const yearField = schema.find((f) => f.type === "number" && f.display_position !== "hidden" &&
54
- (/year/i.test(f.key) || /year/i.test(f.name)));
55
- // Sq footage field: any field (text or number) whose key or name matches common area/coverage terms
56
- // Falls back to the first non-year number field so it works regardless of client field naming
57
- const sqftField = (_h = schema.find((f) => f.display_position !== "hidden" &&
58
- (/coverage|sq.?ft|square.?foot|area|footage/i.test(f.key) || /coverage|sq.?ft|square.?foot|area|footage/i.test(f.name)))) !== null && _h !== void 0 ? _h : schema.find((f) => f.type === "number" && f.display_position !== "hidden" &&
59
- !(/year/i.test(f.key) || /year/i.test(f.name)));
60
52
  const badgeValue = badgeField
61
- ? ((_j = parseMultiValue(project.custom_field_values[badgeField.key])[0]) !== null && _j !== void 0 ? _j : null)
53
+ ? ((_h = parseMultiValue(project.custom_field_values[badgeField.key])[0]) !== null && _h !== void 0 ? _h : null)
62
54
  : null;
63
55
  const locationValue = locationField
64
56
  ? project.custom_field_values[locationField.key]
@@ -66,36 +58,92 @@ export async function ProjectDetail({ slug, clientSlug, apiBase, backPath = "/pr
66
58
  const locationString = locationValue
67
59
  ? [locationValue.city, locationValue.state].filter(Boolean).join(", ")
68
60
  : null;
69
- const coverageFormatted = (() => {
70
- const raw = sqftField ? project.custom_field_values[sqftField.key] : null;
61
+ // Helper: format a raw field value to a display string
62
+ function formatFieldValue(field, raw) {
71
63
  if (raw === null || raw === undefined || raw === "")
72
64
  return null;
73
65
  if (typeof raw === "string")
74
- return raw; // already formatted e.g. "225 SF"
75
- const n = typeof raw === "number" ? raw : parseFloat(String(raw));
76
- return isNaN(n) ? String(raw) : `${n.toLocaleString()} SF`;
77
- })();
78
- const yearCompleted = (() => {
79
- const raw = yearField ? project.custom_field_values[yearField.key] : null;
80
- if (raw === null || raw === undefined)
81
- return null;
82
- const n = typeof raw === "number" ? raw : parseFloat(String(raw));
83
- return isNaN(n) ? String(raw) : String(Math.round(n));
84
- })();
66
+ return raw;
67
+ if (typeof raw === "number") {
68
+ if (/year/i.test(field.key + field.name))
69
+ return String(Math.round(raw));
70
+ return Math.round(raw).toLocaleString();
71
+ }
72
+ if (Array.isArray(raw)) {
73
+ const vals = raw.map(String).filter(Boolean);
74
+ return vals.length > 0 ? vals.join(", ") : null;
75
+ }
76
+ return null;
77
+ }
78
+ // ── Stats bar: explicit ordered list of fields to show ────────────────────
79
+ // Location and badge/type are handled first from their detected fields,
80
+ // then remaining stat bar fields are matched by key in the schema.
81
+ const STAT_BAR_KEYS = ["coverage", "year-completed", "architect", "general-contractor"];
82
+ const metadataStats = [];
83
+ if (locationString) {
84
+ metadataStats.push({ label: "Location", value: locationString });
85
+ }
86
+ if (badgeValue) {
87
+ metadataStats.push({ label: badgeField.name, value: badgeValue });
88
+ }
89
+ for (const key of STAT_BAR_KEYS) {
90
+ const field = schema.find((f) => f.key === key);
91
+ if (!field)
92
+ continue;
93
+ const raw = project.custom_field_values[field.key];
94
+ const formatted = formatFieldValue(field, raw);
95
+ // Use "Completed" as the label for year-completed
96
+ const label = key === "year-completed" ? "Completed" : field.name;
97
+ if (formatted)
98
+ metadataStats.push({ label, value: formatted });
99
+ }
100
+ // ── Specs sidebar: explicit ordered list of fields ────────────────────────
101
+ const SPEC_SIDEBAR_KEYS = [
102
+ "systems-used",
103
+ "systems",
104
+ "series-used",
105
+ "operation-type",
106
+ "finishes",
107
+ "specifications",
108
+ ];
109
+ const specFields = SPEC_SIDEBAR_KEYS
110
+ .map((key) => schema.find((f) => f.key === key))
111
+ .filter((f) => f !== undefined);
85
112
  const font = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
86
- return (_jsxs("main", { style: { minHeight: "100vh", backgroundColor: "#fff", fontFamily: font }, children: [_jsxs("section", { className: "chisel-hero-img", style: { position: "relative", width: "100%", overflow: "hidden" }, children: [imageUrl ? (_jsx("img", { src: imageUrl, alt: project.title, style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } })) : (_jsx("div", { style: { position: "absolute", inset: 0, backgroundColor: "#27272a" } })), _jsx("div", { style: { position: "absolute", inset: 0, background: "linear-gradient(to top, rgba(0,0,0,0.82) 0%, rgba(0,0,0,0.38) 50%, rgba(0,0,0,0.12) 100%)" } }), _jsxs("div", { style: { position: "absolute", bottom: 0, left: 0, right: 0, padding: "0 1rem 1.5rem", maxWidth: "900px" }, children: [badgeValue && (_jsx("p", { style: { color: "#f18a00", fontSize: "11px", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", margin: "0 0 10px 0" }, children: badgeValue })), _jsx("h1", { style: { color: "#fff", fontWeight: 700, fontSize: "clamp(28px, 4vw, 52px)", lineHeight: 1.1, margin: "0 0 12px 0", fontFamily: font }, children: project.title }), locationString && (_jsxs("p", { style: { display: "flex", alignItems: "center", gap: "6px", color: "rgba(255,255,255,0.75)", fontSize: "15px", margin: 0 }, children: [_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { flexShrink: 0 }, children: [_jsx("path", { d: "M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" }), _jsx("circle", { cx: "12", cy: "10", r: "3" })] }), locationString] }))] })] }), _jsxs("section", { style: { borderBottom: "1px solid #e4e4e7", backgroundColor: "#fff" }, children: [_jsx("style", { children: `
87
- .chisel-stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; }
88
- @media (min-width: 768px) { .chisel-stats-grid { grid-template-columns: repeat(4, 1fr); gap: 2rem; } }
89
- .chisel-gallery-placeholder { display: grid; grid-template-columns: 1fr; gap: 12px; }
90
- @media (min-width: 640px) { .chisel-gallery-placeholder { grid-template-columns: repeat(3, 1fr); } }
91
- .chisel-hero-img { height: 50vw; min-height: 220px; max-height: 560px; }
92
- .chisel-gallery-main-img { height: 60vw; min-height: 240px; max-height: 520px; }
93
- ` }), _jsx("div", { style: { maxWidth: "1280px", margin: "0 auto", padding: "1.5rem 1rem", boxSizing: "border-box" }, className: "chisel-stats-grid", children: [
94
- { label: "Location", value: locationString !== null && locationString !== void 0 ? locationString : "—" },
95
- { label: "Project Type", value: badgeValue !== null && badgeValue !== void 0 ? badgeValue : "—" },
96
- { label: "Sq. Footage", value: coverageFormatted !== null && coverageFormatted !== void 0 ? coverageFormatted : "—" },
97
- { label: "Completed", value: yearCompleted !== null && yearCompleted !== void 0 ? yearCompleted : "" },
98
- ].map(({ label, value }) => (_jsxs("div", { children: [_jsx("p", { style: { fontSize: "10px", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: "#a1a1aa", margin: "0 0 6px 0" }, children: label }), _jsx("p", { style: { color: "#18181b", fontWeight: 600, fontSize: "15px", margin: 0 }, children: value })] }, label))) })] }), _jsxs("article", { style: { maxWidth: "1280px", margin: "0 auto", padding: "2rem 1rem", boxSizing: "border-box", display: "flex", flexDirection: "column", gap: "2.5rem" }, children: [(project.blurb || project.description) && (_jsxs("section", { style: { maxWidth: "900px" }, children: [_jsx("h2", { style: { color: "#18181b", fontWeight: 700, fontSize: "22px", margin: "0 0 16px 0", fontFamily: font }, children: "Project Overview" }), project.blurb && (_jsx("p", { style: { color: "#3f3f46", fontSize: "16px", lineHeight: 1.7, margin: "0 0 16px 0" }, children: project.blurb })), project.description && project.description !== project.blurb && (_jsx("p", { style: { color: "#3f3f46", fontSize: "16px", lineHeight: 1.7, margin: 0 }, children: project.description }))] })), _jsxs("section", { children: [_jsx("h2", { style: { color: "#18181b", fontWeight: 700, fontSize: "22px", margin: "0 0 1.5rem 0", fontFamily: font }, children: "Project Gallery" }), galleryImages.length > 0 ? (_jsx(GalleryCarousel, { images: galleryImages, projectTitle: project.title })) : (
113
+ return (_jsxs("main", { style: { minHeight: "100vh", backgroundColor: "#fff", fontFamily: font }, children: [_jsxs("section", { className: "chisel-hero-img", style: { position: "relative", width: "100%", overflow: "hidden" }, children: [imageUrl ? (_jsx("img", { src: imageUrl, alt: project.title, style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } })) : (_jsx("div", { style: { position: "absolute", inset: 0, backgroundColor: "#27272a" } })), _jsx("div", { style: { position: "absolute", inset: 0, background: "linear-gradient(to top, rgba(0,0,0,0.82) 0%, rgba(0,0,0,0.38) 50%, rgba(0,0,0,0.12) 100%)" } }), _jsxs("div", { style: { position: "absolute", bottom: 0, left: 0, right: 0, padding: "0 1rem 1.5rem", maxWidth: "900px" }, children: [badgeValue && (_jsx("p", { style: { color: "#f18a00", fontSize: "11px", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", margin: "0 0 10px 0" }, children: badgeValue })), _jsx("h1", { style: { color: "#fff", fontWeight: 700, fontSize: "clamp(28px, 4vw, 52px)", lineHeight: 1.1, margin: "0 0 12px 0", fontFamily: font }, children: project.title }), locationString && (_jsxs("p", { style: { display: "flex", alignItems: "center", gap: "6px", color: "rgba(255,255,255,0.75)", fontSize: "15px", margin: 0 }, children: [_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { flexShrink: 0 }, children: [_jsx("path", { d: "M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" }), _jsx("circle", { cx: "12", cy: "10", r: "3" })] }), locationString] }))] })] }), metadataStats.length > 0 && (_jsxs("section", { style: { borderBottom: "1px solid #e4e4e7", backgroundColor: "#fff" }, children: [_jsx("style", { children: `
114
+ .chisel-stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0; }
115
+ @media (min-width: 640px) { .chisel-stats-grid { grid-template-columns: repeat(3, 1fr); } }
116
+ @media (min-width: 1024px) { .chisel-stats-grid { grid-template-columns: repeat(${Math.min(metadataStats.length, 6)}, 1fr); } }
117
+ .chisel-stat-item { padding: 1.5rem 1.25rem; border-right: 1px solid #e4e4e7; border-bottom: 1px solid #e4e4e7; }
118
+ .chisel-stat-item:last-child { border-right: none; }
119
+ .chisel-gallery-placeholder { display: grid; grid-template-columns: 1fr; gap: 12px; }
120
+ @media (min-width: 640px) { .chisel-gallery-placeholder { grid-template-columns: repeat(3, 1fr); } }
121
+ .chisel-hero-img { height: 50vw; min-height: 220px; max-height: 560px; }
122
+ .chisel-overview-grid { display: grid; grid-template-columns: 1fr; gap: 2.5rem; }
123
+ @media (min-width: 1024px) { .chisel-overview-grid { grid-template-columns: 1fr 300px; gap: 4rem; align-items: start; } }
124
+ ` }), _jsx("div", { style: { maxWidth: "1280px", margin: "0 auto", boxSizing: "border-box", padding: "0 0 0 0" }, className: "chisel-stats-grid", children: metadataStats.map(({ label, value }) => (_jsxs("div", { className: "chisel-stat-item", children: [_jsx("p", { style: { fontSize: "10px", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: "#a1a1aa", margin: "0 0 8px 0" }, children: label }), _jsx("p", { style: { color: "#18181b", fontWeight: 600, fontSize: "15px", margin: 0, lineHeight: 1.4 }, children: value })] }, label))) })] })), _jsxs("article", { style: { maxWidth: "1280px", margin: "0 auto", padding: "3rem 1.5rem", boxSizing: "border-box" }, children: [(project.blurb || project.description || specFields.length > 0) && (_jsxs("section", { style: { marginBottom: "3rem" }, children: [_jsx("div", { style: { borderBottom: "1px solid #e4e4e7", marginBottom: "2rem", paddingBottom: "1rem", display: "flex", alignItems: "baseline", justifyContent: "space-between" }, children: _jsx("h2", { style: { color: "#18181b", fontWeight: 700, fontSize: "20px", margin: 0, letterSpacing: "-0.01em", fontFamily: font }, children: "Project Overview" }) }), _jsxs("div", { className: "chisel-overview-grid", children: [_jsxs("div", { children: [project.blurb && (_jsx("p", { style: { color: "#3f3f46", fontSize: "16px", fontWeight: 400, lineHeight: 1.85, margin: "0 0 20px 0" }, children: project.blurb })), project.description && project.description !== project.blurb && (_jsx("p", { style: { color: "#3f3f46", fontSize: "16px", fontWeight: 400, lineHeight: 1.85, margin: 0 }, children: project.description }))] }), specFields.length > 0 && (_jsx("aside", { style: { borderLeft: "3px solid #f18a00", paddingLeft: "2rem" }, children: _jsx("div", { style: { display: "flex", flexDirection: "column", gap: "2rem" }, children: specFields.map((field) => {
125
+ const raw = project.custom_field_values[field.key];
126
+ const vals = Array.isArray(raw)
127
+ ? raw.map(String).filter(Boolean)
128
+ : typeof raw === "string"
129
+ ? [raw]
130
+ : raw !== null && raw !== undefined
131
+ ? [String(raw)]
132
+ : [];
133
+ if (vals.length === 0)
134
+ return null;
135
+ return (_jsxs("div", { children: [_jsx("p", { style: { fontSize: "10px", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: "#a1a1aa", margin: "0 0 12px 0" }, children: field.name }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: vals.map((val) => (_jsx("span", { style: {
136
+ display: "inline-block",
137
+ padding: "5px 14px",
138
+ backgroundColor: "#fafafa",
139
+ border: "1px solid #e4e4e7",
140
+ color: "#3f3f46",
141
+ fontSize: "13px",
142
+ fontWeight: 500,
143
+ borderRadius: "2px",
144
+ lineHeight: 1.5,
145
+ }, children: val }, val))) })] }, field.key));
146
+ }) }) }))] })] })), _jsxs("section", { children: [_jsx("div", { style: { borderBottom: "1px solid #e4e4e7", marginBottom: "2rem", paddingBottom: "1rem" }, children: _jsx("h2", { style: { color: "#18181b", fontWeight: 700, fontSize: "20px", margin: 0, letterSpacing: "-0.01em", fontFamily: font }, children: "Project Gallery" }) }), galleryImages.length > 0 ? (_jsx(GalleryCarousel, { images: galleryImages, projectTitle: project.title })) : (
99
147
  /* Placeholder */
100
148
  _jsx("div", { className: "chisel-gallery-placeholder", children: [0, 1, 2].map((i) => (_jsxs("div", { style: { aspectRatio: "4/3", borderRadius: "4px", backgroundColor: "#f9f9f9", border: "1px solid #e4e4e7", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "8px" }, children: [_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#a1a1aa", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909M3.75 19.5h16.5" }) }), _jsx("p", { style: { color: "#a1a1aa", fontSize: "12px", margin: 0 }, children: "Photos coming soon" })] }, i))) }))] })] })] }));
101
149
  }
@@ -4,6 +4,11 @@ export interface ProjectMenuProps {
4
4
  clientSlug: string;
5
5
  /** API base URL e.g. "https://nexus.chiselandco.com" */
6
6
  apiBase: string;
7
+ /**
8
+ * Optional menu ID to fetch a specific curated menu instead of all projects.
9
+ * When provided, fetches from /api/v1/clients/{clientSlug}/menus/{menuId}
10
+ */
11
+ menuId?: string;
7
12
  /** Base path for project detail links. Defaults to "/projects" */
8
13
  basePath?: string;
9
14
  /** Path for the "View All" link. Defaults to basePath */
@@ -46,14 +51,17 @@ export interface ProjectMenuProps {
46
51
  * import { revalidateTag } from "next/cache"
47
52
  * revalidateTag("chisel-menu-my-client")
48
53
  */
49
- export declare function createMenuHandler({ clientSlug, apiBase, revalidate, }: {
54
+ export declare function createMenuHandler({ clientSlug, apiBase, menuId, revalidate, }: {
50
55
  clientSlug: string;
51
56
  apiBase: string;
57
+ /** Optional menu ID to fetch a specific curated menu instead of all projects */
58
+ menuId?: string;
52
59
  revalidate?: number;
53
60
  }): (request: Request) => Promise<Response>;
54
- export declare function fetchProjectMenuData({ apiBase, clientSlug, revalidate, noCache, }: {
61
+ export declare function fetchProjectMenuData({ apiBase, clientSlug, menuId, revalidate, noCache, }: {
55
62
  apiBase: string;
56
63
  clientSlug: string;
64
+ menuId?: string;
57
65
  revalidate?: number;
58
66
  noCache?: boolean;
59
67
  }): Promise<{
@@ -67,5 +75,5 @@ export declare function fetchProjectMenuData({ apiBase, clientSlug, revalidate,
67
75
  filterFieldName: string;
68
76
  fieldOptionsMap: Record<string, Record<string, string>>;
69
77
  }>;
70
- export declare function ProjectMenu({ clientSlug, apiBase, basePath, viewAllPath, subtitle, font, maxProjects, revalidate, noCache, }: ProjectMenuProps): Promise<import("react/jsx-runtime").JSX.Element>;
78
+ export declare function ProjectMenu({ clientSlug, apiBase, menuId, basePath, viewAllPath, subtitle, font, maxProjects, revalidate, noCache, }: ProjectMenuProps): Promise<import("react/jsx-runtime").JSX.Element>;
71
79
  //# sourceMappingURL=ProjectMenu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMenu.d.ts","sourceRoot":"","sources":["../src/ProjectMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAKzD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,OAAO,EACP,UAAkB,GACnB,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,aACoC,OAAO,uBAsD3C;AAED,wBAAsB,oBAAoB,CAAC,EACzC,OAAO,EACP,UAAU,EACV,UAAkB,EAClB,OAAe,GAChB,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,OAAO,CAAC;IACV,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,aAAa,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACxD,CAAC,CAqBD;AA6CD,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,WAAW,EACX,QAAQ,EACR,IAA0E,EAC1E,WAAe,EACf,UAAkB,EAClB,OAAe,GAChB,EAAE,gBAAgB,oDAiClB"}
1
+ {"version":3,"file":"ProjectMenu.d.ts","sourceRoot":"","sources":["../src/ProjectMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAKzD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,UAAkB,GACnB,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,aACoC,OAAO,uBAwE3C;AAED,wBAAsB,oBAAoB,CAAC,EACzC,OAAO,EACP,UAAU,EACV,MAAM,EACN,UAAkB,EAClB,OAAe,GAChB,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,OAAO,CAAC;IACV,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,aAAa,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACxD,CAAC,CAqBD;AAgED,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,WAAW,EACX,QAAQ,EACR,IAA0E,EAC1E,WAAe,EACf,UAAkB,EAClB,OAAe,GAChB,EAAE,gBAAgB,oDAiClB"}
@@ -26,27 +26,43 @@ const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
26
26
  * import { revalidateTag } from "next/cache"
27
27
  * revalidateTag("chisel-menu-my-client")
28
28
  */
29
- export function createMenuHandler({ clientSlug, apiBase, revalidate = 86400, }) {
29
+ export function createMenuHandler({ clientSlug, apiBase, menuId, revalidate = 86400, }) {
30
30
  return async function GET(request) {
31
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
31
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
32
32
  const bypass = process.env.CHISEL_CACHE_BYPASS === "true" ||
33
33
  new URL(request.url).searchParams.has("bust");
34
+ const cacheTag = menuId ? `chisel-menu-${clientSlug}-${menuId}` : `chisel-menu-${clientSlug}`;
34
35
  const fetchOpts = bypass
35
36
  ? { cache: "no-store" }
36
- : { next: { revalidate, tags: [`chisel-menu-${clientSlug}`] } };
37
+ : { next: { revalidate, tags: [cacheTag] } };
37
38
  try {
38
- const [projectsRes, fieldsRes] = await Promise.all([
39
+ // Always fetch /projects for schema, and /fields for options
40
+ // If menuId is provided, also fetch the menu endpoint for curated projects
41
+ const fetches = [
39
42
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts),
40
43
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`, fetchOpts),
41
- ]);
42
- const json = projectsRes.ok ? await projectsRes.json() : {};
43
- const projects = (_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : [];
44
- const schema = (_c = (_b = json === null || json === void 0 ? void 0 : json.client) === null || _b === void 0 ? void 0 : _b.custom_fields_schema) !== null && _c !== void 0 ? _c : [];
44
+ ];
45
+ if (menuId) {
46
+ fetches.push(fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`, fetchOpts));
47
+ }
48
+ const responses = await Promise.all(fetches);
49
+ const [projectsRes, fieldsRes] = responses;
50
+ const menuRes = menuId ? responses[2] : null;
51
+ const projectsJson = projectsRes.ok ? await projectsRes.json() : {};
52
+ const menuJson = menuRes && menuRes.ok ? await menuRes.json() : null;
53
+ // If menuId provided, use menu projects; otherwise use all projects
54
+ // Filter out archived (is_published === false) projects in both cases
55
+ const rawProjects = menuJson
56
+ ? ((_b = (_a = menuJson.projects) !== null && _a !== void 0 ? _a : menuJson.data) !== null && _b !== void 0 ? _b : [])
57
+ : ((_c = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.data) !== null && _c !== void 0 ? _c : []);
58
+ const projects = rawProjects.filter((p) => p.is_published !== false);
59
+ // Always get schema from /projects response
60
+ const schema = (_e = (_d = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.client) === null || _d === void 0 ? void 0 : _d.custom_fields_schema) !== null && _e !== void 0 ? _e : [];
45
61
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
46
62
  const fieldOptionsMap = {};
47
- for (const field of ((_d = fieldsJson.fields) !== null && _d !== void 0 ? _d : [])) {
63
+ for (const field of ((_f = fieldsJson.fields) !== null && _f !== void 0 ? _f : [])) {
48
64
  const map = {};
49
- for (const opt of ((_e = field.options) !== null && _e !== void 0 ? _e : [])) {
65
+ for (const opt of ((_g = field.options) !== null && _g !== void 0 ? _g : [])) {
50
66
  if (typeof opt === "object" && opt.id && opt.label) {
51
67
  map[opt.id] = opt.label;
52
68
  map[opt.label] = opt.label;
@@ -54,9 +70,9 @@ export function createMenuHandler({ clientSlug, apiBase, revalidate = 86400, })
54
70
  }
55
71
  fieldOptionsMap[field.key] = map;
56
72
  }
57
- const filterField = (_f = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _f !== void 0 ? _f : null;
73
+ const filterField = (_h = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _h !== void 0 ? _h : null;
58
74
  const filterOptions = filterField
59
- ? ((_g = filterField.options) !== null && _g !== void 0 ? _g : []).map((opt) => {
75
+ ? ((_j = filterField.options) !== null && _j !== void 0 ? _j : []).map((opt) => {
60
76
  var _a, _b;
61
77
  if (typeof opt === "string")
62
78
  return { id: opt.toLowerCase().replace(/\s+/g, "-"), label: opt };
@@ -67,19 +83,19 @@ export function createMenuHandler({ clientSlug, apiBase, revalidate = 86400, })
67
83
  projects,
68
84
  schema,
69
85
  filterOptions,
70
- filterFieldKey: (_h = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _h !== void 0 ? _h : null,
71
- filterFieldName: (_j = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _j !== void 0 ? _j : "Project Type",
86
+ filterFieldKey: (_k = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _k !== void 0 ? _k : null,
87
+ filterFieldName: (_l = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _l !== void 0 ? _l : "Project Type",
72
88
  fieldOptionsMap,
73
89
  });
74
90
  }
75
- catch (_k) {
91
+ catch (_m) {
76
92
  return Response.json({ projects: [], schema: [], filterOptions: [], filterFieldKey: null, filterFieldName: "Project Type", fieldOptionsMap: {} }, { status: 500 });
77
93
  }
78
94
  };
79
95
  }
80
- export async function fetchProjectMenuData({ apiBase, clientSlug, revalidate = 86400, noCache = false, }) {
96
+ export async function fetchProjectMenuData({ apiBase, clientSlug, menuId, revalidate = 86400, noCache = false, }) {
81
97
  var _a, _b, _c, _d;
82
- const raw = await _fetchMenuData(apiBase, clientSlug, revalidate, noCache);
98
+ const raw = await _fetchMenuData(apiBase, clientSlug, menuId, revalidate, noCache);
83
99
  const filterField = (_a = raw.schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _a !== void 0 ? _a : null;
84
100
  const filterOptions = filterField
85
101
  ? ((_b = filterField.options) !== null && _b !== void 0 ? _b : []).map((opt) => {
@@ -99,27 +115,42 @@ export async function fetchProjectMenuData({ apiBase, clientSlug, revalidate = 8
99
115
  fieldOptionsMap: raw.fieldOptionsMap,
100
116
  };
101
117
  }
102
- const _fetchMenuData = cache(async (apiBase, clientSlug, revalidate, noCache = false) => {
103
- var _a, _b, _c, _d, _e;
118
+ const _fetchMenuData = cache(async (apiBase, clientSlug, menuId, revalidate, noCache = false) => {
119
+ var _a, _b, _c, _d, _e, _f, _g;
104
120
  const fetchOpts = noCache
105
121
  ? { cache: "no-store" }
106
122
  : { next: { revalidate } };
107
123
  try {
108
- const [res, fieldsRes] = await Promise.all([
124
+ // Always fetch /projects for schema, and /fields for options
125
+ // If menuId is provided, also fetch the menu endpoint for curated projects
126
+ const fetches = [
109
127
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts),
110
128
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`, fetchOpts),
111
- ]);
112
- if (!res.ok)
129
+ ];
130
+ if (menuId) {
131
+ fetches.push(fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`, fetchOpts));
132
+ }
133
+ const responses = await Promise.all(fetches);
134
+ const [projectsRes, fieldsRes] = responses;
135
+ const menuRes = menuId ? responses[2] : null;
136
+ if (!projectsRes.ok)
113
137
  return { projects: [], schema: [], fieldOptionsMap: {} };
114
- const json = await res.json();
115
- const projects = (_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : [];
116
- const schema = (_c = (_b = json === null || json === void 0 ? void 0 : json.client) === null || _b === void 0 ? void 0 : _b.custom_fields_schema) !== null && _c !== void 0 ? _c : [];
138
+ const projectsJson = await projectsRes.json();
139
+ const menuJson = menuRes && menuRes.ok ? await menuRes.json() : null;
140
+ // If menuId provided, use menu projects; otherwise use all projects
141
+ // Filter out archived (is_published === false) projects in both cases
142
+ const rawProjects = menuJson
143
+ ? ((_b = (_a = menuJson.projects) !== null && _a !== void 0 ? _a : menuJson.data) !== null && _b !== void 0 ? _b : [])
144
+ : ((_c = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.data) !== null && _c !== void 0 ? _c : []);
145
+ const projects = rawProjects.filter((p) => p.is_published !== false);
146
+ // Always get schema from /projects response
147
+ const schema = (_e = (_d = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.client) === null || _d === void 0 ? void 0 : _d.custom_fields_schema) !== null && _e !== void 0 ? _e : [];
117
148
  // Build fieldOptionsMap: { fieldKey: { id: label, label: label } }
118
149
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
119
150
  const fieldOptionsMap = {};
120
- for (const field of ((_d = fieldsJson.fields) !== null && _d !== void 0 ? _d : [])) {
151
+ for (const field of ((_f = fieldsJson.fields) !== null && _f !== void 0 ? _f : [])) {
121
152
  const map = {};
122
- for (const opt of ((_e = field.options) !== null && _e !== void 0 ? _e : [])) {
153
+ for (const opt of ((_g = field.options) !== null && _g !== void 0 ? _g : [])) {
123
154
  if (typeof opt === "object" && opt.id && opt.label) {
124
155
  map[opt.id] = opt.label;
125
156
  map[opt.label] = opt.label;
@@ -129,13 +160,13 @@ const _fetchMenuData = cache(async (apiBase, clientSlug, revalidate, noCache = f
129
160
  }
130
161
  return { projects, schema, fieldOptionsMap };
131
162
  }
132
- catch (_f) {
163
+ catch (_h) {
133
164
  return { projects: [], schema: [], fieldOptionsMap: {} };
134
165
  }
135
166
  });
136
- export async function ProjectMenu({ clientSlug, apiBase, basePath = "/projects", viewAllPath, subtitle, font = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", maxProjects = 6, revalidate = 86400, noCache = false, }) {
167
+ export async function ProjectMenu({ clientSlug, apiBase, menuId, basePath = "/projects", viewAllPath, subtitle, font = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", maxProjects = 6, revalidate = 86400, noCache = false, }) {
137
168
  var _a, _b, _c, _d;
138
- const { projects, schema, fieldOptionsMap } = await _fetchMenuData(apiBase, clientSlug, revalidate, noCache);
169
+ const { projects, schema, fieldOptionsMap } = await _fetchMenuData(apiBase, clientSlug, menuId, revalidate, noCache);
139
170
  // Find the filterable select field (badge_overlay is our category field)
140
171
  const filterField = (_a = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _a !== void 0 ? _a : null;
141
172
  // Build filter option list from schema
@@ -14,6 +14,11 @@ export interface ProjectMenuClientProps {
14
14
  */
15
15
  clientSlug?: string;
16
16
  apiBase?: string;
17
+ /**
18
+ * Optional menu ID to fetch a specific curated menu instead of all projects.
19
+ * When provided, fetches from /api/v1/clients/{clientSlug}/menus/{menuId}
20
+ */
21
+ menuId?: string;
17
22
  projects?: Project[];
18
23
  schema?: CustomFieldSchema[];
19
24
  filterOptions?: {
@@ -29,5 +34,5 @@ export interface ProjectMenuClientProps {
29
34
  font?: string;
30
35
  maxProjects?: number;
31
36
  }
32
- export declare function ProjectMenuClient({ dataUrl, clientSlug, apiBase, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp, fieldOptionsMap: fieldOptionsMapProp, subtitle, basePath, viewAllPath, font, maxProjects, }: ProjectMenuClientProps): import("react/jsx-runtime").JSX.Element;
37
+ export declare function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp, fieldOptionsMap: fieldOptionsMapProp, subtitle, basePath, viewAllPath, font, maxProjects, }: ProjectMenuClientProps): import("react/jsx-runtime").JSX.Element;
33
38
  //# sourceMappingURL=ProjectMenuClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAa3E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAmBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,kBAAkB,EAClC,eAAe,EAAE,mBAAoC,EACrD,eAAe,EAAE,mBAAwB,EACzC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,IAAmB,EACnB,WAAe,GAChB,EAAE,sBAAsB,2CAqcxB"}
1
+ {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAa3E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAmBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,iBAAiB,EAChC,cAAc,EAAE,kBAAkB,EAClC,eAAe,EAAE,mBAAoC,EACrD,eAAe,EAAE,mBAAwB,EACzC,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,IAAmB,EACnB,WAAe,GAChB,EAAE,sBAAsB,2CA4fxB"}