project-portfolio 2.1.0 → 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.
package/README.md CHANGED
@@ -216,7 +216,7 @@ export default function ProjectsPage() {
216
216
 
217
217
  ### `ProjectDetail`
218
218
 
219
- A full server-rendered project detail page. Fetches a single project by slug and renders its image gallery, custom fields, and a back link.
219
+ A full server-rendered project detail page. Fetches a single project by slug and renders a hero image, a dynamic stats bar, a "Project Overview" section with description and specs sidebar, a photo gallery, and a back link.
220
220
 
221
221
  ```tsx
222
222
  // app/projects/[slug]/page.tsx
@@ -235,6 +235,36 @@ export default async function ProjectPage({ params }: { params: { slug: string }
235
235
  }
236
236
  ```
237
237
 
238
+ #### Stats bar
239
+
240
+ The stats bar below the hero is driven by an explicit ordered key list. Fields are shown in this order when present in the schema and populated on the project:
241
+
242
+ | Schema key | Label shown |
243
+ |---|---|
244
+ | `location` | Location |
245
+ | `type` (badge field) | field `name` from schema |
246
+ | `coverage` | Coverage |
247
+ | `year-completed` | Completed |
248
+ | `architect` | Architect |
249
+ | `general-contractor` | General Contractor |
250
+
251
+ If a field doesn't exist in the schema for a given client, or the project has no value for it, that stat is silently omitted. The column count adjusts automatically — 2 columns on mobile, 3 on tablet, up to 6 on desktop.
252
+
253
+ #### Project Overview specs sidebar
254
+
255
+ The "Project Overview" section renders the project description on the left and a specs sidebar on the right (amber accent border). The sidebar shows the following fields when populated, in this order:
256
+
257
+ | Schema key | Label shown |
258
+ |---|---|
259
+ | `systems-used` | Systems Used |
260
+ | `systems` | Track Systems |
261
+ | `series-used` | Series Used |
262
+ | `operation-type` | Operation Type |
263
+ | `finishes` | Finishes |
264
+ | `specifications` | Specifications |
265
+
266
+ Each group renders values as outlined pills. Fields with no value for the current project are omitted. Both the stats bar and the specs sidebar are fully schema-driven — if a key doesn't exist in a client's schema it is simply not shown, making `ProjectDetail` safe to reuse across clients with wildly different field configurations.
267
+
238
268
  | Prop | Type | Required | Default | Description |
239
269
  |---|---|---|---|---|
240
270
  | `slug` | `string` | Yes | — | The project slug to load |
@@ -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
  }
@@ -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;;;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,uBAsE3C;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;AA8DD,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"}
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"}
@@ -51,9 +51,11 @@ export function createMenuHandler({ clientSlug, apiBase, menuId, revalidate = 86
51
51
  const projectsJson = projectsRes.ok ? await projectsRes.json() : {};
52
52
  const menuJson = menuRes && menuRes.ok ? await menuRes.json() : null;
53
53
  // If menuId provided, use menu projects; otherwise use all projects
54
- const projects = menuJson
54
+ // Filter out archived (is_published === false) projects in both cases
55
+ const rawProjects = menuJson
55
56
  ? ((_b = (_a = menuJson.projects) !== null && _a !== void 0 ? _a : menuJson.data) !== null && _b !== void 0 ? _b : [])
56
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);
57
59
  // Always get schema from /projects response
58
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 : [];
59
61
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
@@ -136,9 +138,11 @@ const _fetchMenuData = cache(async (apiBase, clientSlug, menuId, revalidate, noC
136
138
  const projectsJson = await projectsRes.json();
137
139
  const menuJson = menuRes && menuRes.ok ? await menuRes.json() : null;
138
140
  // If menuId provided, use menu projects; otherwise use all projects
139
- const projects = menuJson
141
+ // Filter out archived (is_published === false) projects in both cases
142
+ const rawProjects = menuJson
140
143
  ? ((_b = (_a = menuJson.projects) !== null && _a !== void 0 ? _a : menuJson.data) !== null && _b !== void 0 ? _b : [])
141
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);
142
146
  // Always get schema from /projects response
143
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 : [];
144
148
  // Build fieldOptionsMap: { fieldKey: { id: label, label: label } }
@@ -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;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,2CAwfxB"}
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"}
@@ -56,7 +56,7 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
56
56
  ]);
57
57
  const menuJson = menuRes.ok ? await menuRes.json() : {};
58
58
  const projectsJson = projectsRes.ok ? await projectsRes.json() : {};
59
- const projects = (_h = (_g = menuJson === null || menuJson === void 0 ? void 0 : menuJson.projects) !== null && _g !== void 0 ? _g : menuJson === null || menuJson === void 0 ? void 0 : menuJson.data) !== null && _h !== void 0 ? _h : [];
59
+ const projects = ((_h = (_g = menuJson === null || menuJson === void 0 ? void 0 : menuJson.projects) !== null && _g !== void 0 ? _g : menuJson === null || menuJson === void 0 ? void 0 : menuJson.data) !== null && _h !== void 0 ? _h : []).filter((p) => p.is_published !== false);
60
60
  // Get schema from menu response first, fallback to projects response
61
61
  const schema = (_o = (_l = (_k = (_j = menuJson === null || menuJson === void 0 ? void 0 : menuJson.client) === null || _j === void 0 ? void 0 : _j.custom_fields_schema) !== null && _k !== void 0 ? _k : menuJson === null || menuJson === void 0 ? void 0 : menuJson.schema) !== null && _l !== void 0 ? _l : (_m = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.client) === null || _m === void 0 ? void 0 : _m.custom_fields_schema) !== null && _o !== void 0 ? _o : [];
62
62
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
@@ -95,7 +95,7 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
95
95
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`),
96
96
  ]);
97
97
  const json = projectsRes.ok ? await projectsRes.json() : {};
98
- const projects = (_v = json === null || json === void 0 ? void 0 : json.data) !== null && _v !== void 0 ? _v : [];
98
+ const projects = ((_v = json === null || json === void 0 ? void 0 : json.data) !== null && _v !== void 0 ? _v : []).filter((p) => p.is_published !== false);
99
99
  const schema = (_x = (_w = json === null || json === void 0 ? void 0 : json.client) === null || _w === void 0 ? void 0 : _w.custom_fields_schema) !== null && _x !== void 0 ? _x : [];
100
100
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
101
101
  const fieldOptionsMap = {};
@@ -348,7 +348,12 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
348
348
  }, children: project.title }), badge && (_jsx("p", { style: { fontSize: "12px", color: "#71717a", margin: 0, fontFamily: font }, children: badge })), tags.length > 0 && (() => {
349
349
  var _a;
350
350
  const optMap = tagsField ? ((_a = fieldOptionsMap[tagsField.key]) !== null && _a !== void 0 ? _a : {}) : {};
351
- const tagLabels = tags.map((t) => { var _a; return (_a = optMap[t]) !== null && _a !== void 0 ? _a : t; }).slice(0, 2);
351
+ const hasOptions = Object.keys(optMap).length > 0;
352
+ // Filter out archived values, then resolve to display labels
353
+ const activeTags = hasOptions ? tags.filter((t) => optMap[t] !== undefined) : tags;
354
+ const tagLabels = activeTags.map((t) => { var _a; return (_a = optMap[t]) !== null && _a !== void 0 ? _a : t; }).slice(0, 2);
355
+ if (tagLabels.length === 0)
356
+ return null;
352
357
  return (_jsx("p", { style: { fontSize: "11px", color: ACCENT, margin: 0, fontFamily: font, lineHeight: 1.4 }, children: tagLabels.join(" · ") }));
353
358
  })()] })] }, project.id));
354
359
  }) }))] }), _jsx("div", { className: "chisel-menu-divider-v" }), _jsxs("div", { className: "chisel-menu-right", children: [filterOptions.length > 0 && (_jsxs(_Fragment, { children: [_jsx("p", { style: {
@@ -49,7 +49,7 @@ const fetchPortfolioData = cache(async (apiBase, clientSlug, revalidate, filters
49
49
  }
50
50
  return {
51
51
  clientName: (_f = (_e = json.client) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : "Projects",
52
- projects: (_g = json.data) !== null && _g !== void 0 ? _g : [],
52
+ projects: ((_g = json.data) !== null && _g !== void 0 ? _g : []).filter((p) => p.is_published !== false),
53
53
  schema,
54
54
  fieldOptionsMap,
55
55
  };
@@ -52,7 +52,7 @@ export function ProjectPortfolioClient({ clientSlug, apiBase, basePath = "/proje
52
52
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`),
53
53
  ]);
54
54
  const json = projectsRes.ok ? await projectsRes.json() : {};
55
- const projects = (_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : [];
55
+ const projects = ((_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : []).filter((p) => p.is_published !== false);
56
56
  // Deduplicate schema keys
57
57
  const seen = new Set();
58
58
  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 : []).filter((f) => {
@@ -29,7 +29,7 @@ const fetchSimilarData = cache(async (apiBase, clientSlug, revalidate) => {
29
29
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`, fetchOpts),
30
30
  ]);
31
31
  const json = res.ok ? await res.json() : null;
32
- const allProjects = (_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : [];
32
+ const allProjects = ((_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : []).filter((p) => p.is_published !== false);
33
33
  const schema = dedupeByKey((_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 : []);
34
34
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
35
35
  const fieldOptionsMap = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-portfolio",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Self-contained project portfolio components for Next.js App Router. Includes ProjectPortfolio, ProjectPortfolioClient (with built-in filtering), ProjectDetail, SimilarProjects, ProjectMenu, ProjectMenuClient, and GalleryCarousel. Pass a clientSlug and apiBase — done.",
5
5
  "keywords": ["nextjs", "react", "portfolio", "projects", "megamenu", "gallery", "filtering"],
6
6
  "license": "MIT",