project-portfolio 2.2.1 → 2.2.2

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
@@ -586,6 +586,7 @@ With a curated menu:
586
586
  | `clientSlug` | `string` | No* | — | Client slug for direct fetch mode |
587
587
  | `apiBase` | `string` | No* | — | API base URL for direct fetch mode |
588
588
  | `menuId` | `string` | No | — | Slug of a curated menu. Fetches from `/menus/{slug}` for projects. Filters are always shown regardless. |
589
+ | `noCache` | `boolean` | No | `false` | Bypasses the module-level data cache and sets `cache: "no-store"` on all fetch calls. Useful during development or when projects update frequently. |
589
590
  | `basePath` | `string` | Yes | — | Base path for project detail links |
590
591
  | `viewAllPath` | `string` | Yes | — | Path for the "View All Projects" link |
591
592
  | `subtitle` | `string` | No | — | Description shown above the project cards (hidden on mobile) |
@@ -15,10 +15,16 @@ export interface ProjectMenuClientProps {
15
15
  clientSlug?: string;
16
16
  apiBase?: string;
17
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}
18
+ * Optional menu slug to fetch a specific curated menu instead of all projects.
19
+ * When provided, fetches from /api/v1/clients/{clientSlug}/menus/{slug}
20
20
  */
21
21
  menuId?: string;
22
+ /**
23
+ * When true, bypasses the module-level data cache and always fetches fresh data.
24
+ * Also sets fetch cache to "no-store" on all underlying requests.
25
+ * Useful during development or when projects update frequently.
26
+ */
27
+ noCache?: boolean;
22
28
  projects?: Project[];
23
29
  schema?: CustomFieldSchema[];
24
30
  filterOptions?: {
@@ -34,5 +40,5 @@ export interface ProjectMenuClientProps {
34
40
  font?: string;
35
41
  maxProjects?: number;
36
42
  }
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;
43
+ export declare function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, noCache, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp, fieldOptionsMap: fieldOptionsMapProp, subtitle, basePath, viewAllPath, font, maxProjects, }: ProjectMenuClientProps): import("react/jsx-runtime").JSX.Element;
38
44
  //# 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;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"}
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;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,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,OAAe,EACf,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,2CAqfxB"}
@@ -17,25 +17,27 @@ const ACCENT = "oklch(0.78 0.16 85)";
17
17
  const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
18
18
  const DEFAULT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
19
19
  const menuDataCache = new Map();
20
- export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp = "Project Type", fieldOptionsMap: fieldOptionsMapProp = {}, subtitle, basePath, viewAllPath, font = DEFAULT_FONT, maxProjects = 6, }) {
20
+ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, noCache = false, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp = "Project Type", fieldOptionsMap: fieldOptionsMapProp = {}, subtitle, basePath, viewAllPath, font = DEFAULT_FONT, maxProjects = 6, }) {
21
21
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
22
22
  const [filtersOpen, setFiltersOpen] = useState(false);
23
23
  const [hoveredCard, setHoveredCard] = useState(null);
24
24
  const [fetched, setFetched] = useState(null);
25
25
  // Self-fetch mode: fires when dataUrl OR (clientSlug + apiBase) are provided.
26
26
  // Uses a module-level Promise cache so repeated mounts never re-hit the API.
27
+ // Pass noCache=true to bypass the module cache and always fetch fresh data.
27
28
  useEffect(() => {
28
29
  const hasDataUrl = !!dataUrl;
29
30
  const hasDirectFetch = !!(clientSlug && apiBase);
30
31
  if (!hasDataUrl && !hasDirectFetch)
31
32
  return;
32
33
  let cancelled = false;
34
+ const fetchOpts = noCache ? { cache: "no-store" } : {};
33
35
  const cacheKey = dataUrl !== null && dataUrl !== void 0 ? dataUrl : `${clientSlug}:${apiBase}:${menuId !== null && menuId !== void 0 ? menuId : "all"}`;
34
36
  async function fetchAndCache() {
35
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
37
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
36
38
  // dataUrl mode: fetch from local API route (server-cached, no API key exposed)
37
39
  if (dataUrl) {
38
- const res = await fetch(dataUrl);
40
+ const res = await fetch(dataUrl, fetchOpts);
39
41
  const json = res.ok ? await res.json() : {};
40
42
  return {
41
43
  projects: (_a = json.projects) !== null && _a !== void 0 ? _a : [],
@@ -46,24 +48,20 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
46
48
  fieldOptionsMap: (_f = json.fieldOptionsMap) !== null && _f !== void 0 ? _f : {},
47
49
  };
48
50
  }
49
- // Menu endpoint mode: fetch from /menus/{menuId} when menuId is provided
50
- // Also fetch /projects to get schema for filters (menu endpoint may not return it)
51
+ // Menu endpoint mode: /menus/{slug} for projects, /projects for schema+options.
51
52
  if (menuId) {
52
- const [menuRes, projectsRes, fieldsRes] = await Promise.all([
53
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`),
54
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`),
55
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`),
53
+ const [menuRes, projectsRes] = await Promise.all([
54
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`, fetchOpts),
55
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts),
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 : []).filter((p) => p.is_published !== false);
60
- // Get schema from menu response first, fallback to projects response
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
- const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
59
+ const projects = ((_g = menuJson === null || menuJson === void 0 ? void 0 : menuJson.projects) !== null && _g !== void 0 ? _g : []).filter((p) => p.is_published !== false);
60
+ const schema = (_j = (_h = projectsJson === null || projectsJson === void 0 ? void 0 : projectsJson.client) === null || _h === void 0 ? void 0 : _h.custom_fields_schema) !== null && _j !== void 0 ? _j : [];
63
61
  const fieldOptionsMap = {};
64
- for (const field of ((_p = fieldsJson.fields) !== null && _p !== void 0 ? _p : [])) {
62
+ for (const field of schema) {
65
63
  const map = {};
66
- for (const opt of ((_q = field.options) !== null && _q !== void 0 ? _q : [])) {
64
+ for (const opt of ((_k = field.options) !== null && _k !== void 0 ? _k : [])) {
67
65
  if (typeof opt === "object" && opt.id && opt.label) {
68
66
  map[opt.id] = opt.label;
69
67
  map[opt.label] = opt.label;
@@ -71,9 +69,9 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
71
69
  }
72
70
  fieldOptionsMap[field.key] = map;
73
71
  }
74
- const filterField = (_r = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _r !== void 0 ? _r : null;
72
+ const filterField = (_l = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _l !== void 0 ? _l : null;
75
73
  const filterOptions = filterField
76
- ? ((_s = filterField.options) !== null && _s !== void 0 ? _s : []).map((opt) => {
74
+ ? ((_m = filterField.options) !== null && _m !== void 0 ? _m : []).map((opt) => {
77
75
  var _a, _b;
78
76
  if (typeof opt === "string")
79
77
  return { id: opt.toLowerCase().replace(/\s+/g, "-"), label: opt };
@@ -84,24 +82,20 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
84
82
  projects,
85
83
  schema,
86
84
  filterOptions,
87
- filterFieldKey: (_t = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _t !== void 0 ? _t : null,
88
- filterFieldName: (_u = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _u !== void 0 ? _u : "Project Type",
85
+ filterFieldKey: (_o = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _o !== void 0 ? _o : null,
86
+ filterFieldName: (_p = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _p !== void 0 ? _p : "Project Type",
89
87
  fieldOptionsMap,
90
88
  };
91
89
  }
92
- // Direct fetch mode: fetch from the upstream API directly (all projects)
93
- const [projectsRes, fieldsRes] = await Promise.all([
94
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`),
95
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`),
96
- ]);
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 : []).filter((p) => p.is_published !== false);
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
- const fieldsJson = fieldsRes.ok ? await fieldsRes.json() : { fields: [] };
90
+ // Direct fetch mode single call, /projects returns everything.
91
+ const res = await fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts);
92
+ const json = res.ok ? await res.json() : {};
93
+ const projects = ((_q = json === null || json === void 0 ? void 0 : json.data) !== null && _q !== void 0 ? _q : []).filter((p) => p.is_published !== false);
94
+ const schema = (_s = (_r = json === null || json === void 0 ? void 0 : json.client) === null || _r === void 0 ? void 0 : _r.custom_fields_schema) !== null && _s !== void 0 ? _s : [];
101
95
  const fieldOptionsMap = {};
102
- for (const field of ((_y = fieldsJson.fields) !== null && _y !== void 0 ? _y : [])) {
96
+ for (const field of schema) {
103
97
  const map = {};
104
- for (const opt of ((_z = field.options) !== null && _z !== void 0 ? _z : [])) {
98
+ for (const opt of ((_t = field.options) !== null && _t !== void 0 ? _t : [])) {
105
99
  if (typeof opt === "object" && opt.id && opt.label) {
106
100
  map[opt.id] = opt.label;
107
101
  map[opt.label] = opt.label;
@@ -109,9 +103,9 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
109
103
  }
110
104
  fieldOptionsMap[field.key] = map;
111
105
  }
112
- const filterField = (_0 = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _0 !== void 0 ? _0 : null;
106
+ const filterField = (_u = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _u !== void 0 ? _u : null;
113
107
  const filterOptions = filterField
114
- ? ((_1 = filterField.options) !== null && _1 !== void 0 ? _1 : []).map((opt) => {
108
+ ? ((_v = filterField.options) !== null && _v !== void 0 ? _v : []).map((opt) => {
115
109
  var _a, _b;
116
110
  if (typeof opt === "string")
117
111
  return { id: opt.toLowerCase().replace(/\s+/g, "-"), label: opt };
@@ -122,23 +116,26 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, projec
122
116
  projects,
123
117
  schema,
124
118
  filterOptions,
125
- filterFieldKey: (_2 = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _2 !== void 0 ? _2 : null,
126
- filterFieldName: (_3 = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _3 !== void 0 ? _3 : "Project Type",
119
+ filterFieldKey: (_w = filterField === null || filterField === void 0 ? void 0 : filterField.key) !== null && _w !== void 0 ? _w : null,
120
+ filterFieldName: (_x = filterField === null || filterField === void 0 ? void 0 : filterField.name) !== null && _x !== void 0 ? _x : "Project Type",
127
121
  fieldOptionsMap,
128
122
  };
129
123
  }
130
- // Store the Promise on first call reuse it on every subsequent mount
131
- if (!menuDataCache.has(cacheKey)) {
132
- menuDataCache.set(cacheKey, fetchAndCache());
133
- }
134
- menuDataCache.get(cacheKey).then((data) => {
124
+ // When noCache=true, skip the module cache entirely and always fetch fresh.
125
+ // Otherwise store the Promise on first call and reuse it on every subsequent mount.
126
+ const getOrFetch = noCache
127
+ ? fetchAndCache()
128
+ : (menuDataCache.has(cacheKey)
129
+ ? menuDataCache.get(cacheKey)
130
+ : (() => { const p = fetchAndCache(); menuDataCache.set(cacheKey, p); return p; })());
131
+ getOrFetch.then((data) => {
135
132
  if (!cancelled)
136
133
  setFetched(data);
137
134
  }).catch(() => {
138
135
  // silently fail — render nothing
139
136
  });
140
137
  return () => { cancelled = true; };
141
- }, [dataUrl, clientSlug, apiBase, menuId]);
138
+ }, [dataUrl, clientSlug, apiBase, menuId, noCache]);
142
139
  // Resolve data: prefer self-fetched, fall back to props
143
140
  const projects = (_b = (_a = fetched === null || fetched === void 0 ? void 0 : fetched.projects) !== null && _a !== void 0 ? _a : projectsProp) !== null && _b !== void 0 ? _b : [];
144
141
  const schema = (_d = (_c = fetched === null || fetched === void 0 ? void 0 : fetched.schema) !== null && _c !== void 0 ? _c : schemaProp) !== null && _d !== void 0 ? _d : [];
@@ -1 +1 @@
1
- {"version":3,"file":"SimilarProjects.d.ts","sourceRoot":"","sources":["../src/SimilarProjects.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AA0DD,wBAAsB,eAAe,CAAC,EACpC,OAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,QAAY,EACZ,UAAe,EACf,OAAgB,EAChB,YAAY,GACb,EAAE,oBAAoB,2DA+JtB"}
1
+ {"version":3,"file":"SimilarProjects.d.ts","sourceRoot":"","sources":["../src/SimilarProjects.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AA0DD,wBAAsB,eAAe,CAAC,EACpC,OAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,QAAY,EACZ,UAAe,EACf,OAAgB,EAChB,YAAY,GACb,EAAE,oBAAoB,2DAiKtB"}
@@ -100,11 +100,13 @@ export async function SimilarProjects({ filters = {}, excludeSlug, clientSlug, a
100
100
  }
101
101
  // ── List variant (default) ────────────────────────────────────────────────────
102
102
  return (_jsxs("section", { style: { borderTop: "1px solid #e4e4e7", maxWidth: "1280px", margin: "0 auto", padding: "3rem 1rem 2rem", boxSizing: "border-box", fontFamily: font }, children: [sharedStyles, header, _jsx("div", { className: "chisel-similar-grid", children: similar.map((p) => {
103
- var _a, _b, _c, _d, _e;
103
+ var _a, _b, _c, _d, _e, _f, _g;
104
104
  const imgUrl = (_d = (_a = p.image_url) !== null && _a !== void 0 ? _a : (_c = (_b = p.media) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url) !== null && _d !== void 0 ? _d : null;
105
- const badge = badgeField
105
+ const badgeRaw = badgeField
106
106
  ? ((_e = parseMultiValue(p.custom_field_values[badgeField.key])[0]) !== null && _e !== void 0 ? _e : null)
107
107
  : null;
108
+ const badgeOptMap = badgeField ? ((_f = fieldOptionsMap[badgeField.key]) !== null && _f !== void 0 ? _f : {}) : {};
109
+ const badge = badgeRaw ? ((_g = badgeOptMap[badgeRaw]) !== null && _g !== void 0 ? _g : badgeRaw) : null;
108
110
  const loc = locationField
109
111
  ? p.custom_field_values[locationField.key]
110
112
  : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-portfolio",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
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",