project-portfolio 2.0.0 → 2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"GalleryCarousel.d.ts","sourceRoot":"","sources":["../src/GalleryCarousel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpC,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,YAAY,GACb,EAAE;IACD,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;CACrB,kDAwJA"}
1
+ {"version":3,"file":"GalleryCarousel.d.ts","sourceRoot":"","sources":["../src/GalleryCarousel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpC,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,KAAK,EAAE,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,YAAY,GACb,EAAE,oBAAoB,kDAwJtB"}
@@ -65,7 +65,7 @@ export function GalleryCarousel({ images, projectTitle, }) {
65
65
  margin: 0,
66
66
  padding: "0 1rem",
67
67
  fontFamily: font,
68
- }, children: caption })), total > 1 && (_jsx("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: images.map((img, i) => {
68
+ }, children: caption })), total > 1 && (_jsx("div", { style: { display: "flex", gap: "8px", overflowX: "auto", WebkitOverflowScrolling: "touch", paddingBottom: "4px" }, children: images.map((img, i) => {
69
69
  var _a;
70
70
  return (_jsx("button", { onClick: () => setCurrent(i), "aria-label": `View image ${i + 1}`, style: {
71
71
  width: "72px",
@@ -83,7 +83,7 @@ export async function ProjectDetail({ slug, clientSlug, apiBase, backPath = "/pr
83
83
  return isNaN(n) ? String(raw) : String(Math.round(n));
84
84
  })();
85
85
  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 1.5rem 2.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: `
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
87
  .chisel-stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; }
88
88
  @media (min-width: 768px) { .chisel-stats-grid { grid-template-columns: repeat(4, 1fr); gap: 2rem; } }
89
89
  .chisel-gallery-placeholder { display: grid; grid-template-columns: 1fr; gap: 12px; }
@@ -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,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"}
@@ -26,27 +26,41 @@ 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
+ const projects = menuJson
55
+ ? ((_b = (_a = menuJson.projects) !== null && _a !== void 0 ? _a : menuJson.data) !== null && _b !== void 0 ? _b : [])
56
+ : ((_c = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.data) !== null && _c !== void 0 ? _c : []);
57
+ // Always get schema from /projects response
58
+ 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
59
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
46
60
  const fieldOptionsMap = {};
47
- for (const field of ((_d = fieldsJson.fields) !== null && _d !== void 0 ? _d : [])) {
61
+ for (const field of ((_f = fieldsJson.fields) !== null && _f !== void 0 ? _f : [])) {
48
62
  const map = {};
49
- for (const opt of ((_e = field.options) !== null && _e !== void 0 ? _e : [])) {
63
+ for (const opt of ((_g = field.options) !== null && _g !== void 0 ? _g : [])) {
50
64
  if (typeof opt === "object" && opt.id && opt.label) {
51
65
  map[opt.id] = opt.label;
52
66
  map[opt.label] = opt.label;
@@ -54,9 +68,9 @@ export function createMenuHandler({ clientSlug, apiBase, revalidate = 86400, })
54
68
  }
55
69
  fieldOptionsMap[field.key] = map;
56
70
  }
57
- const filterField = (_f = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _f !== void 0 ? _f : null;
71
+ const filterField = (_h = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _h !== void 0 ? _h : null;
58
72
  const filterOptions = filterField
59
- ? ((_g = filterField.options) !== null && _g !== void 0 ? _g : []).map((opt) => {
73
+ ? ((_j = filterField.options) !== null && _j !== void 0 ? _j : []).map((opt) => {
60
74
  var _a, _b;
61
75
  if (typeof opt === "string")
62
76
  return { id: opt.toLowerCase().replace(/\s+/g, "-"), label: opt };
@@ -67,19 +81,19 @@ export function createMenuHandler({ clientSlug, apiBase, revalidate = 86400, })
67
81
  projects,
68
82
  schema,
69
83
  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",
84
+ filterFieldKey: (_k = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _k !== void 0 ? _k : null,
85
+ filterFieldName: (_l = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _l !== void 0 ? _l : "Project Type",
72
86
  fieldOptionsMap,
73
87
  });
74
88
  }
75
- catch (_k) {
89
+ catch (_m) {
76
90
  return Response.json({ projects: [], schema: [], filterOptions: [], filterFieldKey: null, filterFieldName: "Project Type", fieldOptionsMap: {} }, { status: 500 });
77
91
  }
78
92
  };
79
93
  }
80
- export async function fetchProjectMenuData({ apiBase, clientSlug, revalidate = 86400, noCache = false, }) {
94
+ export async function fetchProjectMenuData({ apiBase, clientSlug, menuId, revalidate = 86400, noCache = false, }) {
81
95
  var _a, _b, _c, _d;
82
- const raw = await _fetchMenuData(apiBase, clientSlug, revalidate, noCache);
96
+ const raw = await _fetchMenuData(apiBase, clientSlug, menuId, revalidate, noCache);
83
97
  const filterField = (_a = raw.schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _a !== void 0 ? _a : null;
84
98
  const filterOptions = filterField
85
99
  ? ((_b = filterField.options) !== null && _b !== void 0 ? _b : []).map((opt) => {
@@ -99,27 +113,40 @@ export async function fetchProjectMenuData({ apiBase, clientSlug, revalidate = 8
99
113
  fieldOptionsMap: raw.fieldOptionsMap,
100
114
  };
101
115
  }
102
- const _fetchMenuData = cache(async (apiBase, clientSlug, revalidate, noCache = false) => {
103
- var _a, _b, _c, _d, _e;
116
+ const _fetchMenuData = cache(async (apiBase, clientSlug, menuId, revalidate, noCache = false) => {
117
+ var _a, _b, _c, _d, _e, _f, _g;
104
118
  const fetchOpts = noCache
105
119
  ? { cache: "no-store" }
106
120
  : { next: { revalidate } };
107
121
  try {
108
- const [res, fieldsRes] = await Promise.all([
122
+ // Always fetch /projects for schema, and /fields for options
123
+ // If menuId is provided, also fetch the menu endpoint for curated projects
124
+ const fetches = [
109
125
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts),
110
126
  fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`, fetchOpts),
111
- ]);
112
- if (!res.ok)
127
+ ];
128
+ if (menuId) {
129
+ fetches.push(fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`, fetchOpts));
130
+ }
131
+ const responses = await Promise.all(fetches);
132
+ const [projectsRes, fieldsRes] = responses;
133
+ const menuRes = menuId ? responses[2] : null;
134
+ if (!projectsRes.ok)
113
135
  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 : [];
136
+ const projectsJson = await projectsRes.json();
137
+ const menuJson = menuRes && menuRes.ok ? await menuRes.json() : null;
138
+ // If menuId provided, use menu projects; otherwise use all projects
139
+ const projects = menuJson
140
+ ? ((_b = (_a = menuJson.projects) !== null && _a !== void 0 ? _a : menuJson.data) !== null && _b !== void 0 ? _b : [])
141
+ : ((_c = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.data) !== null && _c !== void 0 ? _c : []);
142
+ // Always get schema from /projects response
143
+ 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
144
  // Build fieldOptionsMap: { fieldKey: { id: label, label: label } }
118
145
  const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
119
146
  const fieldOptionsMap = {};
120
- for (const field of ((_d = fieldsJson.fields) !== null && _d !== void 0 ? _d : [])) {
147
+ for (const field of ((_f = fieldsJson.fields) !== null && _f !== void 0 ? _f : [])) {
121
148
  const map = {};
122
- for (const opt of ((_e = field.options) !== null && _e !== void 0 ? _e : [])) {
149
+ for (const opt of ((_g = field.options) !== null && _g !== void 0 ? _g : [])) {
123
150
  if (typeof opt === "object" && opt.id && opt.label) {
124
151
  map[opt.id] = opt.label;
125
152
  map[opt.label] = opt.label;
@@ -129,13 +156,13 @@ const _fetchMenuData = cache(async (apiBase, clientSlug, revalidate, noCache = f
129
156
  }
130
157
  return { projects, schema, fieldOptionsMap };
131
158
  }
132
- catch (_f) {
159
+ catch (_h) {
133
160
  return { projects: [], schema: [], fieldOptionsMap: {} };
134
161
  }
135
162
  });
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, }) {
163
+ 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
164
  var _a, _b, _c, _d;
138
- const { projects, schema, fieldOptionsMap } = await _fetchMenuData(apiBase, clientSlug, revalidate, noCache);
165
+ const { projects, schema, fieldOptionsMap } = await _fetchMenuData(apiBase, clientSlug, menuId, revalidate, noCache);
139
166
  // Find the filterable select field (badge_overlay is our category field)
140
167
  const filterField = (_a = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _a !== void 0 ? _a : null;
141
168
  // 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,2CAscxB"}
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"}