project-portfolio 1.0.1 → 1.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectGrid.d.ts","sourceRoot":"","sources":["../src/ProjectGrid.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAA;AAE3E,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,2CA6HzF"}
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,64 +1,8 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useMemo } from "react";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
4
2
  import { ProjectCard } from "./ProjectCard";
5
- import { ProjectFilters } from "./ProjectFilters";
6
3
  export function ProjectGrid({ projects, schema, basePath = "/projects" }) {
7
- const [filters, setFilters] = useState({});
8
- const [variant, setVariant] = useState("card");
9
- const filterableFields = useMemo(() => schema.filter((f) => f.is_filterable).sort((a, b) => a.sort_order - b.sort_order), [schema]);
10
- const filteredProjects = useMemo(() => {
11
- return projects.filter((project) => Object.entries(filters).every(([key, filterValue]) => {
12
- if (!filterValue)
13
- return true;
14
- const field = schema.find((f) => f.key === key);
15
- if (!field)
16
- return true;
17
- const projectValue = project.custom_field_values[key];
18
- const parseVal = (v) => {
19
- if (Array.isArray(v))
20
- return v.map(String).map((s) => s.replace(/`/g, "").trim());
21
- if (typeof v === "string")
22
- return v.replace(/`/g, "").split(",").map((s) => s.trim()).filter(Boolean);
23
- return [String(v !== null && v !== void 0 ? v : "")];
24
- };
25
- if (field.type === "multi-select")
26
- return parseVal(projectValue).includes(filterValue);
27
- if (field.type === "select")
28
- return parseVal(projectValue)[0] === filterValue;
29
- if (field.type === "text")
30
- return String(projectValue || "").toLowerCase().includes(filterValue.toLowerCase());
31
- if (field.type === "location") {
32
- const loc = projectValue;
33
- if (!loc)
34
- return false;
35
- return `${loc.city || ""} ${loc.state || ""}`.toLowerCase().includes(filterValue.toLowerCase());
36
- }
37
- return true;
38
- }));
39
- }, [projects, filters, schema]);
40
- const handleFilterChange = (key, value) => setFilters((prev) => (Object.assign(Object.assign({}, prev), { [key]: value })));
41
- const clearFilters = () => setFilters({});
42
- const hasActiveFilters = Object.values(filters).some((v) => v);
43
- return (_jsxs("div", { children: [filterableFields.length > 0 && (_jsx(ProjectFilters, { fields: filterableFields, filters: filters, onFilterChange: handleFilterChange, onClearFilters: clearFilters, hasActiveFilters: hasActiveFilters })), _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "1.5rem" }, children: [_jsxs("p", { style: { fontSize: "14px", color: "#71717a", margin: 0 }, children: [filteredProjects.length, " ", filteredProjects.length === 1 ? "project" : "projects"] }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px", border: "1px solid #e4e4e7", borderRadius: "8px", padding: "4px", backgroundColor: "#fff" }, children: [_jsx("button", { onClick: () => setVariant("card"), "aria-label": "Card view", style: {
44
- padding: "6px",
45
- border: "none",
46
- borderRadius: "6px",
47
- cursor: "pointer",
48
- backgroundColor: variant === "card" ? "#18181b" : "transparent",
49
- color: variant === "card" ? "#fff" : "#a1a1aa",
50
- display: "flex",
51
- alignItems: "center",
52
- transition: "background-color 0.2s",
53
- }, children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "3", y: "3", width: "7", height: "7" }), _jsx("rect", { x: "14", y: "3", width: "7", height: "7" }), _jsx("rect", { x: "3", y: "14", width: "7", height: "7" }), _jsx("rect", { x: "14", y: "14", width: "7", height: "7" })] }) }), _jsx("button", { onClick: () => setVariant("compact"), "aria-label": "Compact view", style: {
54
- padding: "6px",
55
- border: "none",
56
- borderRadius: "6px",
57
- cursor: "pointer",
58
- backgroundColor: variant === "compact" ? "#18181b" : "transparent",
59
- color: variant === "compact" ? "#fff" : "#a1a1aa",
60
- display: "flex",
61
- alignItems: "center",
62
- transition: "background-color 0.2s",
63
- }, children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })] }) })] })] }), filteredProjects.length === 0 ? (_jsxs("div", { style: { textAlign: "center", padding: "4rem 0" }, children: [_jsx("p", { style: { color: "#71717a", fontSize: "18px" }, children: "No projects found matching your filters." }), hasActiveFilters && (_jsx("button", { onClick: clearFilters, style: { marginTop: "1rem", background: "none", border: "none", textDecoration: "underline", cursor: "pointer", fontSize: "14px" }, children: "Clear all filters" }))] })) : variant === "compact" ? (_jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(340px, 1fr))", gap: "12px" }, children: filteredProjects.map((project, index) => (_jsx(ProjectCard, { project: project, schema: schema, priority: index === 0, variant: "compact", basePath: basePath }, project.id))) })) : (_jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))", gap: "2rem" }, children: filteredProjects.map((project, index) => (_jsx(ProjectCard, { project: project, schema: schema, priority: index === 0, variant: "card", basePath: basePath }, project.id))) }))] }));
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))) }));
64
8
  }
@@ -27,9 +27,7 @@ export interface ProjectPortfolioProps {
27
27
  /**
28
28
  * ProjectPortfolio — pure card grid, no chrome.
29
29
  *
30
- * Fetches its own data with two-layer caching:
31
- * - React.cache() deduplicates within a single render (no double-fetch if used twice on one page)
32
- * - next: { revalidate: 60 } caches at the Next.js Data Cache layer for 60s across requests
30
+ * Fetches client and project data directly from the API.
33
31
  *
34
32
  * Usage:
35
33
  * import { ProjectPortfolio } from "chisel-project-portfolio"
@@ -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;CAClB;AAgED;;;;;;;;;;;;;;;;;GAiBG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,QAAsB,GACvB,EAAE,qBAAqB,oDA8BvB"}
1
+ {"version":3,"file":"ProjectPortfolio.d.ts","sourceRoot":"","sources":["../src/ProjectPortfolio.tsx"],"names":[],"mappings":"AAGA,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;CAClB;AAyDD;;;;;;;;;;;;;;;;;GAiBG;AACH;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,UAAU,EACV,OAAO,EACP,QAAsB,GACvB,EAAE,qBAAqB,oDA8BvB"}
@@ -1,15 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cache } from "react";
3
2
  import { ProjectCard } from "./ProjectCard";
4
- // React.cache() deduplicates calls within a single render pass.
5
- // next: { revalidate: 60 } caches the fetch in Next.js Data Cache for 60s.
6
- const fetchPortfolioData = cache(async (apiBase, clientSlug) => {
3
+ const fetchPortfolioData = async (apiBase, clientSlug) => {
7
4
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
8
- // `next` is a Next.js-specific fetch option not present in the DOM RequestInit type.
9
- const nextRevalidateInit = { next: { revalidate: 60 } };
10
5
  const [clientRes, projectsRes] = await Promise.all([
11
- fetch(`${apiBase}/api/v1/clients/${clientSlug}`, nextRevalidateInit),
12
- fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects`, nextRevalidateInit),
6
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}`),
7
+ fetch(`${apiBase}/api/v1/clients/${clientSlug}/projects`),
13
8
  ]);
14
9
  const clientJson = clientRes.ok
15
10
  ? await clientRes.json()
@@ -31,7 +26,7 @@ const fetchPortfolioData = cache(async (apiBase, clientSlug) => {
31
26
  projects: (_j = projectsJson.data) !== null && _j !== void 0 ? _j : [],
32
27
  schema,
33
28
  };
34
- });
29
+ };
35
30
  /**
36
31
  * ProjectPortfolio — self-contained async server component.
37
32
  *
@@ -53,9 +48,7 @@ const fetchPortfolioData = cache(async (apiBase, clientSlug) => {
53
48
  /**
54
49
  * ProjectPortfolio — pure card grid, no chrome.
55
50
  *
56
- * Fetches its own data with two-layer caching:
57
- * - React.cache() deduplicates within a single render (no double-fetch if used twice on one page)
58
- * - next: { revalidate: 60 } caches at the Next.js Data Cache layer for 60s across requests
51
+ * Fetches client and project data directly from the API.
59
52
  *
60
53
  * Usage:
61
54
  * import { ProjectPortfolio } from "chisel-project-portfolio"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-portfolio",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Self-contained project portfolio component for Next.js App Router. Drop in one component, pass a clientSlug and apiBase, done.",
5
5
  "keywords": ["nextjs", "react", "portfolio", "projects"],
6
6
  "license": "MIT",