@burdenoff/microfe-store 2026.801.1 → 2026.819.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.
- package/dist/StoreAdminRoutes.js +27 -27
- package/dist/StoreRoot.js +78 -45
- package/dist/StoreRoot.js.map +1 -1
- package/dist/StoreRoutes.js +163 -85
- package/dist/StoreRoutes.js.map +1 -1
- package/dist/components/InstallAppModal.js +211 -137
- package/dist/components/InstallAppModal.js.map +1 -1
- package/dist/components/sourcing/SourcingConfirmationDialog.d.ts +15 -0
- package/dist/components/sourcing/SourcingConfirmationDialog.js +61 -0
- package/dist/components/sourcing/SourcingConfirmationDialog.js.map +1 -0
- package/dist/components/sourcing/SourcingUi.d.ts +51 -0
- package/dist/components/sourcing/SourcingUi.js +176 -0
- package/dist/components/sourcing/SourcingUi.js.map +1 -0
- package/dist/generated/global-operations.d.ts +713 -0
- package/dist/generated/global-operations.js +557 -175
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.d.ts +2158 -2159
- package/dist/generated/global-types.js +8 -2
- package/dist/generated/global-types.js.map +1 -1
- package/dist/hooks/useBackendCart.js +17 -17
- package/dist/hooks/useInstallations.js +32 -32
- package/dist/hooks/useOrders.js +7 -7
- package/dist/hooks/useSourcing.d.ts +319 -0
- package/dist/hooks/useSourcing.js +87 -0
- package/dist/hooks/useSourcing.js.map +1 -0
- package/dist/hooks/useStoreGraphQL.js +163 -163
- package/dist/index.js +12 -12
- package/dist/pages/AdminDashboardPage.js +132 -132
- package/dist/pages/AdminReviewModerationPage.js +55 -55
- package/dist/pages/AdminStoresPage.js +95 -95
- package/dist/pages/AdminSubmissionsQueuePage.js +297 -297
- package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
- package/dist/pages/AppDetailPage.js +65 -65
- package/dist/pages/AppSettingsPage.js +32 -32
- package/dist/pages/CartPage.js +66 -66
- package/dist/pages/CreateSourcingRequestPage.d.ts +3 -0
- package/dist/pages/CreateSourcingRequestPage.js +439 -0
- package/dist/pages/CreateSourcingRequestPage.js.map +1 -0
- package/dist/pages/InstalledAppsPage.js +15 -15
- package/dist/pages/InstalledAppsPage.js.map +1 -1
- package/dist/pages/MarketplaceHomePage.js +384 -384
- package/dist/pages/MarketplaceHomePage.js.map +1 -1
- package/dist/pages/MyLicensesPage.js +39 -39
- package/dist/pages/OrderConfirmationPage.js +57 -57
- package/dist/pages/OrdersPage.js +3 -3
- package/dist/pages/OrdersPage.js.map +1 -1
- package/dist/pages/ProductDetailPage.js +6 -6
- package/dist/pages/SellerSourcingOffersPage.d.ts +3 -0
- package/dist/pages/SellerSourcingOffersPage.js +284 -0
- package/dist/pages/SellerSourcingOffersPage.js.map +1 -0
- package/dist/pages/SellerSourcingOpportunitiesPage.d.ts +3 -0
- package/dist/pages/SellerSourcingOpportunitiesPage.js +272 -0
- package/dist/pages/SellerSourcingOpportunitiesPage.js.map +1 -0
- package/dist/pages/SellerSourcingOpportunityDetailPage.d.ts +3 -0
- package/dist/pages/SellerSourcingOpportunityDetailPage.js +465 -0
- package/dist/pages/SellerSourcingOpportunityDetailPage.js.map +1 -0
- package/dist/pages/SourcingRequestDetailPage.d.ts +3 -0
- package/dist/pages/SourcingRequestDetailPage.js +447 -0
- package/dist/pages/SourcingRequestDetailPage.js.map +1 -0
- package/dist/pages/SourcingRequestsPage.d.ts +3 -0
- package/dist/pages/SourcingRequestsPage.js +272 -0
- package/dist/pages/SourcingRequestsPage.js.map +1 -0
- package/dist/providers/SourcingAccessProvider.d.ts +21 -0
- package/dist/providers/SourcingAccessProvider.js +95 -0
- package/dist/providers/SourcingAccessProvider.js.map +1 -0
- package/dist/translations/en.d.ts +3 -0
- package/dist/translations/en.js +3 -0
- package/dist/translations/en.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { useStore as e } from "../providers/StoreProvider.js";
|
|
2
|
+
import { useSourcingApi as t } from "../hooks/useSourcing.js";
|
|
3
|
+
import { usePublisherSourcingAccess as n } from "../providers/SourcingAccessProvider.js";
|
|
4
|
+
import { ContextRequiredPanel as r, SourcingEmptyState as ee, SourcingErrorState as i, SourcingLoadingState as a, SourcingPageHeader as o, formatDate as s, formatLocation as c, formatMoney as l, formatSourcingUnit as u, inputClassName as d, joinStorePath as f, primaryButtonClassName as te, secondaryButtonClassName as p, toErrorMessage as ne } from "../components/sourcing/SourcingUi.js";
|
|
5
|
+
import { useCallback as m, useEffect as h, useState as g } from "react";
|
|
6
|
+
import { useNavigate as re } from "react-router";
|
|
7
|
+
import { ArrowRight as ie, BadgeCheck as _, CalendarDays as v, MapPin as y, PackageSearch as b, RefreshCw as x, Tags as S, TicketCheck as C } from "lucide-react";
|
|
8
|
+
import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
9
|
+
import { GlassCard as D, PagePurpose as O } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
//#region src/pages/SellerSourcingOpportunitiesPage.tsx
|
|
11
|
+
var k = 30;
|
|
12
|
+
function A(e) {
|
|
13
|
+
return e.trim().toLowerCase().replaceAll(/[_\s]+/g, "-").replaceAll(/-+/g, "-");
|
|
14
|
+
}
|
|
15
|
+
var j = () => {
|
|
16
|
+
let j = re(), { basePath: M } = e(), { profile: N, loading: P, error: F, isEligible: I, refresh: L } = n(), { listOpportunities: R } = t(), [z, B] = g([]), [V, H] = g(""), [U, W] = g(""), [G, K] = g(0), [q, J] = g(!1), [Y, X] = g(!1), [Z, Q] = g(null), $ = m(async (e = 0, t = !1) => {
|
|
17
|
+
if (I) {
|
|
18
|
+
X(!0), Q(null);
|
|
19
|
+
try {
|
|
20
|
+
let n = await R({
|
|
21
|
+
category: U || void 0,
|
|
22
|
+
limit: k,
|
|
23
|
+
offset: e
|
|
24
|
+
});
|
|
25
|
+
B((e) => t ? [...e, ...n] : n), K(e), J(n.length === k);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
Q(ne(e, "Sourcing opportunities could not be loaded."));
|
|
28
|
+
} finally {
|
|
29
|
+
X(!1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, [
|
|
33
|
+
U,
|
|
34
|
+
I,
|
|
35
|
+
R
|
|
36
|
+
]);
|
|
37
|
+
return h(() => {
|
|
38
|
+
$();
|
|
39
|
+
}, [$]), /* @__PURE__ */ E("div", {
|
|
40
|
+
className: "min-h-full bg-surface-default",
|
|
41
|
+
children: [/* @__PURE__ */ T(o, {
|
|
42
|
+
title: "Sourcing opportunities",
|
|
43
|
+
description: "Respond to open buyer requests as your verified Store Publisher.",
|
|
44
|
+
icon: /* @__PURE__ */ T(b, {
|
|
45
|
+
className: "size-5",
|
|
46
|
+
"aria-hidden": "true"
|
|
47
|
+
}),
|
|
48
|
+
actions: /* @__PURE__ */ E("button", {
|
|
49
|
+
type: "button",
|
|
50
|
+
onClick: () => j(f(M, "publisher/sourcing/offers")),
|
|
51
|
+
className: p,
|
|
52
|
+
children: [/* @__PURE__ */ T(C, {
|
|
53
|
+
className: "size-4",
|
|
54
|
+
"aria-hidden": "true"
|
|
55
|
+
}), "My offers"]
|
|
56
|
+
})
|
|
57
|
+
}), /* @__PURE__ */ E("main", {
|
|
58
|
+
className: "mx-auto max-w-6xl px-4 py-6 sm:px-6",
|
|
59
|
+
children: [/* @__PURE__ */ T(O, {
|
|
60
|
+
className: "mb-6",
|
|
61
|
+
children: "Opportunities contain only the buyer's sanitized request details. Your offer must match the requested quantity and unit, use the Store's USD settlement currency, and promise delivery within the requested window. One active offer per publisher can be updated until the request closes."
|
|
62
|
+
}), P ? /* @__PURE__ */ T(a, { label: "Checking Store Publisher access…" }) : F ? /* @__PURE__ */ T(i, {
|
|
63
|
+
title: "Publisher access could not be checked",
|
|
64
|
+
message: F,
|
|
65
|
+
onRetry: () => void L()
|
|
66
|
+
}) : N ? I ? /* @__PURE__ */ E(w, { children: [/* @__PURE__ */ T("div", {
|
|
67
|
+
className: "mb-5 rounded-xl border border-border-seam bg-surface-raised p-4",
|
|
68
|
+
children: /* @__PURE__ */ E("div", {
|
|
69
|
+
className: "flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between",
|
|
70
|
+
children: [/* @__PURE__ */ E("form", {
|
|
71
|
+
onSubmit: (e) => {
|
|
72
|
+
e.preventDefault(), W(A(V));
|
|
73
|
+
},
|
|
74
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-end",
|
|
75
|
+
children: [/* @__PURE__ */ E("label", {
|
|
76
|
+
className: "block min-w-0 sm:w-72",
|
|
77
|
+
children: [/* @__PURE__ */ T("span", {
|
|
78
|
+
className: "text-sm font-medium text-text-primary",
|
|
79
|
+
children: "Filter by category"
|
|
80
|
+
}), /* @__PURE__ */ E("div", {
|
|
81
|
+
className: "relative",
|
|
82
|
+
children: [/* @__PURE__ */ T(S, {
|
|
83
|
+
className: "pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-tertiary",
|
|
84
|
+
"aria-hidden": "true"
|
|
85
|
+
}), /* @__PURE__ */ T("input", {
|
|
86
|
+
type: "text",
|
|
87
|
+
value: V,
|
|
88
|
+
onChange: (e) => H(e.target.value),
|
|
89
|
+
maxLength: 64,
|
|
90
|
+
placeholder: "food-grains",
|
|
91
|
+
className: `${d} pl-9`
|
|
92
|
+
})]
|
|
93
|
+
})]
|
|
94
|
+
}), /* @__PURE__ */ E("div", {
|
|
95
|
+
className: "flex gap-2",
|
|
96
|
+
children: [/* @__PURE__ */ T("button", {
|
|
97
|
+
type: "submit",
|
|
98
|
+
className: te,
|
|
99
|
+
children: "Apply"
|
|
100
|
+
}), U ? /* @__PURE__ */ T("button", {
|
|
101
|
+
type: "button",
|
|
102
|
+
onClick: () => {
|
|
103
|
+
H(""), W("");
|
|
104
|
+
},
|
|
105
|
+
className: p,
|
|
106
|
+
children: "Clear"
|
|
107
|
+
}) : null]
|
|
108
|
+
})]
|
|
109
|
+
}), /* @__PURE__ */ E("div", {
|
|
110
|
+
className: "flex flex-wrap items-center gap-3",
|
|
111
|
+
children: [/* @__PURE__ */ E("span", {
|
|
112
|
+
className: "inline-flex items-center gap-1.5 text-sm text-text-secondary",
|
|
113
|
+
children: [
|
|
114
|
+
/* @__PURE__ */ T(_, {
|
|
115
|
+
className: "size-4 text-text-accent",
|
|
116
|
+
"aria-hidden": "true"
|
|
117
|
+
}),
|
|
118
|
+
"Offering as ",
|
|
119
|
+
N.name
|
|
120
|
+
]
|
|
121
|
+
}), /* @__PURE__ */ E("button", {
|
|
122
|
+
type: "button",
|
|
123
|
+
onClick: () => void $(),
|
|
124
|
+
disabled: Y,
|
|
125
|
+
className: p,
|
|
126
|
+
children: [/* @__PURE__ */ T(x, {
|
|
127
|
+
className: `size-4 ${Y ? "animate-spin" : ""}`,
|
|
128
|
+
"aria-hidden": "true"
|
|
129
|
+
}), "Refresh"]
|
|
130
|
+
})]
|
|
131
|
+
})]
|
|
132
|
+
})
|
|
133
|
+
}), Y && z.length === 0 ? /* @__PURE__ */ T(a, { label: "Loading open opportunities…" }) : Z && z.length === 0 ? /* @__PURE__ */ T(i, {
|
|
134
|
+
title: "Opportunities could not be loaded",
|
|
135
|
+
message: Z,
|
|
136
|
+
onRetry: () => void $()
|
|
137
|
+
}) : z.length === 0 ? /* @__PURE__ */ T(ee, {
|
|
138
|
+
title: U ? "No open opportunities in this category" : "No open opportunities",
|
|
139
|
+
description: U ? "Clear the category filter or check again later for new buyer requests." : "New sanitized requests from buyers will appear here while they are open."
|
|
140
|
+
}) : /* @__PURE__ */ E(w, { children: [
|
|
141
|
+
Z ? /* @__PURE__ */ T("div", {
|
|
142
|
+
role: "alert",
|
|
143
|
+
className: "mb-4 rounded-lg border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger",
|
|
144
|
+
children: Z
|
|
145
|
+
}) : null,
|
|
146
|
+
/* @__PURE__ */ T("ul", {
|
|
147
|
+
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-3",
|
|
148
|
+
children: z.map((e) => /* @__PURE__ */ T("li", { children: /* @__PURE__ */ E(D, {
|
|
149
|
+
className: "flex h-full flex-col p-5",
|
|
150
|
+
children: [
|
|
151
|
+
/* @__PURE__ */ E("div", {
|
|
152
|
+
className: "flex items-start justify-between gap-3",
|
|
153
|
+
children: [/* @__PURE__ */ E("div", {
|
|
154
|
+
className: "min-w-0",
|
|
155
|
+
children: [/* @__PURE__ */ T("p", {
|
|
156
|
+
className: "truncate text-xs font-semibold uppercase tracking-wide text-text-accent",
|
|
157
|
+
children: e.category
|
|
158
|
+
}), /* @__PURE__ */ T("h2", {
|
|
159
|
+
className: "mt-1 line-clamp-2 text-lg font-semibold text-text-primary",
|
|
160
|
+
children: e.productName
|
|
161
|
+
})]
|
|
162
|
+
}), /* @__PURE__ */ T("span", {
|
|
163
|
+
className: "rounded-full border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-semibold text-status-success-text",
|
|
164
|
+
children: "OPEN"
|
|
165
|
+
})]
|
|
166
|
+
}),
|
|
167
|
+
e.summary ? /* @__PURE__ */ T("p", {
|
|
168
|
+
className: "mt-3 line-clamp-3 text-sm leading-6 text-text-secondary",
|
|
169
|
+
children: e.summary
|
|
170
|
+
}) : null,
|
|
171
|
+
/* @__PURE__ */ E("dl", {
|
|
172
|
+
className: "mt-5 space-y-3 text-sm",
|
|
173
|
+
children: [
|
|
174
|
+
/* @__PURE__ */ E("div", {
|
|
175
|
+
className: "flex items-start gap-2",
|
|
176
|
+
children: [/* @__PURE__ */ T(b, {
|
|
177
|
+
className: "mt-0.5 size-4 shrink-0 text-text-tertiary",
|
|
178
|
+
"aria-hidden": "true"
|
|
179
|
+
}), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("dt", {
|
|
180
|
+
className: "text-xs text-text-tertiary",
|
|
181
|
+
children: "Quantity"
|
|
182
|
+
}), /* @__PURE__ */ E("dd", {
|
|
183
|
+
className: "font-medium text-text-primary",
|
|
184
|
+
children: [
|
|
185
|
+
e.quantity.toLocaleString(),
|
|
186
|
+
" ",
|
|
187
|
+
u(e.unit)
|
|
188
|
+
]
|
|
189
|
+
})] })]
|
|
190
|
+
}),
|
|
191
|
+
/* @__PURE__ */ E("div", {
|
|
192
|
+
className: "flex items-start gap-2",
|
|
193
|
+
children: [/* @__PURE__ */ T(v, {
|
|
194
|
+
className: "mt-0.5 size-4 shrink-0 text-text-tertiary",
|
|
195
|
+
"aria-hidden": "true"
|
|
196
|
+
}), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("dt", {
|
|
197
|
+
className: "text-xs text-text-tertiary",
|
|
198
|
+
children: "Deliver by"
|
|
199
|
+
}), /* @__PURE__ */ E("dd", {
|
|
200
|
+
className: "font-medium text-text-primary",
|
|
201
|
+
children: [
|
|
202
|
+
s(e.deliveryWindowStart),
|
|
203
|
+
" –",
|
|
204
|
+
" ",
|
|
205
|
+
s(e.deliveryWindowEnd)
|
|
206
|
+
]
|
|
207
|
+
})] })]
|
|
208
|
+
}),
|
|
209
|
+
/* @__PURE__ */ E("div", {
|
|
210
|
+
className: "flex items-start gap-2",
|
|
211
|
+
children: [/* @__PURE__ */ T(y, {
|
|
212
|
+
className: "mt-0.5 size-4 shrink-0 text-text-tertiary",
|
|
213
|
+
"aria-hidden": "true"
|
|
214
|
+
}), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("dt", {
|
|
215
|
+
className: "text-xs text-text-tertiary",
|
|
216
|
+
children: "Destination"
|
|
217
|
+
}), /* @__PURE__ */ T("dd", {
|
|
218
|
+
className: "font-medium text-text-primary",
|
|
219
|
+
children: c({
|
|
220
|
+
country: e.deliveryCountry,
|
|
221
|
+
region: e.deliveryRegion,
|
|
222
|
+
city: e.deliveryCity
|
|
223
|
+
})
|
|
224
|
+
})] })]
|
|
225
|
+
})
|
|
226
|
+
]
|
|
227
|
+
}),
|
|
228
|
+
/* @__PURE__ */ E("div", {
|
|
229
|
+
className: "mt-auto flex items-end justify-between gap-3 border-t border-border-seam pt-4",
|
|
230
|
+
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("p", {
|
|
231
|
+
className: "text-xs text-text-tertiary",
|
|
232
|
+
children: "Buyer target"
|
|
233
|
+
}), /* @__PURE__ */ T("p", {
|
|
234
|
+
className: "text-sm font-semibold text-text-primary",
|
|
235
|
+
children: l(e.targetUnitPrice, e.targetCurrency)
|
|
236
|
+
})] }), /* @__PURE__ */ E("button", {
|
|
237
|
+
type: "button",
|
|
238
|
+
onClick: () => j(f(M, `publisher/sourcing/opportunities/${e.id}`)),
|
|
239
|
+
className: p,
|
|
240
|
+
children: ["View and offer", /* @__PURE__ */ T(ie, {
|
|
241
|
+
className: "size-4",
|
|
242
|
+
"aria-hidden": "true"
|
|
243
|
+
})]
|
|
244
|
+
})]
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
}) }, e.id))
|
|
248
|
+
}),
|
|
249
|
+
q ? /* @__PURE__ */ T("div", {
|
|
250
|
+
className: "mt-6 flex justify-center",
|
|
251
|
+
children: /* @__PURE__ */ T("button", {
|
|
252
|
+
type: "button",
|
|
253
|
+
onClick: () => void $(G + k, !0),
|
|
254
|
+
disabled: Y,
|
|
255
|
+
className: p,
|
|
256
|
+
children: Y ? "Loading…" : "Load more"
|
|
257
|
+
})
|
|
258
|
+
}) : null
|
|
259
|
+
] })] }) : /* @__PURE__ */ T(r, {
|
|
260
|
+
title: "Verified publisher status required",
|
|
261
|
+
description: `The publisher profile “${N.name}” is currently ${N.verificationStatus.toLowerCase().replaceAll("_", " ")}. Opportunities become available after Store verification is active.`
|
|
262
|
+
}) : /* @__PURE__ */ T(r, {
|
|
263
|
+
title: "Store Publisher profile required",
|
|
264
|
+
description: "Only users who own a verified Store Publisher profile can discover sourcing opportunities or submit offers."
|
|
265
|
+
})]
|
|
266
|
+
})]
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
//#endregion
|
|
270
|
+
export { j as SellerSourcingOpportunitiesPage };
|
|
271
|
+
|
|
272
|
+
//# sourceMappingURL=SellerSourcingOpportunitiesPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SellerSourcingOpportunitiesPage.js","names":[],"sources":["../../src/pages/SellerSourcingOpportunitiesPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport type { FC, FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n ArrowRight,\n BadgeCheck,\n CalendarDays,\n MapPin,\n PackageSearch,\n RefreshCw,\n Tags,\n TicketCheck,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { type SellerSourcingOpportunity, useSourcingApi } from '../hooks/useSourcing';\nimport { usePublisherSourcingAccess } from '../providers/SourcingAccessProvider';\nimport {\n ContextRequiredPanel,\n formatDate,\n formatLocation,\n formatMoney,\n formatSourcingUnit,\n inputClassName,\n joinStorePath,\n primaryButtonClassName,\n secondaryButtonClassName,\n SourcingEmptyState,\n SourcingErrorState,\n SourcingLoadingState,\n SourcingPageHeader,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\n\nconst PAGE_SIZE = 30;\n\nfunction normalizeCategory(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replaceAll(/[_\\s]+/g, '-')\n .replaceAll(/-+/g, '-');\n}\n\nexport const SellerSourcingOpportunitiesPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const {\n profile,\n loading: accessLoading,\n error: accessError,\n isEligible,\n refresh: refreshAccess,\n } = usePublisherSourcingAccess();\n const { listOpportunities } = useSourcingApi();\n const [opportunities, setOpportunities] = useState<SellerSourcingOpportunity[]>([]);\n const [categoryInput, setCategoryInput] = useState('');\n const [category, setCategory] = useState('');\n const [offset, setOffset] = useState(0);\n const [hasMore, setHasMore] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const load = useCallback(\n async (nextOffset = 0, append = false) => {\n if (!isEligible) return;\n setLoading(true);\n setError(null);\n try {\n const result = await listOpportunities({\n category: category || undefined,\n limit: PAGE_SIZE,\n offset: nextOffset,\n });\n setOpportunities((current) => (append ? [...current, ...result] : result));\n setOffset(nextOffset);\n setHasMore(result.length === PAGE_SIZE);\n } catch (loadError) {\n setError(toErrorMessage(loadError, 'Sourcing opportunities could not be loaded.'));\n } finally {\n setLoading(false);\n }\n },\n [category, isEligible, listOpportunities]\n );\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const applyCategory = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setCategory(normalizeCategory(categoryInput));\n };\n\n const clearCategory = () => {\n setCategoryInput('');\n setCategory('');\n };\n\n return (\n <div className=\"min-h-full bg-surface-default\">\n <SourcingPageHeader\n title=\"Sourcing opportunities\"\n description=\"Respond to open buyer requests as your verified Store Publisher.\"\n icon={<PackageSearch className=\"size-5\" aria-hidden=\"true\" />}\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(joinStorePath(basePath, 'publisher/sourcing/offers'))}\n className={secondaryButtonClassName}\n >\n <TicketCheck className=\"size-4\" aria-hidden=\"true\" />\n My offers\n </button>\n }\n />\n\n <main className=\"mx-auto max-w-6xl px-4 py-6 sm:px-6\">\n <PagePurpose className=\"mb-6\">\n Opportunities contain only the buyer's sanitized request details. Your offer must match\n the requested quantity and unit, use the Store's USD settlement currency, and promise\n delivery within the requested window. One active offer per publisher can be updated until\n the request closes.\n </PagePurpose>\n\n {accessLoading ? (\n <SourcingLoadingState label=\"Checking Store Publisher access…\" />\n ) : accessError ? (\n <SourcingErrorState\n title=\"Publisher access could not be checked\"\n message={accessError}\n onRetry={() => void refreshAccess()}\n />\n ) : !profile ? (\n <ContextRequiredPanel\n title=\"Store Publisher profile required\"\n description=\"Only users who own a verified Store Publisher profile can discover sourcing opportunities or submit offers.\"\n />\n ) : !isEligible ? (\n <ContextRequiredPanel\n title=\"Verified publisher status required\"\n description={`The publisher profile “${profile.name}” is currently ${profile.verificationStatus.toLowerCase().replaceAll('_', ' ')}. Opportunities become available after Store verification is active.`}\n />\n ) : (\n <>\n <div className=\"mb-5 rounded-xl border border-border-seam bg-surface-raised p-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n <form\n onSubmit={applyCategory}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-end\"\n >\n <label className=\"block min-w-0 sm:w-72\">\n <span className=\"text-sm font-medium text-text-primary\">\n Filter by category\n </span>\n <div className=\"relative\">\n <Tags\n className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <input\n type=\"text\"\n value={categoryInput}\n onChange={(event) => setCategoryInput(event.target.value)}\n maxLength={64}\n placeholder=\"food-grains\"\n className={`${inputClassName} pl-9`}\n />\n </div>\n </label>\n <div className=\"flex gap-2\">\n <button type=\"submit\" className={primaryButtonClassName}>\n Apply\n </button>\n {category ? (\n <button\n type=\"button\"\n onClick={clearCategory}\n className={secondaryButtonClassName}\n >\n Clear\n </button>\n ) : null}\n </div>\n </form>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <BadgeCheck className=\"size-4 text-text-accent\" aria-hidden=\"true\" />\n Offering as {profile.name}\n </span>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading}\n className={secondaryButtonClassName}\n >\n <RefreshCw\n className={`size-4 ${loading ? 'animate-spin' : ''}`}\n aria-hidden=\"true\"\n />\n Refresh\n </button>\n </div>\n </div>\n </div>\n\n {loading && opportunities.length === 0 ? (\n <SourcingLoadingState label=\"Loading open opportunities…\" />\n ) : error && opportunities.length === 0 ? (\n <SourcingErrorState\n title=\"Opportunities could not be loaded\"\n message={error}\n onRetry={() => void load()}\n />\n ) : opportunities.length === 0 ? (\n <SourcingEmptyState\n title={\n category ? 'No open opportunities in this category' : 'No open opportunities'\n }\n description={\n category\n ? 'Clear the category filter or check again later for new buyer requests.'\n : 'New sanitized requests from buyers will appear here while they are open.'\n }\n />\n ) : (\n <>\n {error ? (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n ) : null}\n <ul className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {opportunities.map((opportunity) => (\n <li key={opportunity.id}>\n <GlassCard className=\"flex h-full flex-col p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-xs font-semibold uppercase tracking-wide text-text-accent\">\n {opportunity.category}\n </p>\n <h2 className=\"mt-1 line-clamp-2 text-lg font-semibold text-text-primary\">\n {opportunity.productName}\n </h2>\n </div>\n <span className=\"rounded-full border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-semibold text-status-success-text\">\n OPEN\n </span>\n </div>\n\n {opportunity.summary ? (\n <p className=\"mt-3 line-clamp-3 text-sm leading-6 text-text-secondary\">\n {opportunity.summary}\n </p>\n ) : null}\n\n <dl className=\"mt-5 space-y-3 text-sm\">\n <div className=\"flex items-start gap-2\">\n <PackageSearch\n className=\"mt-0.5 size-4 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Quantity</dt>\n <dd className=\"font-medium text-text-primary\">\n {opportunity.quantity.toLocaleString()}{' '}\n {formatSourcingUnit(opportunity.unit)}\n </dd>\n </div>\n </div>\n <div className=\"flex items-start gap-2\">\n <CalendarDays\n className=\"mt-0.5 size-4 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Deliver by</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDate(opportunity.deliveryWindowStart)} –{' '}\n {formatDate(opportunity.deliveryWindowEnd)}\n </dd>\n </div>\n </div>\n <div className=\"flex items-start gap-2\">\n <MapPin\n className=\"mt-0.5 size-4 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Destination</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatLocation({\n country: opportunity.deliveryCountry,\n region: opportunity.deliveryRegion,\n city: opportunity.deliveryCity,\n })}\n </dd>\n </div>\n </div>\n </dl>\n\n <div className=\"mt-auto flex items-end justify-between gap-3 border-t border-border-seam pt-4\">\n <div>\n <p className=\"text-xs text-text-tertiary\">Buyer target</p>\n <p className=\"text-sm font-semibold text-text-primary\">\n {formatMoney(opportunity.targetUnitPrice, opportunity.targetCurrency)}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n joinStorePath(\n basePath,\n `publisher/sourcing/opportunities/${opportunity.id}`\n )\n )\n }\n className={secondaryButtonClassName}\n >\n View and offer\n <ArrowRight className=\"size-4\" aria-hidden=\"true\" />\n </button>\n </div>\n </GlassCard>\n </li>\n ))}\n </ul>\n\n {hasMore ? (\n <div className=\"mt-6 flex justify-center\">\n <button\n type=\"button\"\n onClick={() => void load(offset + PAGE_SIZE, true)}\n disabled={loading}\n className={secondaryButtonClassName}\n >\n {loading ? 'Loading…' : 'Load more'}\n </button>\n </div>\n ) : null}\n </>\n )}\n </>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|