@chiselandco/nexus 2.2.7 → 2.5.1

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.
Files changed (42) hide show
  1. package/README.md +274 -286
  2. package/dist/FilterSidebar.d.ts +20 -0
  3. package/dist/FilterSidebar.d.ts.map +1 -0
  4. package/dist/FilterSidebar.js +266 -0
  5. package/dist/FilteredPortfolio.d.ts +45 -0
  6. package/dist/FilteredPortfolio.d.ts.map +1 -0
  7. package/dist/FilteredPortfolio.js +134 -0
  8. package/dist/GalleryCarousel.d.ts +10 -2
  9. package/dist/GalleryCarousel.d.ts.map +1 -1
  10. package/dist/GalleryCarousel.js +363 -63
  11. package/dist/ProjectCard.d.ts +2 -1
  12. package/dist/ProjectCard.d.ts.map +1 -1
  13. package/dist/ProjectDetail.d.ts +3 -1
  14. package/dist/ProjectDetail.d.ts.map +1 -1
  15. package/dist/ProjectDetail.js +34 -18
  16. package/dist/ProjectMenu.d.ts +8 -3
  17. package/dist/ProjectMenu.d.ts.map +1 -1
  18. package/dist/ProjectMenu.js +12 -16
  19. package/dist/ProjectMenuClient.d.ts +5 -2
  20. package/dist/ProjectMenuClient.d.ts.map +1 -1
  21. package/dist/ProjectMenuClient.js +6 -7
  22. package/dist/ProjectPortfolio.d.ts +3 -1
  23. package/dist/ProjectPortfolio.d.ts.map +1 -1
  24. package/dist/ProjectPortfolio.js +4 -4
  25. package/dist/ProjectPortfolioClient.d.ts +4 -1
  26. package/dist/ProjectPortfolioClient.d.ts.map +1 -1
  27. package/dist/ProjectPortfolioClient.js +4 -6
  28. package/dist/SimilarProjects.d.ts +3 -1
  29. package/dist/SimilarProjects.d.ts.map +1 -1
  30. package/dist/SimilarProjects.js +11 -9
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -0
  34. package/dist/types.d.ts +2 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/package.json +18 -4
  37. package/dist/ProjectFilters.d.ts +0 -11
  38. package/dist/ProjectFilters.d.ts.map +0 -1
  39. package/dist/ProjectFilters.js +0 -49
  40. package/dist/ProjectGrid.d.ts +0 -10
  41. package/dist/ProjectGrid.d.ts.map +0 -1
  42. package/dist/ProjectGrid.js +0 -8
@@ -4,6 +4,8 @@ export interface ProjectMenuProps {
4
4
  clientSlug: string;
5
5
  /** API base URL e.g. "https://nexus.chiselandco.com" */
6
6
  apiBase: string;
7
+ /** Client API key — pass via environment variable, never hardcode */
8
+ apiKey: string;
7
9
  /**
8
10
  * Optional menu ID to fetch a specific curated menu instead of all projects.
9
11
  * When provided, fetches from /api/v1/clients/{clientSlug}/menus/{menuId}
@@ -51,16 +53,19 @@ export interface ProjectMenuProps {
51
53
  * import { revalidateTag } from "next/cache"
52
54
  * revalidateTag("chisel-menu-my-client")
53
55
  */
54
- export declare function createMenuHandler({ clientSlug, apiBase, menuId, revalidate, }: {
56
+ export declare function createMenuHandler({ clientSlug, apiBase, apiKey, menuId, revalidate, }: {
55
57
  clientSlug: string;
56
58
  apiBase: string;
59
+ /** Client API key — pass via environment variable, never hardcode */
60
+ apiKey: string;
57
61
  /** Optional menu ID to fetch a specific curated menu instead of all projects */
58
62
  menuId?: string;
59
63
  revalidate?: number;
60
64
  }): (request: Request) => Promise<Response>;
61
- export declare function fetchProjectMenuData({ apiBase, clientSlug, menuId, revalidate, noCache, }: {
65
+ export declare function fetchProjectMenuData({ apiBase, clientSlug, apiKey, menuId, revalidate, noCache, }: {
62
66
  apiBase: string;
63
67
  clientSlug: string;
68
+ apiKey: string;
64
69
  menuId?: string;
65
70
  revalidate?: number;
66
71
  noCache?: boolean;
@@ -75,5 +80,5 @@ export declare function fetchProjectMenuData({ apiBase, clientSlug, menuId, reva
75
80
  filterFieldName: string;
76
81
  fieldOptionsMap: Record<string, Record<string, string>>;
77
82
  }>;
78
- export declare function ProjectMenu({ clientSlug, apiBase, menuId, basePath, viewAllPath, subtitle, font, maxProjects, revalidate, noCache, }: ProjectMenuProps): Promise<import("react/jsx-runtime").JSX.Element>;
83
+ export declare function ProjectMenu({ clientSlug, apiBase, apiKey, menuId, basePath, viewAllPath, subtitle, font, maxProjects, revalidate, noCache, }: ProjectMenuProps): Promise<import("react").JSX.Element>;
79
84
  //# 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;;;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,uBAmE3C;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;AA0DD,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;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd;;;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,MAAM,EACN,UAAkB,GACnB,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,IAC2B,SAAS,OAAO,uBAmE3C;AAED,wBAAsB,oBAAoB,CAAC,EACzC,OAAO,EACP,UAAU,EACV,MAAM,EACN,MAAM,EACN,UAAkB,EAClB,OAAe,GAChB,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,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;AAwDD,wBAAsB,WAAW,CAAC,EAChC,UAAU,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,QAAsB,EACtB,WAAW,EACX,QAAQ,EACR,IAA0E,EAC1E,WAAe,EACf,UAAkB,EAClB,OAAe,GAChB,EAAE,gBAAgB,wCAiClB"}
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cache } from "react";
3
3
  import { ProjectMenuClient } from "./ProjectMenuClient";
4
- const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
5
4
  /**
6
5
  * createMenuHandler
7
6
  *
@@ -26,7 +25,7 @@ const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
26
25
  * import { revalidateTag } from "next/cache"
27
26
  * revalidateTag("chisel-menu-my-client")
28
27
  */
29
- export function createMenuHandler({ clientSlug, apiBase, menuId, revalidate = 86400, }) {
28
+ export function createMenuHandler({ clientSlug, apiBase, apiKey, menuId, revalidate = 86400, }) {
30
29
  return async function GET(request) {
31
30
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
32
31
  const bypass = process.env.CHISEL_CACHE_BYPASS === "true" ||
@@ -41,10 +40,10 @@ export function createMenuHandler({ clientSlug, apiBase, menuId, revalidate = 86
41
40
  // Without menuId: 1 call — /projects for everything.
42
41
  const fetches = menuId
43
42
  ? [
44
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`, fetchOpts),
45
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts),
43
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${apiKey}`, fetchOpts),
44
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts),
46
45
  ]
47
- : [fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts)];
46
+ : [fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts)];
48
47
  const [primaryRes, schemaRes] = await Promise.all(fetches);
49
48
  const primaryJson = primaryRes.ok ? await primaryRes.json() : {};
50
49
  const schemaJson = schemaRes ? (schemaRes.ok ? await schemaRes.json() : {}) : primaryJson;
@@ -87,9 +86,9 @@ export function createMenuHandler({ clientSlug, apiBase, menuId, revalidate = 86
87
86
  }
88
87
  };
89
88
  }
90
- export async function fetchProjectMenuData({ apiBase, clientSlug, menuId, revalidate = 86400, noCache = false, }) {
89
+ export async function fetchProjectMenuData({ apiBase, clientSlug, apiKey, menuId, revalidate = 86400, noCache = false, }) {
91
90
  var _a, _b, _c, _d;
92
- const raw = await _fetchMenuData(apiBase, clientSlug, menuId, revalidate, noCache);
91
+ const raw = await _fetchMenuData(apiBase, clientSlug, apiKey, menuId, revalidate, noCache);
93
92
  const filterField = (_a = raw.schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _a !== void 0 ? _a : null;
94
93
  const filterOptions = filterField
95
94
  ? ((_b = filterField.options) !== null && _b !== void 0 ? _b : []).map((opt) => {
@@ -109,21 +108,18 @@ export async function fetchProjectMenuData({ apiBase, clientSlug, menuId, revali
109
108
  fieldOptionsMap: raw.fieldOptionsMap,
110
109
  };
111
110
  }
112
- const _fetchMenuData = cache(async (apiBase, clientSlug, menuId, revalidate, noCache = false) => {
111
+ const _fetchMenuData = cache(async (apiBase, clientSlug, apiKey, menuId, revalidate, noCache = false) => {
113
112
  var _a, _b, _c, _d, _e;
114
113
  const fetchOpts = noCache
115
114
  ? { cache: "no-store" }
116
115
  : { next: { revalidate } };
117
116
  try {
118
- // /projects always returns schema + options. Use it in both cases.
119
- // When menuId provided: 2 calls — /menus/{slug} for projects, /projects for schema.
120
- // Without menuId: 1 call — /projects for everything.
121
117
  const fetches = menuId
122
118
  ? [
123
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${API_KEY}`, fetchOpts),
124
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts),
119
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${apiKey}`, fetchOpts),
120
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts),
125
121
  ]
126
- : [fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts)];
122
+ : [fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts)];
127
123
  const [primaryRes, schemaRes] = await Promise.all(fetches);
128
124
  if (!primaryRes.ok)
129
125
  return { projects: [], schema: [], fieldOptionsMap: {} };
@@ -151,9 +147,9 @@ const _fetchMenuData = cache(async (apiBase, clientSlug, menuId, revalidate, noC
151
147
  return { projects: [], schema: [], fieldOptionsMap: {} };
152
148
  }
153
149
  });
154
- 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, }) {
150
+ export async function ProjectMenu({ clientSlug, apiBase, apiKey, menuId, basePath = "/projects", viewAllPath, subtitle, font = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", maxProjects = 6, revalidate = 86400, noCache = false, }) {
155
151
  var _a, _b, _c, _d;
156
- const { projects, schema, fieldOptionsMap } = await _fetchMenuData(apiBase, clientSlug, menuId, revalidate, noCache);
152
+ const { projects, schema, fieldOptionsMap } = await _fetchMenuData(apiBase, clientSlug, apiKey, menuId, revalidate, noCache);
157
153
  // Find the filterable select field (badge_overlay is our category field)
158
154
  const filterField = (_a = schema.find((f) => f.is_filterable && (f.type === "select" || f.type === "multi-select"))) !== null && _a !== void 0 ? _a : null;
159
155
  // Build filter option list from schema
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type { Project, CustomFieldSchema } from "./types";
2
3
  export interface ProjectMenuClientProps {
3
4
  /**
@@ -8,12 +9,14 @@ export interface ProjectMenuClientProps {
8
9
  */
9
10
  dataUrl?: string;
10
11
  /**
11
- * Pass clientSlug + apiBase to have the component fetch its own data directly.
12
+ * Pass clientSlug + apiBase + apiKey to have the component fetch its own data directly.
12
13
  * Use this for quick setup or non-Next.js environments.
13
14
  * For production Next.js apps, prefer dataUrl + createMenuHandler instead.
14
15
  */
15
16
  clientSlug?: string;
16
17
  apiBase?: string;
18
+ /** Client API key — required when using clientSlug/apiBase direct-fetch mode */
19
+ apiKey?: string;
17
20
  /**
18
21
  * Optional menu slug to fetch a specific curated menu instead of all projects.
19
22
  * When provided, fetches from /api/v1/clients/{clientSlug}/menus/{slug}
@@ -40,5 +43,5 @@ export interface ProjectMenuClientProps {
40
43
  font?: string;
41
44
  maxProjects?: number;
42
45
  }
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;
46
+ export declare function ProjectMenuClient({ dataUrl, clientSlug, apiBase, apiKey, menuId, noCache, projects: projectsProp, schema: schemaProp, filterOptions: filterOptionsProp, filterFieldKey: filterFieldKeyProp, filterFieldName: filterFieldNameProp, fieldOptionsMap: fieldOptionsMapProp, subtitle, basePath, viewAllPath, font, maxProjects, }: ProjectMenuClientProps): React.JSX.Element;
44
47
  //# 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;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"}
1
+ {"version":3,"file":"ProjectMenuClient.d.ts","sourceRoot":"","sources":["../src/ProjectMenuClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAClD,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,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;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;AAkBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,UAAU,EACV,OAAO,EACP,MAAM,EACN,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,qBAqfxB"}
@@ -14,10 +14,9 @@ function parseMultiValue(raw) {
14
14
  return [];
15
15
  }
16
16
  const ACCENT = "oklch(0.78 0.16 85)";
17
- const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
18
17
  const DEFAULT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
19
18
  const menuDataCache = new Map();
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, }) {
19
+ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, apiKey, 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
20
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
22
21
  const [filtersOpen, setFiltersOpen] = useState(false);
23
22
  const [hoveredCard, setHoveredCard] = useState(null);
@@ -32,7 +31,7 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, noCach
32
31
  return;
33
32
  let cancelled = false;
34
33
  const fetchOpts = noCache ? { cache: "no-store" } : {};
35
- const cacheKey = dataUrl !== null && dataUrl !== void 0 ? dataUrl : `${clientSlug}:${apiBase}:${menuId !== null && menuId !== void 0 ? menuId : "all"}`;
34
+ const cacheKey = dataUrl !== null && dataUrl !== void 0 ? dataUrl : `${clientSlug}:${apiBase}:${apiKey}:${menuId !== null && menuId !== void 0 ? menuId : "all"}`;
36
35
  async function fetchAndCache() {
37
36
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
38
37
  // dataUrl mode: fetch from local API route (server-cached, no API key exposed)
@@ -51,8 +50,8 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, noCach
51
50
  // Menu endpoint mode: /menus/{slug} for projects, /projects for schema+options.
52
51
  if (menuId) {
53
52
  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),
53
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/menus/${menuId}?api_key=${apiKey}`, fetchOpts),
54
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts),
56
55
  ]);
57
56
  const menuJson = menuRes.ok ? await menuRes.json() : {};
58
57
  const projectsJson = projectsRes.ok ? await projectsRes.json() : {};
@@ -88,7 +87,7 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, noCach
88
87
  };
89
88
  }
90
89
  // Direct fetch mode — single call, /projects returns everything.
91
- const res = await fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts);
90
+ const res = await fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts);
92
91
  const json = res.ok ? await res.json() : {};
93
92
  const projects = ((_q = json === null || json === void 0 ? void 0 : json.data) !== null && _q !== void 0 ? _q : []).filter((p) => p.is_published !== false);
94
93
  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 : [];
@@ -135,7 +134,7 @@ export function ProjectMenuClient({ dataUrl, clientSlug, apiBase, menuId, noCach
135
134
  // silently fail — render nothing
136
135
  });
137
136
  return () => { cancelled = true; };
138
- }, [dataUrl, clientSlug, apiBase, menuId, noCache]);
137
+ }, [dataUrl, clientSlug, apiBase, apiKey, menuId, noCache]);
139
138
  // Resolve data: prefer self-fetched, fall back to props
140
139
  const projects = (_b = (_a = fetched === null || fetched === void 0 ? void 0 : fetched.projects) !== null && _a !== void 0 ? _a : projectsProp) !== null && _b !== void 0 ? _b : [];
141
140
  const schema = (_d = (_c = fetched === null || fetched === void 0 ? void 0 : fetched.schema) !== null && _c !== void 0 ? _c : schemaProp) !== null && _d !== void 0 ? _d : [];
@@ -3,6 +3,8 @@ export interface ProjectPortfolioProps {
3
3
  clientSlug: string;
4
4
  /** Base URL of the projects API */
5
5
  apiBase: string;
6
+ /** Client API key — pass via environment variable, never hardcode */
7
+ apiKey: string;
6
8
  /** Base path for project detail links. Defaults to "/projects" */
7
9
  basePath?: string;
8
10
  /**
@@ -38,5 +40,5 @@ export interface ProjectPortfolioProps {
38
40
  * apiBase="https://your-api.com"
39
41
  * />
40
42
  */
41
- export declare function ProjectPortfolio({ clientSlug, apiBase, basePath, searchParams, revalidate, noCache, }: ProjectPortfolioProps): Promise<import("react/jsx-runtime").JSX.Element>;
43
+ export declare function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath, searchParams, revalidate, noCache, }: ProjectPortfolioProps): Promise<import("react").JSX.Element>;
42
44
  //# sourceMappingURL=ProjectPortfolio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectPortfolio.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolio.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAyED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,YAAiB,EACjB,UAAe,EACf,OAAe,GAChB,EAAE,qBAAqB,oDA+HvB"}
1
+ {"version":3,"file":"ProjectPortfolio.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolio.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AA0ED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,YAAiB,EACjB,UAAe,EACf,OAAe,GAChB,EAAE,qBAAqB,wCA+HvB"}
@@ -6,14 +6,14 @@ import { ProjectCard } from "./ProjectCard";
6
6
  // If this component appears twice on one page, the API is only hit once.
7
7
  // 2. next: { revalidate } — Next.js Data Cache, caches across multiple requests
8
8
  // on production deployments. Silently ignored in preview/local.
9
- const fetchPortfolioData = cache(async (apiBase, clientSlug, revalidate, filtersKey = "{}", noCache = false) => {
9
+ const fetchPortfolioData = cache(async (apiBase, clientSlug, apiKey, revalidate, filtersKey = "{}", noCache = false) => {
10
10
  var _a, _b, _c, _d, _e, _f;
11
11
  const fetchOpts = noCache
12
12
  ? { cache: "no-store" }
13
13
  : { next: { revalidate } };
14
14
  // Build URL with filter[key]=value as required by the API
15
15
  const filters = JSON.parse(filtersKey);
16
- const params = new URLSearchParams({ api_key: "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR" });
16
+ const params = new URLSearchParams({ api_key: apiKey });
17
17
  Object.entries(filters).forEach(([key, val]) => {
18
18
  if (val)
19
19
  params.append(`filter[${key}]`, val);
@@ -65,7 +65,7 @@ const fetchPortfolioData = cache(async (apiBase, clientSlug, revalidate, filters
65
65
  * apiBase="https://your-api.com"
66
66
  * />
67
67
  */
68
- export async function ProjectPortfolio({ clientSlug, apiBase, basePath = "/projects", searchParams = {}, revalidate = 60, noCache = false, }) {
68
+ export async function ProjectPortfolio({ clientSlug, apiBase, apiKey, basePath = "/projects", searchParams = {}, revalidate = 60, noCache = false, }) {
69
69
  // Parse filter[key]=value from searchParams into { key: value }
70
70
  const filters = {};
71
71
  Object.entries(searchParams).forEach(([key, val]) => {
@@ -76,7 +76,7 @@ export async function ProjectPortfolio({ clientSlug, apiBase, basePath = "/proje
76
76
  filters[match[1]] = Array.isArray(val) ? val[0] : val;
77
77
  });
78
78
  const filtersKey = JSON.stringify(filters);
79
- const { projects, schema, fieldOptionsMap } = await fetchPortfolioData(apiBase, clientSlug, revalidate, filtersKey, noCache);
79
+ const { projects, schema, fieldOptionsMap } = await fetchPortfolioData(apiBase, clientSlug, apiKey, revalidate, filtersKey, noCache);
80
80
  const hasFilters = Object.keys(filters).length > 0;
81
81
  const activeFilterLabels = Object.entries(filters)
82
82
  .map(([key, val]) => {
@@ -1,8 +1,11 @@
1
+ import React from "react";
1
2
  export interface ProjectPortfolioClientProps {
2
3
  /** Client slug identifying which client's projects to load */
3
4
  clientSlug: string;
4
5
  /** Base URL of the projects API */
5
6
  apiBase: string;
7
+ /** Client API key — pass via environment variable, never hardcode */
8
+ apiKey: string;
6
9
  /** Base path for project detail links. Defaults to "/projects" */
7
10
  basePath?: string;
8
11
  /**
@@ -17,5 +20,5 @@ export interface ProjectPortfolioClientProps {
17
20
  /** Max columns in the grid. 2 or 3. Defaults to 3 */
18
21
  columns?: 2 | 3;
19
22
  }
20
- export declare function ProjectPortfolioClient({ clientSlug, apiBase, basePath, filters, font, columns, }: ProjectPortfolioClientProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath, filters, font, columns, }: ProjectPortfolioClientProps): React.JSX.Element;
21
24
  //# sourceMappingURL=ProjectPortfolioClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectPortfolioClient.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolioClient.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CAChB;AA0DD,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,OAAY,EACZ,IAAmB,EACnB,OAAW,GACZ,EAAE,2BAA2B,2CAoJ7B"}
1
+ {"version":3,"file":"ProjectPortfolioClient.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolioClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAuC,MAAM,OAAO,CAAA;AAM3D,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;CAChB;AAyDD,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,OAAY,EACZ,IAAmB,EACnB,OAAW,GACZ,EAAE,2BAA2B,qBAmJ7B"}
@@ -3,7 +3,6 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useEffect, useMemo } from "react";
4
4
  import { ProjectCard } from "./ProjectCard";
5
5
  const portfolioDataCache = new Map();
6
- const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
7
6
  const DEFAULT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
8
7
  // ─── Helpers ─────────────────────────────────────────────────────────────────
9
8
  function parseMultiValue(raw) {
@@ -40,16 +39,15 @@ function matchesFilters(project, filters, schema, fieldOptionsMap) {
40
39
  });
41
40
  }
42
41
  // ─── Component ───────────────────────────────────────────────────────────────
43
- export function ProjectPortfolioClient({ clientSlug, apiBase, basePath = "/projects", filters = {}, font = DEFAULT_FONT, columns = 3, }) {
42
+ export function ProjectPortfolioClient({ clientSlug, apiBase, apiKey, basePath = "/projects", filters = {}, font = DEFAULT_FONT, columns = 3, }) {
44
43
  const [data, setData] = useState(null);
45
- // Self-fetch on mount — uses module-level cache so the API is only called once per page load
46
44
  useEffect(() => {
47
- const cacheKey = `${clientSlug}:${apiBase}`;
45
+ const cacheKey = `${clientSlug}:${apiBase}:${apiKey}`;
48
46
  async function fetchAndCache() {
49
47
  var _a, _b, _c, _d, _e;
50
48
  const [projectsRes, fieldsRes] = await Promise.all([
51
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`),
52
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${API_KEY}`),
49
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`),
50
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/fields?api_key=${apiKey}`),
53
51
  ]);
54
52
  const json = projectsRes.ok ? await projectsRes.json() : {};
55
53
  const projects = ((_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : []).filter((p) => p.is_published !== false);
@@ -14,6 +14,8 @@ export interface SimilarProjectsProps {
14
14
  clientSlug: string;
15
15
  /** Base URL of the projects API */
16
16
  apiBase: string;
17
+ /** Client API key — pass via environment variable, never hardcode */
18
+ apiKey: string;
17
19
  /** Base path used to build individual project detail URLs e.g. "/projects" */
18
20
  basePath?: string;
19
21
  /** Maximum number of projects to show. Defaults to 3 */
@@ -42,5 +44,5 @@ export interface SimilarProjectsProps {
42
44
  /** Small label above the heading. Defaults to "More Work" */
43
45
  subtitle?: string;
44
46
  }
45
- export declare function SimilarProjects({ filters, excludeSlug, clientSlug, apiBase, basePath, maxItems, revalidate, noCache, variant, projectSlugs, title, subtitle, }: SimilarProjectsProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
47
+ export declare function SimilarProjects({ filters, excludeSlug, clientSlug, apiBase, apiKey, basePath, maxItems, revalidate, noCache, variant, projectSlugs, title, subtitle, }: SimilarProjectsProps): Promise<import("react").JSX.Element | null>;
46
48
  //# sourceMappingURL=SimilarProjects.d.ts.map
@@ -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;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AA+DD,wBAAsB,eAAe,CAAC,EACpC,OAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,QAAsB,EACtB,QAAY,EACZ,UAAe,EACf,OAAe,EACf,OAAgB,EAChB,YAAY,EACZ,KAA0B,EAC1B,QAAsB,GACvB,EAAE,oBAAoB,2DAiKtB"}
1
+ {"version":3,"file":"SimilarProjects.d.ts","sourceRoot":"","sources":["../src/SimilarProjects.tsx"],"names":[],"mappings":"AAKA,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,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AA6DD,wBAAsB,eAAe,CAAC,EACpC,OAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAsB,EACtB,QAAY,EACZ,UAAe,EACf,OAAe,EACf,OAAgB,EAChB,YAAY,EACZ,KAA0B,EAC1B,QAAsB,GACvB,EAAE,oBAAoB,+CAoKtB"}
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cache } from "react";
3
2
  import { ProjectCard } from "./ProjectCard";
4
3
  // ─── Helpers ─────────────────────────────────────────────────────────────────
5
4
  function parseMultiValue(raw) {
@@ -19,17 +18,16 @@ function dedupeByKey(arr) {
19
18
  });
20
19
  }
21
20
  // ─── Data fetching ────────────────────────────────────────────────────────────
22
- const fetchSimilarData = cache(async (apiBase, clientSlug, revalidate, noCache) => {
21
+ async function fetchSimilarData(apiBase, clientSlug, apiKey, revalidate, noCache) {
23
22
  var _a, _b, _c, _d;
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
24
  const fetchOpts = noCache
25
25
  ? { cache: "no-store" }
26
26
  : revalidate > 0
27
27
  ? { next: { revalidate } }
28
28
  : {};
29
- const API_KEY = "pk_live_crmsuTIm7NNfb9uEWBCyv88F6kj2YQUR";
30
29
  try {
31
- // Single call /projects returns projects AND client.custom_fields_schema with full options.
32
- const res = await fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${API_KEY}`, fetchOpts);
30
+ const res = await fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects?api_key=${apiKey}`, fetchOpts);
33
31
  const json = res.ok ? await res.json() : null;
34
32
  const allProjects = ((_a = json === null || json === void 0 ? void 0 : json.data) !== null && _a !== void 0 ? _a : []).filter((p) => p.is_published !== false);
35
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 : []);
@@ -49,10 +47,10 @@ const fetchSimilarData = cache(async (apiBase, clientSlug, revalidate, noCache)
49
47
  catch (_e) {
50
48
  return { allProjects: [], schema: [], fieldOptionsMap: {} };
51
49
  }
52
- });
50
+ }
53
51
  // ─── Component ────────────────────────────────────────────────────────────────
54
- export async function SimilarProjects({ filters = {}, excludeSlug, clientSlug, apiBase, basePath = "/projects", maxItems = 3, revalidate = 60, noCache = false, variant = "list", projectSlugs, title = "Similar Projects", subtitle = "More Work", }) {
55
- const { allProjects, schema, fieldOptionsMap } = await fetchSimilarData(apiBase, clientSlug, revalidate, noCache);
52
+ export async function SimilarProjects({ filters = {}, excludeSlug, clientSlug, apiBase, apiKey, basePath = "/projects", maxItems = 3, revalidate = 60, noCache = false, variant = "list", projectSlugs, title = "Similar Projects", subtitle = "More Work", }) {
53
+ const { allProjects, schema, fieldOptionsMap } = await fetchSimilarData(apiBase, clientSlug, apiKey, revalidate, noCache);
56
54
  const badgeField = schema.find((f) => f.display_position === "badge_overlay");
57
55
  const locationField = schema.find((f) => f.type === "location");
58
56
  const font = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
@@ -67,12 +65,16 @@ export async function SimilarProjects({ filters = {}, excludeSlug, clientSlug, a
67
65
  .slice(0, maxItems);
68
66
  }
69
67
  else {
70
- // Filter mode — match by custom field values
68
+ // Filter mode — match by custom field values.
69
+ // clientSlug guard is the hard wall: only projects belonging to this exact client
70
+ // are ever eligible regardless of what the API returns.
71
71
  const filterEntries = Object.entries(filters);
72
72
  similar = allProjects
73
73
  .filter((p) => {
74
74
  if (excludeSlug && p.slug === excludeSlug)
75
75
  return false;
76
+ if (filterEntries.length === 0)
77
+ return true;
76
78
  return filterEntries.every(([key, value]) => {
77
79
  const fieldValues = parseMultiValue(p.custom_field_values[key]);
78
80
  return fieldValues.some((v) => v.toLowerCase() === value.toLowerCase());
package/dist/index.d.ts CHANGED
@@ -14,5 +14,9 @@ export { ProjectMenuClient } from "./ProjectMenuClient";
14
14
  export type { ProjectMenuClientProps } from "./ProjectMenuClient";
15
15
  export { ProjectCard } from "./ProjectCard";
16
16
  export type { CardVariant } from "./ProjectCard";
17
+ export { FilterSidebar } from "./FilterSidebar";
18
+ export type { FilterSidebarProps } from "./FilterSidebar";
19
+ export { FilteredPortfolio } from "./FilteredPortfolio";
20
+ export type { FilteredPortfolioProps } from "./FilteredPortfolio";
17
21
  export type { Project, CustomFieldSchema, CustomFieldValue, LocationValue, Media, } from "./types";
18
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACpF,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,KAAK,GACN,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAA;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACpF,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AACjE,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,KAAK,GACN,MAAM,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -6,3 +6,5 @@ export { GalleryCarousel } from "./GalleryCarousel";
6
6
  export { ProjectMenu, fetchProjectMenuData, createMenuHandler } from "./ProjectMenu";
7
7
  export { ProjectMenuClient } from "./ProjectMenuClient";
8
8
  export { ProjectCard } from "./ProjectCard";
9
+ export { FilterSidebar } from "./FilterSidebar";
10
+ export { FilteredPortfolio } from "./FilteredPortfolio";
package/dist/types.d.ts CHANGED
@@ -22,6 +22,8 @@ export interface Media {
22
22
  description?: string | null;
23
23
  is_primary: boolean;
24
24
  sort_order: number;
25
+ /** Per-image custom field values — keys are field keys, values are slugs or arrays of slugs */
26
+ custom_field_values?: Record<string, string | string[] | null>;
25
27
  }
26
28
  export interface LocationValue {
27
29
  city?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;CACpE;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,GAAG,IAAI,CAAA;AAEhF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,CAAA;IAChE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;CACpE;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,+FAA+F;IAC/F,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;CAC/D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,GAAG,IAAI,CAAA;AAEhF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf"}
package/package.json CHANGED
@@ -1,8 +1,18 @@
1
1
  {
2
2
  "name": "@chiselandco/nexus",
3
- "version": "2.2.7",
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
- "keywords": ["nextjs", "react", "portfolio", "projects", "megamenu", "gallery", "filtering", "chiselandco", "nexus"],
3
+ "version": "2.5.1",
4
+ "description": "Self-contained project portfolio components for Next.js App Router. Includes ProjectPortfolio, ProjectPortfolioClient, ProjectDetail, SimilarProjects, ProjectMenu, ProjectMenuClient, GalleryCarousel, FilterSidebar, and FilteredPortfolio. Pass a clientSlug and apiBase — done.",
5
+ "keywords": [
6
+ "nextjs",
7
+ "react",
8
+ "portfolio",
9
+ "projects",
10
+ "megamenu",
11
+ "gallery",
12
+ "filtering",
13
+ "chiselandco",
14
+ "nexus"
15
+ ],
6
16
  "license": "MIT",
7
17
  "type": "module",
8
18
  "main": "./dist/index.js",
@@ -14,7 +24,9 @@
14
24
  "types": "./dist/index.d.ts"
15
25
  }
16
26
  },
17
- "files": ["dist"],
27
+ "files": [
28
+ "dist"
29
+ ],
18
30
  "scripts": {
19
31
  "build": "tsc",
20
32
  "prepublishOnly": "npm run build"
@@ -25,8 +37,10 @@
25
37
  "react-dom": ">=18.0.0"
26
38
  },
27
39
  "devDependencies": {
40
+ "@types/node": "^25.9.3",
28
41
  "@types/react": "^18.0.0",
29
42
  "@types/react-dom": "^18.0.0",
43
+ "next": "^15.0.0",
30
44
  "typescript": "^5.0.0"
31
45
  }
32
46
  }
@@ -1,11 +0,0 @@
1
- import type { CustomFieldSchema } from "./types";
2
- interface ProjectFiltersProps {
3
- fields: CustomFieldSchema[];
4
- filters: Record<string, string>;
5
- onFilterChange: (key: string, value: string) => void;
6
- onClearFilters: () => void;
7
- hasActiveFilters: boolean;
8
- }
9
- export declare function ProjectFilters({ fields, filters, onFilterChange, onClearFilters, hasActiveFilters, }: ProjectFiltersProps): import("react/jsx-runtime").JSX.Element;
10
- export {};
11
- //# sourceMappingURL=ProjectFilters.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ProjectFilters.d.ts","sourceRoot":"","sources":["../src/ProjectFilters.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEhD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpD,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,OAAO,EACP,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,EAAE,mBAAmB,2CAgGrB"}
@@ -1,49 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export function ProjectFilters({ fields, filters, onFilterChange, onClearFilters, hasActiveFilters, }) {
3
- const deduped = fields.filter((f, i, arr) => arr.findIndex((x) => x.key === f.key) === i);
4
- return (_jsxs("div", { style: {
5
- marginBottom: "2rem",
6
- paddingBottom: "2rem",
7
- borderBottom: "1px solid #e4e4e7",
8
- display: "flex",
9
- flexWrap: "wrap",
10
- alignItems: "flex-end",
11
- gap: "1rem",
12
- }, children: [deduped.map((field, fieldIndex) => {
13
- if (field.type === "select" || field.type === "multi-select") {
14
- return (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: [_jsx("label", { style: { fontSize: "13px", fontWeight: 500, color: "#71717a" }, children: field.name }), _jsxs("select", { value: filters[field.key] || "all", onChange: (e) => onFilterChange(field.key, e.target.value === "all" ? "" : e.target.value), style: {
15
- width: "180px",
16
- padding: "8px 12px",
17
- border: "1px solid #e4e4e7",
18
- borderRadius: "6px",
19
- fontSize: "14px",
20
- backgroundColor: "#fff",
21
- color: "#18181b",
22
- outline: "none",
23
- cursor: "pointer",
24
- }, children: [_jsxs("option", { value: "all", children: ["All ", field.name] }), [...new Set(field.options.map((o) => typeof o === "string" ? o : o.label))].map((option, i) => (_jsx("option", { value: option, children: option }, `${field.key}-opt-${i}`)))] })] }, `filter-${fieldIndex}-${field.key}`));
25
- }
26
- if (field.type === "text" || field.type === "location") {
27
- return (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: [_jsx("label", { style: { fontSize: "13px", fontWeight: 500, color: "#71717a" }, children: field.name }), _jsx("input", { type: "text", placeholder: `Search ${field.name.toLowerCase()}...`, value: filters[field.key] || "", onChange: (e) => onFilterChange(field.key, e.target.value), style: {
28
- width: "180px",
29
- padding: "8px 12px",
30
- border: "1px solid #e4e4e7",
31
- borderRadius: "6px",
32
- fontSize: "14px",
33
- color: "#18181b",
34
- outline: "none",
35
- } })] }, `filter-${fieldIndex}-${field.key}`));
36
- }
37
- return null;
38
- }), hasActiveFilters && (_jsx("button", { onClick: onClearFilters, style: {
39
- display: "flex",
40
- alignItems: "center",
41
- gap: "4px",
42
- padding: "8px 12px",
43
- background: "none",
44
- border: "none",
45
- fontSize: "14px",
46
- color: "#71717a",
47
- cursor: "pointer",
48
- }, children: "\u00D7 Clear filters" }))] }));
49
- }
@@ -1,10 +0,0 @@
1
- import type { Project, CustomFieldSchema } from "./types";
2
- interface ProjectGridProps {
3
- projects: Project[];
4
- schema: CustomFieldSchema[];
5
- /** Base path for project detail links. Defaults to "/projects" */
6
- basePath?: string;
7
- }
8
- export declare function ProjectGrid({ projects, schema, basePath }: ProjectGridProps): import("react/jsx-runtime").JSX.Element;
9
- export {};
10
- //# sourceMappingURL=ProjectGrid.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ProjectGrid.d.ts","sourceRoot":"","sources":["../src/ProjectGrid.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEzD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAsB,EAAE,EAAE,gBAAgB,2CAsBzF"}
@@ -1,8 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ProjectCard } from "./ProjectCard";
3
- export function ProjectGrid({ projects, schema, basePath = "/projects" }) {
4
- if (projects.length === 0) {
5
- return (_jsx("div", { style: { textAlign: "center", padding: "4rem 0" }, children: _jsx("p", { style: { color: "#71717a" }, children: "No projects found." }) }));
6
- }
7
- return (_jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))", gap: "2rem" }, children: projects.map((project, index) => (_jsx(ProjectCard, { project: project, schema: schema, priority: index === 0, basePath: basePath }, project.id))) }));
8
- }