@burdenoff/microfe-crewfoundry 2026.827.1 → 2026.924.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 (35) hide show
  1. package/dist/crewfoundry/CrewFoundryRoot.d.ts.map +1 -1
  2. package/dist/crewfoundry/CrewFoundryRoot.js +190 -185
  3. package/dist/crewfoundry/CrewFoundryRoot.js.map +1 -1
  4. package/dist/crewfoundry/components/forms/ConfirmDialog.d.ts.map +1 -1
  5. package/dist/crewfoundry/components/forms/ConfirmDialog.js +25 -24
  6. package/dist/crewfoundry/components/forms/ConfirmDialog.js.map +1 -1
  7. package/dist/crewfoundry/components/forms/FormModal.d.ts.map +1 -1
  8. package/dist/crewfoundry/components/forms/FormModal.js +103 -101
  9. package/dist/crewfoundry/components/forms/FormModal.js.map +1 -1
  10. package/dist/crewfoundry/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  11. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +207 -210
  12. package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
  13. package/dist/crewfoundry/pages/HiringCreateJobPagePage.d.ts.map +1 -1
  14. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +0 -4
  15. package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
  16. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.d.ts.map +1 -1
  17. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +0 -4
  18. package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
  19. package/dist/crewfoundry/pages/HiringJobsPagePage.d.ts.map +1 -1
  20. package/dist/crewfoundry/pages/HiringJobsPagePage.js +75 -78
  21. package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
  22. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.d.ts.map +1 -1
  23. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +101 -104
  24. package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
  25. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  26. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +95 -98
  27. package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
  28. package/dist/crewfoundry/pages/TeamsTeamsPagePage.d.ts.map +1 -1
  29. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +101 -104
  30. package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
  31. package/dist/shared/hooks/useTr.d.ts +17 -0
  32. package/dist/shared/hooks/useTr.d.ts.map +1 -0
  33. package/dist/shared/hooks/useTr.js +14 -0
  34. package/dist/shared/hooks/useTr.js.map +1 -0
  35. package/package.json +1 -1
@@ -1,40 +1,37 @@
1
1
  import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
2
- import { getInitials as t } from "../components/viz/lib/format.js";
3
- import { RatingStars as n } from "../components/viz/RatingStars.js";
4
- import { SegmentedTabs as r } from "../components/viz/SegmentedTabs.js";
2
+ import { useTr as t } from "../../shared/hooks/useTr.js";
3
+ import { getInitials as n } from "../components/viz/lib/format.js";
4
+ import { RatingStars as r } from "../components/viz/RatingStars.js";
5
+ import { SegmentedTabs as i } from "../components/viz/SegmentedTabs.js";
5
6
  import "../components/viz/index.js";
6
- import { useEffect as i, useMemo as a, useState as o } from "react";
7
- import { CheckCircle2 as s, Clock3 as c, PauseCircle as l, Search as u, ShoppingBag as d, Star as f, Users as p } from "lucide-react";
8
- import { useNavigate as m } from "react-router";
9
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
- import { Button as _, CardContent as v, CardHeader as y, CardTitle as b, GlassCard as x, GradientText as S, IllustratedEmptyState as C, Input as w, PagePurpose as T, Pagination as E } from "@burdenoff/fe-libs/ui";
11
- import { usePagination as D } from "@burdenoff/fe-libs/shared/hooks";
12
- import { useI18n as O } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
+ import { useEffect as a, useMemo as o, useState as s } from "react";
8
+ import { CheckCircle2 as c, Clock3 as l, PauseCircle as u, Search as d, ShoppingBag as f, Star as p, Users as m } from "lucide-react";
9
+ import { useNavigate as h } from "react-router";
10
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
+ import { Button as v, CardContent as y, CardHeader as b, CardTitle as x, GlassCard as S, GradientText as C, IllustratedEmptyState as w, Input as T, PagePurpose as E, Pagination as D } from "@burdenoff/fe-libs/ui";
12
+ import { usePagination as O } from "@burdenoff/fe-libs/shared/hooks";
13
13
  //#region src/crewfoundry/pages/MarketplaceMarketplacePagePage.tsx
14
14
  function k(e) {
15
15
  switch (e) {
16
16
  case "active": return {
17
17
  label: "Available",
18
- icon: s,
18
+ icon: c,
19
19
  className: "bg-status-success-bg text-status-success-text"
20
20
  };
21
21
  case "pending": return {
22
22
  label: "Pending",
23
- icon: c,
23
+ icon: l,
24
24
  className: "bg-status-warning-bg text-status-warning-text"
25
25
  };
26
26
  default: return {
27
27
  label: "Unavailable",
28
- icon: l,
28
+ icon: u,
29
29
  className: "bg-bg-sunken text-text-secondary"
30
30
  };
31
31
  }
32
32
  }
33
33
  function A() {
34
- let { t: s } = O(), l = m(), { marketplaceServices: A } = e(), j = (e, t) => {
35
- let n = s(e);
36
- return n === e ? t : n;
37
- }, { page: M, pageSize: N, offset: P, setPage: F } = D({ initialPageSize: 9 }), [I, L] = o(""), [R, z] = o("all"), B = a(() => {
34
+ let c = t(), u = h(), { marketplaceServices: A } = e(), { page: j, pageSize: M, offset: N, setPage: P } = O({ initialPageSize: 9 }), [F, I] = s(""), [L, R] = s("all"), z = o(() => {
38
35
  let e = /* @__PURE__ */ new Map();
39
36
  for (let t of A) e.set(t.category, (e.get(t.category) ?? 0) + 1);
40
37
  let t = [{
@@ -48,209 +45,209 @@ function A() {
48
45
  count: r
49
46
  });
50
47
  return t;
51
- }, [A]), V = a(() => {
52
- let e = I.trim().toLowerCase();
53
- return A.filter((t) => R !== "all" && t.category !== R ? !1 : e ? `${t.title} ${t.provider} ${t.category} ${t.skills.join(" ")}`.toLowerCase().includes(e) : !0);
48
+ }, [A]), B = o(() => {
49
+ let e = F.trim().toLowerCase();
50
+ return A.filter((t) => L !== "all" && t.category !== L ? !1 : e ? `${t.title} ${t.provider} ${t.category} ${t.skills.join(" ")}`.toLowerCase().includes(e) : !0);
54
51
  }, [
55
52
  A,
56
- I,
57
- R
53
+ F,
54
+ L
58
55
  ]);
59
- i(() => {
60
- F(1);
56
+ a(() => {
57
+ P(1);
61
58
  }, [
62
- I,
63
- R,
64
- F
59
+ F,
60
+ L,
61
+ P
65
62
  ]);
66
- let H = A.length > 0 ? (A.reduce((e, t) => e + t.rating, 0) / A.length).toFixed(1) : "0.0", U = A.reduce((e, t) => e + t.reviews, 0), W = V.slice(P, P + N), G = (e) => l(`../marketplace/service-details-page?id=${e}`);
67
- return /* @__PURE__ */ g("div", {
63
+ let V = A.length > 0 ? (A.reduce((e, t) => e + t.rating, 0) / A.length).toFixed(1) : "0.0", H = A.reduce((e, t) => e + t.reviews, 0), U = B.slice(N, N + M), W = (e) => u(`../marketplace/service-details-page?id=${e}`);
64
+ return /* @__PURE__ */ _("div", {
68
65
  className: "space-y-6 p-6",
69
66
  children: [
70
- /* @__PURE__ */ g("div", { children: [
71
- /* @__PURE__ */ h("h1", {
67
+ /* @__PURE__ */ _("div", { children: [
68
+ /* @__PURE__ */ g("h1", {
72
69
  className: "text-2xl font-semibold",
73
- children: /* @__PURE__ */ h(S, { children: j("crewfoundry.marketplace.title", "Talent Marketplace") })
70
+ children: /* @__PURE__ */ g(C, { children: c("crewfoundry.marketplace.title", "Talent Marketplace") })
74
71
  }),
75
- /* @__PURE__ */ h("p", {
72
+ /* @__PURE__ */ g("p", {
76
73
  className: "text-sm text-text-secondary",
77
- children: j("crewfoundry.marketplace.subtitle", "Book vetted specialists and services")
74
+ children: c("crewfoundry.marketplace.subtitle", "Book vetted specialists and services")
78
75
  }),
79
- /* @__PURE__ */ h(T, {
76
+ /* @__PURE__ */ g(E, {
80
77
  className: "mt-1",
81
78
  children: "Discover freelancers and services for your projects — search, filter by category, and hire the right talent on demand."
82
79
  })
83
80
  ] }),
84
- /* @__PURE__ */ g("div", {
81
+ /* @__PURE__ */ _("div", {
85
82
  className: "grid gap-4 md:grid-cols-3",
86
83
  children: [
87
- /* @__PURE__ */ g(x, {
84
+ /* @__PURE__ */ _(S, {
88
85
  treatment: "glass",
89
86
  glow: !0,
90
87
  className: "border-border-seam",
91
- children: [/* @__PURE__ */ g(y, {
88
+ children: [/* @__PURE__ */ _(b, {
92
89
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
93
- children: [/* @__PURE__ */ h(b, {
90
+ children: [/* @__PURE__ */ g(x, {
94
91
  className: "text-sm font-medium",
95
- children: j("crewfoundry.marketplace.stats.services", "Services")
96
- }), /* @__PURE__ */ h(d, { className: "h-4 w-4 text-text-secondary" })]
97
- }), /* @__PURE__ */ h(v, { children: /* @__PURE__ */ h("div", {
92
+ children: c("crewfoundry.marketplace.stats.services", "Services")
93
+ }), /* @__PURE__ */ g(f, { className: "h-4 w-4 text-text-secondary" })]
94
+ }), /* @__PURE__ */ g(y, { children: /* @__PURE__ */ g("div", {
98
95
  className: "text-2xl font-bold tabular-nums",
99
96
  children: A.length
100
97
  }) })]
101
98
  }),
102
- /* @__PURE__ */ g(x, {
99
+ /* @__PURE__ */ _(S, {
103
100
  treatment: "metric",
104
101
  className: "border-border-seam",
105
- children: [/* @__PURE__ */ g(y, {
102
+ children: [/* @__PURE__ */ _(b, {
106
103
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
107
- children: [/* @__PURE__ */ h(b, {
104
+ children: [/* @__PURE__ */ g(x, {
108
105
  className: "text-sm font-medium",
109
- children: j("crewfoundry.marketplace.stats.avgRating", "Avg Rating")
110
- }), /* @__PURE__ */ h(f, { className: "h-4 w-4 text-status-warning-text" })]
111
- }), /* @__PURE__ */ h(v, { children: /* @__PURE__ */ h("div", {
106
+ children: c("crewfoundry.marketplace.stats.avgRating", "Avg Rating")
107
+ }), /* @__PURE__ */ g(p, { className: "h-4 w-4 text-status-warning-text" })]
108
+ }), /* @__PURE__ */ g(y, { children: /* @__PURE__ */ g("div", {
112
109
  className: "text-2xl font-bold tabular-nums",
113
- children: H
110
+ children: V
114
111
  }) })]
115
112
  }),
116
- /* @__PURE__ */ g(x, {
113
+ /* @__PURE__ */ _(S, {
117
114
  treatment: "metric",
118
115
  className: "border-border-seam",
119
- children: [/* @__PURE__ */ g(y, {
116
+ children: [/* @__PURE__ */ _(b, {
120
117
  className: "flex flex-row items-center justify-between space-y-0 pb-2",
121
- children: [/* @__PURE__ */ h(b, {
118
+ children: [/* @__PURE__ */ g(x, {
122
119
  className: "text-sm font-medium",
123
- children: j("crewfoundry.marketplace.stats.totalReviews", "Total Reviews")
124
- }), /* @__PURE__ */ h(p, { className: "h-4 w-4 text-text-secondary" })]
125
- }), /* @__PURE__ */ h(v, { children: /* @__PURE__ */ h("div", {
120
+ children: c("crewfoundry.marketplace.stats.totalReviews", "Total Reviews")
121
+ }), /* @__PURE__ */ g(m, { className: "h-4 w-4 text-text-secondary" })]
122
+ }), /* @__PURE__ */ g(y, { children: /* @__PURE__ */ g("div", {
126
123
  className: "text-2xl font-bold tabular-nums",
127
- children: U
124
+ children: H
128
125
  }) })]
129
126
  })
130
127
  ]
131
128
  }),
132
- /* @__PURE__ */ g("div", {
129
+ /* @__PURE__ */ _("div", {
133
130
  className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between",
134
- children: [/* @__PURE__ */ g("div", {
131
+ children: [/* @__PURE__ */ _("div", {
135
132
  className: "relative w-full max-w-sm",
136
- children: [/* @__PURE__ */ h(u, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ h(w, {
133
+ children: [/* @__PURE__ */ g(d, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ g(T, {
137
134
  placeholder: "Search services, providers, skills…",
138
135
  className: "pl-9",
139
- value: I,
140
- onChange: (e) => L(e.target.value),
136
+ value: F,
137
+ onChange: (e) => I(e.target.value),
141
138
  "aria-label": "Search marketplace services"
142
139
  })]
143
- }), /* @__PURE__ */ h("div", {
140
+ }), /* @__PURE__ */ g("div", {
144
141
  className: "-mx-1 overflow-x-auto px-1 pb-1",
145
- children: /* @__PURE__ */ h(r, {
146
- tabs: B,
147
- value: R,
148
- onChange: z,
142
+ children: /* @__PURE__ */ g(i, {
143
+ tabs: z,
144
+ value: L,
145
+ onChange: R,
149
146
  "aria-label": "Filter services by category"
150
147
  })
151
148
  })]
152
149
  }),
153
- W.length === 0 ? /* @__PURE__ */ h(C, {
150
+ U.length === 0 ? /* @__PURE__ */ g(w, {
154
151
  illustration: "empty-search",
155
152
  title: "No services found",
156
153
  description: "Try a different search term or category to find the help you need."
157
- }) : /* @__PURE__ */ h("div", {
154
+ }) : /* @__PURE__ */ g("div", {
158
155
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
159
- children: W.map((e) => {
160
- let r = k(e.status), i = r.icon;
161
- return /* @__PURE__ */ h(x, {
156
+ children: U.map((e) => {
157
+ let t = k(e.status), i = t.icon;
158
+ return /* @__PURE__ */ g(S, {
162
159
  treatment: "glass",
163
160
  role: "button",
164
161
  tabIndex: 0,
165
162
  "aria-label": `View and book ${e.title} by ${e.provider}`,
166
- onClick: () => G(e.id),
163
+ onClick: () => W(e.id),
167
164
  onKeyDown: (t) => {
168
- (t.key === "Enter" || t.key === " ") && (t.preventDefault(), G(e.id));
165
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), W(e.id));
169
166
  },
170
167
  className: "cursor-pointer border-border-seam transition-shadow hover:shadow-elevation-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
171
- children: /* @__PURE__ */ g(v, {
168
+ children: /* @__PURE__ */ _(y, {
172
169
  className: "flex h-full flex-col gap-3 p-4",
173
170
  children: [
174
- /* @__PURE__ */ g("div", {
171
+ /* @__PURE__ */ _("div", {
175
172
  className: "flex items-start justify-between gap-2",
176
- children: [/* @__PURE__ */ g("div", {
173
+ children: [/* @__PURE__ */ _("div", {
177
174
  className: "flex items-center gap-2.5",
178
- children: [/* @__PURE__ */ h("span", {
175
+ children: [/* @__PURE__ */ g("span", {
179
176
  className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold text-action-primary-text",
180
177
  "aria-hidden": "true",
181
- children: t(e.provider)
182
- }), /* @__PURE__ */ g("div", {
178
+ children: n(e.provider)
179
+ }), /* @__PURE__ */ _("div", {
183
180
  className: "min-w-0",
184
- children: [/* @__PURE__ */ h("p", {
181
+ children: [/* @__PURE__ */ g("p", {
185
182
  className: "truncate text-sm font-semibold",
186
183
  children: e.provider
187
- }), /* @__PURE__ */ h("span", {
184
+ }), /* @__PURE__ */ g("span", {
188
185
  className: "inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary",
189
186
  children: e.category
190
187
  })]
191
188
  })]
192
- }), /* @__PURE__ */ g("div", {
189
+ }), /* @__PURE__ */ _("div", {
193
190
  className: "shrink-0 text-right",
194
- children: [/* @__PURE__ */ g("p", {
191
+ children: [/* @__PURE__ */ _("p", {
195
192
  className: "text-base font-bold tabular-nums",
196
193
  children: ["$", e.price.toLocaleString()]
197
- }), /* @__PURE__ */ g("p", {
194
+ }), /* @__PURE__ */ _("p", {
198
195
  className: "text-[10px] text-text-secondary",
199
196
  children: ["/", e.priceType]
200
197
  })]
201
198
  })]
202
199
  }),
203
- /* @__PURE__ */ h("p", {
200
+ /* @__PURE__ */ g("p", {
204
201
  className: "text-sm font-medium",
205
202
  children: e.title
206
203
  }),
207
- /* @__PURE__ */ g("div", {
204
+ /* @__PURE__ */ _("div", {
208
205
  className: "flex flex-wrap items-center gap-2",
209
- children: [/* @__PURE__ */ h(n, {
206
+ children: [/* @__PURE__ */ g(r, {
210
207
  value: e.rating,
211
208
  count: e.reviews,
212
209
  showValue: !0,
213
210
  size: 14
214
- }), /* @__PURE__ */ g("span", {
215
- className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${r.className}`,
216
- children: [/* @__PURE__ */ h(i, { className: "h-3 w-3" }), r.label]
211
+ }), /* @__PURE__ */ _("span", {
212
+ className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${t.className}`,
213
+ children: [/* @__PURE__ */ g(i, { className: "h-3 w-3" }), t.label]
217
214
  })]
218
215
  }),
219
- /* @__PURE__ */ h("p", {
216
+ /* @__PURE__ */ g("p", {
220
217
  className: "line-clamp-2 text-xs text-text-secondary",
221
218
  children: e.description
222
219
  }),
223
- /* @__PURE__ */ h("div", {
220
+ /* @__PURE__ */ g("div", {
224
221
  className: "flex flex-wrap gap-1",
225
- children: e.skills.slice(0, 4).map((e) => /* @__PURE__ */ h("span", {
222
+ children: e.skills.slice(0, 4).map((e) => /* @__PURE__ */ g("span", {
226
223
  className: "rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary",
227
224
  children: e
228
225
  }, e))
229
226
  }),
230
- /* @__PURE__ */ h("div", {
227
+ /* @__PURE__ */ g("div", {
231
228
  className: "mt-auto flex items-center justify-between pt-1 text-[11px] text-text-secondary",
232
- children: /* @__PURE__ */ g("span", {
229
+ children: /* @__PURE__ */ _("span", {
233
230
  className: "flex items-center gap-1",
234
- children: [/* @__PURE__ */ h(c, { className: "h-3 w-3" }), e.deliveryTime]
231
+ children: [/* @__PURE__ */ g(l, { className: "h-3 w-3" }), e.deliveryTime]
235
232
  })
236
233
  }),
237
- /* @__PURE__ */ h(_, {
234
+ /* @__PURE__ */ g(v, {
238
235
  className: "w-full",
239
236
  onClick: (t) => {
240
- t.stopPropagation(), G(e.id);
237
+ t.stopPropagation(), W(e.id);
241
238
  },
242
- children: j("crewfoundry.marketplace.card.book", "View & Book")
239
+ children: c("crewfoundry.marketplace.card.book", "View & Book")
243
240
  })
244
241
  ]
245
242
  })
246
243
  }, e.id);
247
244
  })
248
245
  }),
249
- V.length > N ? /* @__PURE__ */ h(E, {
250
- page: M,
251
- pageSize: N,
252
- totalItems: V.length,
253
- onPageChange: F
246
+ B.length > M ? /* @__PURE__ */ g(D, {
247
+ page: j,
248
+ pageSize: M,
249
+ totalItems: B.length,
250
+ onPageChange: P
254
251
  }) : null
255
252
  ]
256
253
  });
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceMarketplacePagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMarketplacePagePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport {\n Button,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { CheckCircle2, Clock3, PauseCircle, Search, ShoppingBag, Star, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { useCrewFoundryStore, type MarketplaceService } from '../store/crewfoundryStore';\nimport { RatingStars, SegmentedTabs, getInitials, type SegmentedTab } from '../components/viz';\n\n/** Honest availability derived from the real service `status` enum (never \"Booked\"). */\nfunction availabilityOf(status: MarketplaceService['status']): {\n label: string;\n icon: typeof CheckCircle2;\n className: string;\n} {\n switch (status) {\n case 'active':\n return {\n label: 'Available',\n icon: CheckCircle2,\n className: 'bg-status-success-bg text-status-success-text',\n };\n case 'pending':\n return {\n label: 'Pending',\n icon: Clock3,\n className: 'bg-status-warning-bg text-status-warning-text',\n };\n default:\n return {\n label: 'Unavailable',\n icon: PauseCircle,\n className: 'bg-bg-sunken text-text-secondary',\n };\n }\n}\n\nexport function MarketplaceMarketplacePagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { marketplaceServices } = useCrewFoundryStore();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 9 });\n const [search, setSearch] = useState('');\n const [category, setCategory] = useState('all');\n\n const categoryTabs = useMemo<SegmentedTab[]>(() => {\n const counts = new Map<string, number>();\n for (const svc of marketplaceServices) {\n counts.set(svc.category, (counts.get(svc.category) ?? 0) + 1);\n }\n const tabs: SegmentedTab[] = [{ id: 'all', label: 'All', count: marketplaceServices.length }];\n for (const [name, count] of [...counts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {\n tabs.push({ id: name, label: name, count });\n }\n return tabs;\n }, [marketplaceServices]);\n\n const filtered = useMemo(() => {\n const query = search.trim().toLowerCase();\n return marketplaceServices.filter((svc) => {\n if (category !== 'all' && svc.category !== category) {\n return false;\n }\n if (!query) {\n return true;\n }\n return `${svc.title} ${svc.provider} ${svc.category} ${svc.skills.join(' ')}`\n .toLowerCase()\n .includes(query);\n });\n }, [marketplaceServices, search, category]);\n\n useEffect(() => {\n setPage(1);\n }, [search, category, setPage]);\n\n const avgRating =\n marketplaceServices.length > 0\n ? (\n marketplaceServices.reduce((s, m) => s + m.rating, 0) / marketplaceServices.length\n ).toFixed(1)\n : '0.0';\n const totalReviews = marketplaceServices.reduce((s, m) => s + m.reviews, 0);\n const pagedServices = filtered.slice(offset, offset + pageSize);\n const openService = (id: string) => navigate(`../marketplace/service-details-page?id=${id}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{t('crewfoundry.marketplace.title', 'Talent Marketplace')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t('crewfoundry.marketplace.subtitle', 'Book vetted specialists and services')}\n </p>\n <PagePurpose className=\"mt-1\">\n Discover freelancers and services for your projects — search, filter by category, and hire\n the right talent on demand.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {t('crewfoundry.marketplace.stats.services', 'Services')}\n </CardTitle>\n <ShoppingBag className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{marketplaceServices.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {t('crewfoundry.marketplace.stats.avgRating', 'Avg Rating')}\n </CardTitle>\n <Star className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{avgRating}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {t('crewfoundry.marketplace.stats.totalReviews', 'Total Reviews')}\n </CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalReviews}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"relative w-full max-w-sm\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search services, providers, skills…\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n aria-label=\"Search marketplace services\"\n />\n </div>\n <div className=\"-mx-1 overflow-x-auto px-1 pb-1\">\n <SegmentedTabs\n tabs={categoryTabs}\n value={category}\n onChange={setCategory}\n aria-label=\"Filter services by category\"\n />\n </div>\n </div>\n\n {pagedServices.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No services found\"\n description=\"Try a different search term or category to find the help you need.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {pagedServices.map((svc) => {\n const availability = availabilityOf(svc.status);\n const AvailabilityIcon = availability.icon;\n return (\n <GlassCard\n key={svc.id}\n treatment=\"glass\"\n role=\"button\"\n tabIndex={0}\n aria-label={`View and book ${svc.title} by ${svc.provider}`}\n onClick={() => openService(svc.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n openService(svc.id);\n }\n }}\n className=\"cursor-pointer border-border-seam transition-shadow hover:shadow-elevation-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <CardContent className=\"flex h-full flex-col gap-3 p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-2.5\">\n <span\n className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold text-action-primary-text\"\n aria-hidden=\"true\"\n >\n {getInitials(svc.provider)}\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold\">{svc.provider}</p>\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary\">\n {svc.category}\n </span>\n </div>\n </div>\n <div className=\"shrink-0 text-right\">\n <p className=\"text-base font-bold tabular-nums\">\n ${svc.price.toLocaleString()}\n </p>\n <p className=\"text-[10px] text-text-secondary\">/{svc.priceType}</p>\n </div>\n </div>\n\n <p className=\"text-sm font-medium\">{svc.title}</p>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <RatingStars value={svc.rating} count={svc.reviews} showValue size={14} />\n <span\n className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${availability.className}`}\n >\n <AvailabilityIcon className=\"h-3 w-3\" />\n {availability.label}\n </span>\n </div>\n\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{svc.description}</p>\n\n <div className=\"flex flex-wrap gap-1\">\n {svc.skills.slice(0, 4).map((skill) => (\n <span\n key={skill}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary\"\n >\n {skill}\n </span>\n ))}\n </div>\n\n <div className=\"mt-auto flex items-center justify-between pt-1 text-[11px] text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Clock3 className=\"h-3 w-3\" />\n {svc.deliveryTime}\n </span>\n </div>\n\n <Button\n className=\"w-full\"\n onClick={(event) => {\n event.stopPropagation();\n openService(svc.id);\n }}\n >\n {t('crewfoundry.marketplace.card.book', 'View & Book')}\n </Button>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {filtered.length > pageSize ? (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarketplaceMarketplacePagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMarketplacePagePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport {\n Button,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { CheckCircle2, Clock3, PauseCircle, Search, ShoppingBag, Star, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport { useCrewFoundryStore, type MarketplaceService } from '../store/crewfoundryStore';\nimport { RatingStars, SegmentedTabs, getInitials, type SegmentedTab } from '../components/viz';\n\n/** Honest availability derived from the real service `status` enum (never \"Booked\"). */\nfunction availabilityOf(status: MarketplaceService['status']): {\n label: string;\n icon: typeof CheckCircle2;\n className: string;\n} {\n switch (status) {\n case 'active':\n return {\n label: 'Available',\n icon: CheckCircle2,\n className: 'bg-status-success-bg text-status-success-text',\n };\n case 'pending':\n return {\n label: 'Pending',\n icon: Clock3,\n className: 'bg-status-warning-bg text-status-warning-text',\n };\n default:\n return {\n label: 'Unavailable',\n icon: PauseCircle,\n className: 'bg-bg-sunken text-text-secondary',\n };\n }\n}\n\nexport function MarketplaceMarketplacePagePage() {\n const tr = useTr();\n const navigate = useNavigate();\n const { marketplaceServices } = useCrewFoundryStore();\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 9 });\n const [search, setSearch] = useState('');\n const [category, setCategory] = useState('all');\n\n const categoryTabs = useMemo<SegmentedTab[]>(() => {\n const counts = new Map<string, number>();\n for (const svc of marketplaceServices) {\n counts.set(svc.category, (counts.get(svc.category) ?? 0) + 1);\n }\n const tabs: SegmentedTab[] = [{ id: 'all', label: 'All', count: marketplaceServices.length }];\n for (const [name, count] of [...counts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {\n tabs.push({ id: name, label: name, count });\n }\n return tabs;\n }, [marketplaceServices]);\n\n const filtered = useMemo(() => {\n const query = search.trim().toLowerCase();\n return marketplaceServices.filter((svc) => {\n if (category !== 'all' && svc.category !== category) {\n return false;\n }\n if (!query) {\n return true;\n }\n return `${svc.title} ${svc.provider} ${svc.category} ${svc.skills.join(' ')}`\n .toLowerCase()\n .includes(query);\n });\n }, [marketplaceServices, search, category]);\n\n useEffect(() => {\n setPage(1);\n }, [search, category, setPage]);\n\n const avgRating =\n marketplaceServices.length > 0\n ? (\n marketplaceServices.reduce((s, m) => s + m.rating, 0) / marketplaceServices.length\n ).toFixed(1)\n : '0.0';\n const totalReviews = marketplaceServices.reduce((s, m) => s + m.reviews, 0);\n const pagedServices = filtered.slice(offset, offset + pageSize);\n const openService = (id: string) => navigate(`../marketplace/service-details-page?id=${id}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{tr('crewfoundry.marketplace.title', 'Talent Marketplace')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('crewfoundry.marketplace.subtitle', 'Book vetted specialists and services')}\n </p>\n <PagePurpose className=\"mt-1\">\n Discover freelancers and services for your projects — search, filter by category, and hire\n the right talent on demand.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {tr('crewfoundry.marketplace.stats.services', 'Services')}\n </CardTitle>\n <ShoppingBag className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{marketplaceServices.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {tr('crewfoundry.marketplace.stats.avgRating', 'Avg Rating')}\n </CardTitle>\n <Star className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{avgRating}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {tr('crewfoundry.marketplace.stats.totalReviews', 'Total Reviews')}\n </CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{totalReviews}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"relative w-full max-w-sm\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search services, providers, skills…\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n aria-label=\"Search marketplace services\"\n />\n </div>\n <div className=\"-mx-1 overflow-x-auto px-1 pb-1\">\n <SegmentedTabs\n tabs={categoryTabs}\n value={category}\n onChange={setCategory}\n aria-label=\"Filter services by category\"\n />\n </div>\n </div>\n\n {pagedServices.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No services found\"\n description=\"Try a different search term or category to find the help you need.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {pagedServices.map((svc) => {\n const availability = availabilityOf(svc.status);\n const AvailabilityIcon = availability.icon;\n return (\n <GlassCard\n key={svc.id}\n treatment=\"glass\"\n role=\"button\"\n tabIndex={0}\n aria-label={`View and book ${svc.title} by ${svc.provider}`}\n onClick={() => openService(svc.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n openService(svc.id);\n }\n }}\n className=\"cursor-pointer border-border-seam transition-shadow hover:shadow-elevation-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <CardContent className=\"flex h-full flex-col gap-3 p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-center gap-2.5\">\n <span\n className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold text-action-primary-text\"\n aria-hidden=\"true\"\n >\n {getInitials(svc.provider)}\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold\">{svc.provider}</p>\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary\">\n {svc.category}\n </span>\n </div>\n </div>\n <div className=\"shrink-0 text-right\">\n <p className=\"text-base font-bold tabular-nums\">\n ${svc.price.toLocaleString()}\n </p>\n <p className=\"text-[10px] text-text-secondary\">/{svc.priceType}</p>\n </div>\n </div>\n\n <p className=\"text-sm font-medium\">{svc.title}</p>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <RatingStars value={svc.rating} count={svc.reviews} showValue size={14} />\n <span\n className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${availability.className}`}\n >\n <AvailabilityIcon className=\"h-3 w-3\" />\n {availability.label}\n </span>\n </div>\n\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{svc.description}</p>\n\n <div className=\"flex flex-wrap gap-1\">\n {svc.skills.slice(0, 4).map((skill) => (\n <span\n key={skill}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] text-text-secondary\"\n >\n {skill}\n </span>\n ))}\n </div>\n\n <div className=\"mt-auto flex items-center justify-between pt-1 text-[11px] text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Clock3 className=\"h-3 w-3\" />\n {svc.deliveryTime}\n </span>\n </div>\n\n <Button\n className=\"w-full\"\n onClick={(event) => {\n event.stopPropagation();\n openService(svc.id);\n }}\n >\n {tr('crewfoundry.marketplace.card.book', 'View & Book')}\n </Button>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {filtered.length > pageSize ? (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsProjectsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/ProjectsProjectsPagePage.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,wBAAwB,gCAiTvC"}
1
+ {"version":3,"file":"ProjectsProjectsPagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/ProjectsProjectsPagePage.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,wBAAwB,gCA6SvC"}