@burdenoff/microfe-store 2026.819.10 → 2026.819.11
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/components/sourcing/SourcingConfirmationDialog.js +1 -1
- package/dist/components/sourcing/SourcingConfirmationDialog.js.map +1 -1
- package/dist/components/sourcing/SourcingUi.d.ts +2 -2
- package/dist/components/sourcing/SourcingUi.js +7 -7
- package/dist/components/sourcing/SourcingUi.js.map +1 -1
- package/dist/pages/CreateSourcingRequestPage.js +13 -14
- package/dist/pages/CreateSourcingRequestPage.js.map +1 -1
- package/dist/pages/SellerSourcingOffersPage.js +16 -16
- package/dist/pages/SellerSourcingOffersPage.js.map +1 -1
- package/dist/pages/SellerSourcingOpportunitiesPage.js +14 -14
- package/dist/pages/SellerSourcingOpportunitiesPage.js.map +1 -1
- package/dist/pages/SellerSourcingOpportunityDetailPage.js +25 -25
- package/dist/pages/SellerSourcingOpportunityDetailPage.js.map +1 -1
- package/dist/pages/SourcingRequestDetailPage.js +22 -22
- package/dist/pages/SourcingRequestDetailPage.js.map +1 -1
- package/dist/pages/SourcingRequestsPage.js +13 -13
- package/dist/pages/SourcingRequestsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SellerSourcingOpportunityDetailPage.js","names":[],"sources":["../../src/pages/SellerSourcingOpportunityDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\nimport type { ChangeEvent, FC, FormEvent } from 'react';\nimport { useNavigate, useParams } from 'react-router';\nimport {\n BadgeCheck,\n CalendarDays,\n Check,\n CircleDollarSign,\n Loader2,\n MapPin,\n PackageSearch,\n Save,\n TicketCheck,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { SourcingOfferStatus } from '../generated/global-types';\nimport type { UpsertSourcingOfferInput } from '../generated/global-types';\nimport { useStore } from '../providers/StoreProvider';\nimport {\n type SellerSourcingOfferDetail,\n type SellerSourcingOpportunityDetail,\n useSourcingApi,\n} from '../hooks/useSourcing';\nimport { usePublisherSourcingAccess } from '../providers/SourcingAccessProvider';\nimport {\n ContextRequiredPanel,\n formatDateTime,\n formatLocation,\n formatMoney,\n formatSourcingUnit,\n helpClassName,\n inputClassName,\n joinStorePath,\n labelClassName,\n primaryButtonClassName,\n secondaryButtonClassName,\n SourcingErrorState,\n SourcingLoadingState,\n SourcingPageHeader,\n SourcingStatusBadge,\n toDateTimeLocal,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\n\ninterface OfferFormState {\n unitPrice: string;\n deliveryDate: string;\n storeProductId: string;\n message: string;\n}\n\ntype OfferField = keyof OfferFormState;\ntype OfferErrors = Partial<Record<OfferField, string>>;\n\nconst EMPTY_FORM: OfferFormState = {\n unitPrice: '',\n deliveryDate: '',\n storeProductId: '',\n message: '',\n};\nconst MONEY_PATTERN = /^(?:0|[1-9]\\d{0,15})(?:\\.\\d{1,2})?$/;\n\nfunction validateOffer(\n form: OfferFormState,\n opportunity: SellerSourcingOpportunityDetail\n): OfferErrors {\n const errors: OfferErrors = {};\n const amount = form.unitPrice.trim();\n const delivery = new Date(form.deliveryDate);\n const start = new Date(opportunity.deliveryWindowStart);\n const end = new Date(opportunity.deliveryWindowEnd);\n\n if (!MONEY_PATTERN.test(amount) || Number(amount) <= 0) {\n errors.unitPrice = 'Enter a positive unit price with no more than two decimal places.';\n }\n if (\n !form.deliveryDate ||\n Number.isNaN(delivery.getTime()) ||\n delivery < start ||\n delivery > end\n ) {\n errors.deliveryDate = 'Choose a delivery date within the buyer’s requested window.';\n }\n if (form.message.trim().length > 1000) {\n errors.message = 'Offer message cannot exceed 1,000 characters.';\n }\n return errors;\n}\n\nexport const SellerSourcingOpportunityDetailPage: FC = () => {\n const navigate = useNavigate();\n const { requestId } = useParams<{ requestId: string }>();\n const { basePath } = useStore();\n const {\n profile,\n products,\n productsLoading,\n productsError,\n loading: accessLoading,\n error: accessError,\n isEligible,\n refresh: refreshAccess,\n refreshProducts,\n } = usePublisherSourcingAccess();\n const { getOpportunity, upsertOffer } = useSourcingApi();\n const [opportunity, setOpportunity] = useState<SellerSourcingOpportunityDetail | null>(null);\n const [offer, setOffer] = useState<SellerSourcingOfferDetail | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [form, setForm] = useState<OfferFormState>(EMPTY_FORM);\n const [errors, setErrors] = useState<OfferErrors>({});\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [success, setSuccess] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n const opportunitiesPath = joinStorePath(basePath, 'publisher/sourcing/opportunities');\n\n const load = useCallback(\n async (initializeForm = false) => {\n if (!isEligible || !requestId) return;\n setLoading(true);\n setError(null);\n try {\n const result = await getOpportunity(requestId);\n setOpportunity(result.opportunity);\n setOffer(result.offer);\n setIsOpen(result.isOpen);\n if (!result.opportunity) {\n setError('This sourcing opportunity is no longer available.');\n } else if (initializeForm) {\n setForm({\n unitPrice: result.offer?.unitPrice ?? '',\n deliveryDate: result.offer\n ? toDateTimeLocal(result.offer.deliveryDate)\n : toDateTimeLocal(result.opportunity.deliveryWindowStart),\n storeProductId: result.offer?.storeProductId ?? '',\n message: result.offer?.message ?? '',\n });\n }\n } catch (loadError) {\n setError(toErrorMessage(loadError, 'The sourcing opportunity could not be loaded.'));\n } finally {\n setLoading(false);\n }\n },\n [getOpportunity, isEligible, requestId]\n );\n\n useEffect(() => {\n void load(true);\n }, [load]);\n\n const publishedProducts = useMemo(\n () =>\n [...products]\n .filter((product) => product.status === 'PUBLISHED')\n .sort((left, right) => left.name.localeCompare(right.name)),\n [products]\n );\n\n const updateField =\n (field: OfferField) =>\n (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {\n setForm((current) => ({ ...current, [field]: event.target.value }));\n setErrors((current) => ({ ...current, [field]: undefined }));\n setSuccess(null);\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!opportunity || !requestId || !isOpen || submitting) return;\n\n const validationErrors = validateOffer(form, opportunity);\n setErrors(validationErrors);\n setSubmitError(null);\n setSuccess(null);\n if (Object.keys(validationErrors).length > 0) return;\n\n const input: UpsertSourcingOfferInput = {\n requestId,\n quantity: opportunity.quantity,\n unit: opportunity.unit,\n unitPrice: form.unitPrice.trim(),\n currency: 'USD',\n deliveryDate: new Date(form.deliveryDate).toISOString(),\n storeProductId: form.storeProductId || undefined,\n message: form.message.trim() || undefined,\n };\n\n setSubmitting(true);\n try {\n const saved = await upsertOffer(input);\n setSuccess(offer ? 'Your offer was updated.' : 'Your offer was submitted.');\n setOffer(saved);\n await load(false);\n } catch (saveError) {\n setSubmitError(\n toErrorMessage(\n saveError,\n 'Your offer could not be saved. The request may have closed while you were editing.'\n )\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n const fieldError = (field: OfferField) => {\n const message = errors[field];\n return message ? (\n <p id={`${field}-error`} className=\"mt-1 text-xs text-text-danger\">\n {message}\n </p>\n ) : null;\n };\n\n const canEdit = isOpen && (!offer || offer.status === SourcingOfferStatus.Active);\n\n return (\n <div className=\"min-h-full bg-surface-default\">\n <SourcingPageHeader\n title={opportunity?.productName ?? 'Sourcing opportunity'}\n description=\"Review the sanitized buyer request and create or update your publisher’s offer.\"\n icon={<TicketCheck className=\"size-5\" aria-hidden=\"true\" />}\n onBack={() => navigate(opportunitiesPath)}\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(joinStorePath(basePath, 'publisher/sourcing/offers'))}\n className={secondaryButtonClassName}\n >\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 Quantity and unit are fixed by the buyer request. Store validates publisher identity, USD\n settlement price, delivery window, and optional linked product on every save. You can\n update one active offer until 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 view this opportunity or submit an offer.\"\n />\n ) : !isEligible ? (\n <ContextRequiredPanel\n title=\"Verified publisher status required\"\n description={`The publisher profile “${profile.name}” is currently ${profile.verificationStatus.toLowerCase().replaceAll('_', ' ')}.`}\n />\n ) : loading && !opportunity ? (\n <SourcingLoadingState label=\"Loading opportunity and your offer…\" />\n ) : error && !opportunity ? (\n <SourcingErrorState\n title=\"Opportunity unavailable\"\n message={error}\n onRetry={requestId ? () => void load(true) : undefined}\n />\n ) : opportunity ? (\n <>\n {submitError ? (\n <div\n role=\"alert\"\n className=\"mb-5 rounded-lg border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {submitError}\n </div>\n ) : null}\n {success ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"mb-5 flex items-center gap-2 rounded-lg border border-status-success-border bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\"\n >\n <Check className=\"size-4\" aria-hidden=\"true\" />\n {success}\n </div>\n ) : null}\n\n {offer?.order ? (\n <EmphasisPanel className=\"mb-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-semibold text-status-success-text\">\n This offer was accepted\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Store order {offer.order.id} ·{' '}\n {formatMoney(offer.order.total, offer.order.currency)} · {offer.order.status}\n </p>\n </div>\n {offer.order.fulfillment ? (\n <span className=\"rounded-full border border-border-default bg-surface-muted px-3 py-1.5 text-xs font-medium text-text-secondary\">\n Fulfillment {offer.order.fulfillment.status.replaceAll('_', ' ')}\n </span>\n ) : null}\n </div>\n </EmphasisPanel>\n ) : null}\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1.2fr)_minmax(340px,0.8fr)]\">\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-medium text-text-accent\">{opportunity.category}</p>\n <h2 className=\"mt-1 text-xl font-semibold text-text-primary\">\n {opportunity.productName}\n </h2>\n </div>\n <span\n className={`rounded-full border px-2.5 py-1 text-xs font-semibold ${\n isOpen\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-border-default bg-surface-muted text-text-secondary'\n }`}\n >\n {isOpen ? 'OPEN' : 'CLOSED'}\n </span>\n </div>\n\n {opportunity.summary ? (\n <p className=\"mt-4 whitespace-pre-wrap text-sm leading-6 text-text-secondary\">\n {opportunity.summary}\n </p>\n ) : null}\n\n <dl className=\"mt-6 grid gap-5 border-t border-border-seam pt-5 sm:grid-cols-2\">\n <div className=\"flex gap-3\">\n <PackageSearch\n className=\"mt-0.5 size-5 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Required quantity</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {opportunity.quantity.toLocaleString()}{' '}\n {formatSourcingUnit(opportunity.unit)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CircleDollarSign\n className=\"mt-0.5 size-5 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Buyer target</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatMoney(opportunity.targetUnitPrice, opportunity.targetCurrency)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CalendarDays\n className=\"mt-0.5 size-5 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Delivery window</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(opportunity.deliveryWindowStart)} –{' '}\n {formatDateTime(opportunity.deliveryWindowEnd)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <MapPin\n className=\"mt-0.5 size-5 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=\"mt-0.5 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 </GlassCard>\n\n {canEdit ? (\n <form onSubmit={(event) => void handleSubmit(event)} noValidate>\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {offer ? 'Update your offer' : 'Create an offer'}\n </h2>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Offering as {profile.name}\n </p>\n </div>\n <BadgeCheck\n className=\"size-5 text-text-accent\"\n aria-label=\"Verified publisher\"\n />\n </div>\n\n <div className=\"mt-5 space-y-5\">\n <label className={labelClassName}>\n Unit price\n <div className=\"mt-1.5 grid grid-cols-[1fr_96px] gap-2\">\n <input\n type=\"text\"\n value={form.unitPrice}\n onChange={updateField('unitPrice')}\n inputMode=\"decimal\"\n required\n maxLength={19}\n aria-invalid={Boolean(errors.unitPrice)}\n aria-describedby={\n errors.unitPrice ? 'unitPrice-error' : 'unitPrice-help'\n }\n placeholder=\"125.50\"\n className={`${inputClassName} mt-0`}\n />\n <span\n aria-label=\"Offer currency\"\n className=\"mt-0 flex items-center justify-center rounded-md border border-border-default bg-surface-muted px-3 text-sm font-semibold text-text-secondary\"\n >\n USD\n </span>\n </div>\n {fieldError('unitPrice')}\n {!errors.unitPrice ? (\n <p id=\"unitPrice-help\" className={helpClassName}>\n Price per {formatSourcingUnit(opportunity.unit).toLowerCase()}. Store\n sourcing currently settles in USD.\n </p>\n ) : null}\n </label>\n\n <label className={labelClassName}>\n Promised delivery date\n <input\n type=\"datetime-local\"\n value={form.deliveryDate}\n onChange={updateField('deliveryDate')}\n required\n min={toDateTimeLocal(opportunity.deliveryWindowStart)}\n max={toDateTimeLocal(opportunity.deliveryWindowEnd)}\n aria-invalid={Boolean(errors.deliveryDate)}\n aria-describedby={\n errors.deliveryDate ? 'deliveryDate-error' : 'deliveryDate-help'\n }\n className={inputClassName}\n />\n {fieldError('deliveryDate')}\n {!errors.deliveryDate ? (\n <p id=\"deliveryDate-help\" className={helpClassName}>\n Must fall inside the buyer’s delivery window.\n </p>\n ) : null}\n </label>\n\n <label className={labelClassName}>\n Link a published product{' '}\n <span className=\"font-normal text-text-tertiary\">(optional)</span>\n <select\n value={form.storeProductId}\n onChange={updateField('storeProductId')}\n disabled={productsLoading}\n aria-describedby={\n productsError ? 'store-product-error' : 'store-product-help'\n }\n className={inputClassName}\n >\n <option value=\"\">\n {productsLoading\n ? 'Loading published products…'\n : 'No linked Store product'}\n </option>\n {publishedProducts.map((product) => (\n <option key={product.id} value={product.id}>\n {product.name}\n </option>\n ))}\n </select>\n {productsError ? (\n <div\n id=\"store-product-error\"\n role=\"alert\"\n className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-danger\"\n >\n <span>{productsError}</span>\n <button\n type=\"button\"\n onClick={() => void refreshProducts()}\n className=\"font-semibold underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg\"\n >\n Try again\n </button>\n </div>\n ) : (\n <p id=\"store-product-help\" className={helpClassName}>\n Only products owned by this publisher and currently published are\n accepted. The link is reference metadata and does not reserve catalog\n inventory or create digital entitlements.\n </p>\n )}\n </label>\n\n <label className={labelClassName}>\n Message <span className=\"font-normal text-text-tertiary\">(optional)</span>\n <textarea\n value={form.message}\n onChange={updateField('message')}\n maxLength={1000}\n rows={4}\n aria-invalid={Boolean(errors.message)}\n aria-describedby={errors.message ? 'message-error' : 'message-help'}\n placeholder=\"Add packaging, quality, or delivery notes relevant to this offer.\"\n className={`${inputClassName} resize-y`}\n />\n {fieldError('message')}\n {!errors.message ? (\n <p id=\"message-help\" className={helpClassName}>\n {form.message.length}/1,000 characters\n </p>\n ) : null}\n </label>\n </div>\n\n <button\n type=\"submit\"\n disabled={submitting}\n className={`${primaryButtonClassName} mt-6 w-full`}\n >\n {submitting ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : offer ? (\n <Save className=\"size-4\" aria-hidden=\"true\" />\n ) : (\n <TicketCheck className=\"size-4\" aria-hidden=\"true\" />\n )}\n {submitting ? 'Saving offer…' : offer ? 'Update offer' : 'Submit offer'}\n </button>\n </GlassCard>\n </form>\n ) : offer ? (\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Your offer</h2>\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Updated {formatDateTime(offer.updatedAt)}\n </p>\n </div>\n <SourcingStatusBadge status={offer.status} />\n </div>\n <dl className=\"mt-5 space-y-4 text-sm\">\n <div>\n <dt className=\"text-xs text-text-tertiary\">Unit price</dt>\n <dd className=\"mt-0.5 text-lg font-semibold text-text-primary\">\n {formatMoney(offer.unitPrice, offer.currency)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Promised delivery</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(offer.deliveryDate)}\n </dd>\n </div>\n {offer.message ? (\n <div>\n <dt className=\"text-xs text-text-tertiary\">Message</dt>\n <dd className=\"mt-1 whitespace-pre-wrap text-text-secondary\">\n {offer.message}\n </dd>\n </div>\n ) : null}\n </dl>\n </GlassCard>\n ) : (\n <ContextRequiredPanel\n title=\"This request is closed\"\n description=\"The buyer matched or canceled this request before your publisher submitted an offer.\"\n />\n )}\n </div>\n </>\n ) : null}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SellerSourcingOpportunityDetailPage.js","names":[],"sources":["../../src/pages/SellerSourcingOpportunityDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\nimport type { ChangeEvent, FC, FormEvent } from 'react';\nimport { useNavigate, useParams } from 'react-router';\nimport {\n BadgeCheck,\n CalendarDays,\n Check,\n CircleDollarSign,\n Loader2,\n MapPin,\n PackageSearch,\n Save,\n TicketCheck,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { SourcingOfferStatus } from '../generated/global-types';\nimport type { UpsertSourcingOfferInput } from '../generated/global-types';\nimport { useStore } from '../providers/StoreProvider';\nimport {\n type SellerSourcingOfferDetail,\n type SellerSourcingOpportunityDetail,\n useSourcingApi,\n} from '../hooks/useSourcing';\nimport { usePublisherSourcingAccess } from '../providers/SourcingAccessProvider';\nimport {\n ContextRequiredPanel,\n formatDateTime,\n formatLocation,\n formatMoney,\n formatSourcingUnit,\n helpClassName,\n inputClassName,\n joinStorePath,\n labelClassName,\n primaryButtonClassName,\n secondaryButtonClassName,\n SourcingErrorState,\n SourcingLoadingState,\n SourcingPageHeader,\n SourcingStatusBadge,\n toDateTimeLocal,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\n\ninterface OfferFormState {\n unitPrice: string;\n deliveryDate: string;\n storeProductId: string;\n message: string;\n}\n\ntype OfferField = keyof OfferFormState;\ntype OfferErrors = Partial<Record<OfferField, string>>;\n\nconst EMPTY_FORM: OfferFormState = {\n unitPrice: '',\n deliveryDate: '',\n storeProductId: '',\n message: '',\n};\nconst MONEY_PATTERN = /^(?:0|[1-9]\\d{0,15})(?:\\.\\d{1,2})?$/;\n\nfunction validateOffer(\n form: OfferFormState,\n opportunity: SellerSourcingOpportunityDetail\n): OfferErrors {\n const errors: OfferErrors = {};\n const amount = form.unitPrice.trim();\n const delivery = new Date(form.deliveryDate);\n const start = new Date(opportunity.deliveryWindowStart);\n const end = new Date(opportunity.deliveryWindowEnd);\n\n if (!MONEY_PATTERN.test(amount) || Number(amount) <= 0) {\n errors.unitPrice = 'Enter a positive unit price with no more than two decimal places.';\n }\n if (\n !form.deliveryDate ||\n Number.isNaN(delivery.getTime()) ||\n delivery < start ||\n delivery > end\n ) {\n errors.deliveryDate = 'Choose a delivery date within the buyer’s requested window.';\n }\n if (form.message.trim().length > 1000) {\n errors.message = 'Offer message cannot exceed 1,000 characters.';\n }\n return errors;\n}\n\nexport const SellerSourcingOpportunityDetailPage: FC = () => {\n const navigate = useNavigate();\n const { requestId } = useParams<{ requestId: string }>();\n const { basePath } = useStore();\n const {\n profile,\n products,\n productsLoading,\n productsError,\n loading: accessLoading,\n error: accessError,\n isEligible,\n refresh: refreshAccess,\n refreshProducts,\n } = usePublisherSourcingAccess();\n const { getOpportunity, upsertOffer } = useSourcingApi();\n const [opportunity, setOpportunity] = useState<SellerSourcingOpportunityDetail | null>(null);\n const [offer, setOffer] = useState<SellerSourcingOfferDetail | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [form, setForm] = useState<OfferFormState>(EMPTY_FORM);\n const [errors, setErrors] = useState<OfferErrors>({});\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [success, setSuccess] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n const opportunitiesPath = joinStorePath(basePath, 'publisher/sourcing/opportunities');\n\n const load = useCallback(\n async (initializeForm = false) => {\n if (!isEligible || !requestId) return;\n setLoading(true);\n setError(null);\n try {\n const result = await getOpportunity(requestId);\n setOpportunity(result.opportunity);\n setOffer(result.offer);\n setIsOpen(result.isOpen);\n if (!result.opportunity) {\n setError('This sourcing opportunity is no longer available.');\n } else if (initializeForm) {\n setForm({\n unitPrice: result.offer?.unitPrice ?? '',\n deliveryDate: result.offer\n ? toDateTimeLocal(result.offer.deliveryDate)\n : toDateTimeLocal(result.opportunity.deliveryWindowStart),\n storeProductId: result.offer?.storeProductId ?? '',\n message: result.offer?.message ?? '',\n });\n }\n } catch (loadError) {\n setError(toErrorMessage(loadError, 'The sourcing opportunity could not be loaded.'));\n } finally {\n setLoading(false);\n }\n },\n [getOpportunity, isEligible, requestId]\n );\n\n useEffect(() => {\n void load(true);\n }, [load]);\n\n const publishedProducts = useMemo(\n () =>\n [...products]\n .filter((product) => product.status === 'PUBLISHED')\n .sort((left, right) => left.name.localeCompare(right.name)),\n [products]\n );\n\n const updateField =\n (field: OfferField) =>\n (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {\n setForm((current) => ({ ...current, [field]: event.target.value }));\n setErrors((current) => ({ ...current, [field]: undefined }));\n setSuccess(null);\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!opportunity || !requestId || !isOpen || submitting) return;\n\n const validationErrors = validateOffer(form, opportunity);\n setErrors(validationErrors);\n setSubmitError(null);\n setSuccess(null);\n if (Object.keys(validationErrors).length > 0) return;\n\n const input: UpsertSourcingOfferInput = {\n requestId,\n quantity: opportunity.quantity,\n unit: opportunity.unit,\n unitPrice: form.unitPrice.trim(),\n currency: 'USD',\n deliveryDate: new Date(form.deliveryDate).toISOString(),\n storeProductId: form.storeProductId || undefined,\n message: form.message.trim() || undefined,\n };\n\n setSubmitting(true);\n try {\n const saved = await upsertOffer(input);\n setSuccess(offer ? 'Your offer was updated.' : 'Your offer was submitted.');\n setOffer(saved);\n await load(false);\n } catch (saveError) {\n setSubmitError(\n toErrorMessage(\n saveError,\n 'Your offer could not be saved. The request may have closed while you were editing.'\n )\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n const fieldError = (field: OfferField) => {\n const message = errors[field];\n return message ? (\n <p id={`${field}-error`} className=\"mt-1 text-xs text-status-error-text\">\n {message}\n </p>\n ) : null;\n };\n\n const canEdit = isOpen && (!offer || offer.status === SourcingOfferStatus.Active);\n\n return (\n <div className=\"min-h-full bg-surface\">\n <SourcingPageHeader\n title={opportunity?.productName ?? 'Sourcing opportunity'}\n description=\"Review the sanitized buyer request and create or update your publisher’s offer.\"\n icon={<TicketCheck className=\"size-5\" aria-hidden=\"true\" />}\n onBack={() => navigate(opportunitiesPath)}\n actions={\n <button\n type=\"button\"\n onClick={() => navigate(joinStorePath(basePath, 'publisher/sourcing/offers'))}\n className={secondaryButtonClassName}\n >\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 Quantity and unit are fixed by the buyer request. Store validates publisher identity, USD\n settlement price, delivery window, and optional linked product on every save. You can\n update one active offer until 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 view this opportunity or submit an offer.\"\n />\n ) : !isEligible ? (\n <ContextRequiredPanel\n title=\"Verified publisher status required\"\n description={`The publisher profile “${profile.name}” is currently ${profile.verificationStatus.toLowerCase().replaceAll('_', ' ')}.`}\n />\n ) : loading && !opportunity ? (\n <SourcingLoadingState label=\"Loading opportunity and your offer…\" />\n ) : error && !opportunity ? (\n <SourcingErrorState\n title=\"Opportunity unavailable\"\n message={error}\n onRetry={requestId ? () => void load(true) : undefined}\n />\n ) : opportunity ? (\n <>\n {submitError ? (\n <div\n role=\"alert\"\n className=\"mb-5 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {submitError}\n </div>\n ) : null}\n {success ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"mb-5 flex items-center gap-2 rounded-lg border border-status-success-border bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\"\n >\n <Check className=\"size-4\" aria-hidden=\"true\" />\n {success}\n </div>\n ) : null}\n\n {offer?.order ? (\n <EmphasisPanel className=\"mb-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-semibold text-status-success-text\">\n This offer was accepted\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Store order {offer.order.id} ·{' '}\n {formatMoney(offer.order.total, offer.order.currency)} · {offer.order.status}\n </p>\n </div>\n {offer.order.fulfillment ? (\n <span className=\"rounded-full border border-border-default bg-surface-sunken px-3 py-1.5 text-xs font-medium text-text-secondary\">\n Fulfillment {offer.order.fulfillment.status.replaceAll('_', ' ')}\n </span>\n ) : null}\n </div>\n </EmphasisPanel>\n ) : null}\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1.2fr)_minmax(340px,0.8fr)]\">\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-medium text-accent-primary\">\n {opportunity.category}\n </p>\n <h2 className=\"mt-1 text-xl font-semibold text-text-primary\">\n {opportunity.productName}\n </h2>\n </div>\n <span\n className={`rounded-full border px-2.5 py-1 text-xs font-semibold ${\n isOpen\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-border-default bg-surface-sunken text-text-secondary'\n }`}\n >\n {isOpen ? 'OPEN' : 'CLOSED'}\n </span>\n </div>\n\n {opportunity.summary ? (\n <p className=\"mt-4 whitespace-pre-wrap text-sm leading-6 text-text-secondary\">\n {opportunity.summary}\n </p>\n ) : null}\n\n <dl className=\"mt-6 grid gap-5 border-t border-border-subtle pt-5 sm:grid-cols-2\">\n <div className=\"flex gap-3\">\n <PackageSearch\n className=\"mt-0.5 size-5 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-muted\">Required quantity</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {opportunity.quantity.toLocaleString()}{' '}\n {formatSourcingUnit(opportunity.unit)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CircleDollarSign\n className=\"mt-0.5 size-5 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-muted\">Buyer target</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatMoney(opportunity.targetUnitPrice, opportunity.targetCurrency)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CalendarDays\n className=\"mt-0.5 size-5 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-muted\">Delivery window</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(opportunity.deliveryWindowStart)} –{' '}\n {formatDateTime(opportunity.deliveryWindowEnd)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <MapPin className=\"mt-0.5 size-5 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n <div>\n <dt className=\"text-xs text-text-muted\">Destination</dt>\n <dd className=\"mt-0.5 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 </GlassCard>\n\n {canEdit ? (\n <form onSubmit={(event) => void handleSubmit(event)} noValidate>\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {offer ? 'Update your offer' : 'Create an offer'}\n </h2>\n <p className=\"mt-1 text-xs text-text-muted\">Offering as {profile.name}</p>\n </div>\n <BadgeCheck\n className=\"size-5 text-accent-primary\"\n aria-label=\"Verified publisher\"\n />\n </div>\n\n <div className=\"mt-5 space-y-5\">\n <label className={labelClassName}>\n Unit price\n <div className=\"mt-1.5 grid grid-cols-[1fr_96px] gap-2\">\n <input\n type=\"text\"\n value={form.unitPrice}\n onChange={updateField('unitPrice')}\n inputMode=\"decimal\"\n required\n maxLength={19}\n aria-invalid={Boolean(errors.unitPrice)}\n aria-describedby={\n errors.unitPrice ? 'unitPrice-error' : 'unitPrice-help'\n }\n placeholder=\"125.50\"\n className={`${inputClassName} mt-0`}\n />\n <span\n aria-label=\"Offer currency\"\n className=\"mt-0 flex items-center justify-center rounded-md border border-border-default bg-surface-sunken px-3 text-sm font-semibold text-text-secondary\"\n >\n USD\n </span>\n </div>\n {fieldError('unitPrice')}\n {!errors.unitPrice ? (\n <p id=\"unitPrice-help\" className={helpClassName}>\n Price per {formatSourcingUnit(opportunity.unit).toLowerCase()}. Store\n sourcing currently settles in USD.\n </p>\n ) : null}\n </label>\n\n <label className={labelClassName}>\n Promised delivery date\n <input\n type=\"datetime-local\"\n value={form.deliveryDate}\n onChange={updateField('deliveryDate')}\n required\n min={toDateTimeLocal(opportunity.deliveryWindowStart)}\n max={toDateTimeLocal(opportunity.deliveryWindowEnd)}\n aria-invalid={Boolean(errors.deliveryDate)}\n aria-describedby={\n errors.deliveryDate ? 'deliveryDate-error' : 'deliveryDate-help'\n }\n className={inputClassName}\n />\n {fieldError('deliveryDate')}\n {!errors.deliveryDate ? (\n <p id=\"deliveryDate-help\" className={helpClassName}>\n Must fall inside the buyer’s delivery window.\n </p>\n ) : null}\n </label>\n\n <label className={labelClassName}>\n Link a published product{' '}\n <span className=\"font-normal text-text-muted\">(optional)</span>\n <select\n value={form.storeProductId}\n onChange={updateField('storeProductId')}\n disabled={productsLoading}\n aria-describedby={\n productsError ? 'store-product-error' : 'store-product-help'\n }\n className={inputClassName}\n >\n <option value=\"\">\n {productsLoading\n ? 'Loading published products…'\n : 'No linked Store product'}\n </option>\n {publishedProducts.map((product) => (\n <option key={product.id} value={product.id}>\n {product.name}\n </option>\n ))}\n </select>\n {productsError ? (\n <div\n id=\"store-product-error\"\n role=\"alert\"\n className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-status-error-text\"\n >\n <span>{productsError}</span>\n <button\n type=\"button\"\n onClick={() => void refreshProducts()}\n className=\"font-semibold underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg\"\n >\n Try again\n </button>\n </div>\n ) : (\n <p id=\"store-product-help\" className={helpClassName}>\n Only products owned by this publisher and currently published are\n accepted. The link is reference metadata and does not reserve catalog\n inventory or create digital entitlements.\n </p>\n )}\n </label>\n\n <label className={labelClassName}>\n Message <span className=\"font-normal text-text-muted\">(optional)</span>\n <textarea\n value={form.message}\n onChange={updateField('message')}\n maxLength={1000}\n rows={4}\n aria-invalid={Boolean(errors.message)}\n aria-describedby={errors.message ? 'message-error' : 'message-help'}\n placeholder=\"Add packaging, quality, or delivery notes relevant to this offer.\"\n className={`${inputClassName} resize-y`}\n />\n {fieldError('message')}\n {!errors.message ? (\n <p id=\"message-help\" className={helpClassName}>\n {form.message.length}/1,000 characters\n </p>\n ) : null}\n </label>\n </div>\n\n <button\n type=\"submit\"\n disabled={submitting}\n className={`${primaryButtonClassName} mt-6 w-full`}\n >\n {submitting ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : offer ? (\n <Save className=\"size-4\" aria-hidden=\"true\" />\n ) : (\n <TicketCheck className=\"size-4\" aria-hidden=\"true\" />\n )}\n {submitting ? 'Saving offer…' : offer ? 'Update offer' : 'Submit offer'}\n </button>\n </GlassCard>\n </form>\n ) : offer ? (\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Your offer</h2>\n <p className=\"mt-1 text-xs text-text-muted\">\n Updated {formatDateTime(offer.updatedAt)}\n </p>\n </div>\n <SourcingStatusBadge status={offer.status} />\n </div>\n <dl className=\"mt-5 space-y-4 text-sm\">\n <div>\n <dt className=\"text-xs text-text-muted\">Unit price</dt>\n <dd className=\"mt-0.5 text-lg font-semibold text-text-primary\">\n {formatMoney(offer.unitPrice, offer.currency)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-muted\">Promised delivery</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(offer.deliveryDate)}\n </dd>\n </div>\n {offer.message ? (\n <div>\n <dt className=\"text-xs text-text-muted\">Message</dt>\n <dd className=\"mt-1 whitespace-pre-wrap text-text-secondary\">\n {offer.message}\n </dd>\n </div>\n ) : null}\n </dl>\n </GlassCard>\n ) : (\n <ContextRequiredPanel\n title=\"This request is closed\"\n description=\"The buyer matched or canceled this request before your publisher submitted an offer.\"\n />\n )}\n </div>\n </>\n ) : null}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -64,7 +64,7 @@ var O = () => {
|
|
|
64
64
|
}
|
|
65
65
|
}, $ = ve ?? F?.acceptedOrder ?? null;
|
|
66
66
|
return /* @__PURE__ */ T("div", {
|
|
67
|
-
className: "min-h-full bg-surface
|
|
67
|
+
className: "min-h-full bg-surface",
|
|
68
68
|
children: [
|
|
69
69
|
/* @__PURE__ */ w(re, {
|
|
70
70
|
title: F?.productName ?? "Sourcing request",
|
|
@@ -136,7 +136,7 @@ var O = () => {
|
|
|
136
136
|
}),
|
|
137
137
|
V ? /* @__PURE__ */ w("div", {
|
|
138
138
|
role: "alert",
|
|
139
|
-
className: "mb-5 rounded-lg border border-border
|
|
139
|
+
className: "mb-5 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
140
140
|
children: V
|
|
141
141
|
}) : null,
|
|
142
142
|
$ ? /* @__PURE__ */ w(ge, {
|
|
@@ -197,7 +197,7 @@ var O = () => {
|
|
|
197
197
|
/* @__PURE__ */ T("div", {
|
|
198
198
|
className: "flex flex-wrap items-start justify-between gap-3",
|
|
199
199
|
children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
|
|
200
|
-
className: "text-sm font-medium text-
|
|
200
|
+
className: "text-sm font-medium text-accent-primary",
|
|
201
201
|
children: F.category
|
|
202
202
|
}), /* @__PURE__ */ w("h2", {
|
|
203
203
|
className: "mt-1 text-xl font-semibold text-text-primary",
|
|
@@ -209,15 +209,15 @@ var O = () => {
|
|
|
209
209
|
children: F.summary
|
|
210
210
|
}) : null,
|
|
211
211
|
/* @__PURE__ */ T("dl", {
|
|
212
|
-
className: "mt-6 grid gap-5 border-t border-border-
|
|
212
|
+
className: "mt-6 grid gap-5 border-t border-border-subtle pt-5 sm:grid-cols-2",
|
|
213
213
|
children: [
|
|
214
214
|
/* @__PURE__ */ T("div", {
|
|
215
215
|
className: "flex gap-3",
|
|
216
216
|
children: [/* @__PURE__ */ w(fe, {
|
|
217
|
-
className: "mt-0.5 size-5 shrink-0 text-text-
|
|
217
|
+
className: "mt-0.5 size-5 shrink-0 text-text-muted",
|
|
218
218
|
"aria-hidden": "true"
|
|
219
219
|
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
220
|
-
className: "text-xs text-text-
|
|
220
|
+
className: "text-xs text-text-muted",
|
|
221
221
|
children: "Quantity"
|
|
222
222
|
}), /* @__PURE__ */ T("dd", {
|
|
223
223
|
className: "mt-0.5 font-medium text-text-primary",
|
|
@@ -231,10 +231,10 @@ var O = () => {
|
|
|
231
231
|
/* @__PURE__ */ T("div", {
|
|
232
232
|
className: "flex gap-3",
|
|
233
233
|
children: [/* @__PURE__ */ w(de, {
|
|
234
|
-
className: "mt-0.5 size-5 shrink-0 text-text-
|
|
234
|
+
className: "mt-0.5 size-5 shrink-0 text-text-muted",
|
|
235
235
|
"aria-hidden": "true"
|
|
236
236
|
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
237
|
-
className: "text-xs text-text-
|
|
237
|
+
className: "text-xs text-text-muted",
|
|
238
238
|
children: "Target unit price"
|
|
239
239
|
}), /* @__PURE__ */ w("dd", {
|
|
240
240
|
className: "mt-0.5 font-medium text-text-primary",
|
|
@@ -244,10 +244,10 @@ var O = () => {
|
|
|
244
244
|
/* @__PURE__ */ T("div", {
|
|
245
245
|
className: "flex gap-3",
|
|
246
246
|
children: [/* @__PURE__ */ w(le, {
|
|
247
|
-
className: "mt-0.5 size-5 shrink-0 text-text-
|
|
247
|
+
className: "mt-0.5 size-5 shrink-0 text-text-muted",
|
|
248
248
|
"aria-hidden": "true"
|
|
249
249
|
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
250
|
-
className: "text-xs text-text-
|
|
250
|
+
className: "text-xs text-text-muted",
|
|
251
251
|
children: "Delivery window"
|
|
252
252
|
}), /* @__PURE__ */ T("dd", {
|
|
253
253
|
className: "mt-0.5 font-medium text-text-primary",
|
|
@@ -262,10 +262,10 @@ var O = () => {
|
|
|
262
262
|
/* @__PURE__ */ T("div", {
|
|
263
263
|
className: "flex gap-3",
|
|
264
264
|
children: [/* @__PURE__ */ w(x, {
|
|
265
|
-
className: "mt-0.5 size-5 shrink-0 text-text-
|
|
265
|
+
className: "mt-0.5 size-5 shrink-0 text-text-muted",
|
|
266
266
|
"aria-hidden": "true"
|
|
267
267
|
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
268
|
-
className: "text-xs text-text-
|
|
268
|
+
className: "text-xs text-text-muted",
|
|
269
269
|
children: "Destination"
|
|
270
270
|
}), /* @__PURE__ */ w("dd", {
|
|
271
271
|
className: "mt-0.5 font-medium text-text-primary",
|
|
@@ -288,28 +288,28 @@ var O = () => {
|
|
|
288
288
|
className: "mt-4 space-y-4 text-sm",
|
|
289
289
|
children: [
|
|
290
290
|
/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
291
|
-
className: "text-text-
|
|
291
|
+
className: "text-text-muted",
|
|
292
292
|
children: "Created"
|
|
293
293
|
}), /* @__PURE__ */ w("dd", {
|
|
294
294
|
className: "font-medium text-text-primary",
|
|
295
295
|
children: c(F.createdAt)
|
|
296
296
|
})] }),
|
|
297
297
|
/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
298
|
-
className: "text-text-
|
|
298
|
+
className: "text-text-muted",
|
|
299
299
|
children: "Last updated"
|
|
300
300
|
}), /* @__PURE__ */ w("dd", {
|
|
301
301
|
className: "font-medium text-text-primary",
|
|
302
302
|
children: c(F.updatedAt)
|
|
303
303
|
})] }),
|
|
304
304
|
F.matchedAt ? /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
305
|
-
className: "text-text-
|
|
305
|
+
className: "text-text-muted",
|
|
306
306
|
children: "Matched"
|
|
307
307
|
}), /* @__PURE__ */ w("dd", {
|
|
308
308
|
className: "font-medium text-text-primary",
|
|
309
309
|
children: c(F.matchedAt)
|
|
310
310
|
})] }) : null,
|
|
311
311
|
F.canceledAt ? /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
312
|
-
className: "text-text-
|
|
312
|
+
className: "text-text-muted",
|
|
313
313
|
children: "Canceled"
|
|
314
314
|
}), /* @__PURE__ */ w("dd", {
|
|
315
315
|
className: "font-medium text-text-primary",
|
|
@@ -365,7 +365,7 @@ var O = () => {
|
|
|
365
365
|
alt: "",
|
|
366
366
|
className: "size-11 shrink-0 rounded-lg object-cover"
|
|
367
367
|
}) : /* @__PURE__ */ w("span", {
|
|
368
|
-
className: "flex size-11 shrink-0 items-center justify-center rounded-lg bg-surface-
|
|
368
|
+
className: "flex size-11 shrink-0 items-center justify-center rounded-lg bg-surface-sunken text-text-muted",
|
|
369
369
|
children: /* @__PURE__ */ w(he, {
|
|
370
370
|
className: "size-5",
|
|
371
371
|
"aria-hidden": "true"
|
|
@@ -378,11 +378,11 @@ var O = () => {
|
|
|
378
378
|
className: "truncate font-semibold text-text-primary",
|
|
379
379
|
children: e.publisher?.name ?? "Verified publisher"
|
|
380
380
|
}), e.publisher?.isVerified ? /* @__PURE__ */ w(ce, {
|
|
381
|
-
className: "size-4 shrink-0 text-
|
|
381
|
+
className: "size-4 shrink-0 text-accent-primary",
|
|
382
382
|
"aria-label": "Verified publisher"
|
|
383
383
|
}) : null]
|
|
384
384
|
}), /* @__PURE__ */ T("p", {
|
|
385
|
-
className: "text-xs text-text-
|
|
385
|
+
className: "text-xs text-text-muted",
|
|
386
386
|
children: ["Offered ", s(e.createdAt)]
|
|
387
387
|
})]
|
|
388
388
|
})]
|
|
@@ -391,13 +391,13 @@ var O = () => {
|
|
|
391
391
|
/* @__PURE__ */ T("dl", {
|
|
392
392
|
className: "mt-5 grid grid-cols-2 gap-4 text-sm",
|
|
393
393
|
children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
394
|
-
className: "text-xs text-text-
|
|
394
|
+
className: "text-xs text-text-muted",
|
|
395
395
|
children: "Unit price"
|
|
396
396
|
}), /* @__PURE__ */ w("dd", {
|
|
397
397
|
className: "mt-0.5 text-lg font-semibold text-text-primary",
|
|
398
398
|
children: l(e.unitPrice, e.currency)
|
|
399
399
|
})] }), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("dt", {
|
|
400
|
-
className: "text-xs text-text-
|
|
400
|
+
className: "text-xs text-text-muted",
|
|
401
401
|
children: "Delivery date"
|
|
402
402
|
}), /* @__PURE__ */ w("dd", {
|
|
403
403
|
className: "mt-0.5 font-medium text-text-primary",
|
|
@@ -405,7 +405,7 @@ var O = () => {
|
|
|
405
405
|
})] })]
|
|
406
406
|
}),
|
|
407
407
|
e.message ? /* @__PURE__ */ w("p", {
|
|
408
|
-
className: "mt-4 rounded-lg bg-surface-
|
|
408
|
+
className: "mt-4 rounded-lg bg-surface-sunken p-3 text-sm leading-6 text-text-secondary",
|
|
409
409
|
children: e.message
|
|
410
410
|
}) : null,
|
|
411
411
|
t ? /* @__PURE__ */ T("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SourcingRequestDetailPage.js","names":[],"sources":["../../src/pages/SourcingRequestDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router';\nimport {\n BadgeCheck,\n CalendarDays,\n Check,\n CircleDollarSign,\n FileText,\n MapPin,\n PackageSearch,\n Receipt,\n ShoppingBag,\n Trash2,\n UserRound,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { SourcingOfferStatus, SourcingRequestStatus } from '../generated/global-types';\nimport { useStore } from '../providers/StoreProvider';\nimport {\n type AcceptedSourcingOrder,\n type BuyerSourcingRequestDetail,\n useSourcingApi,\n} from '../hooks/useSourcing';\nimport {\n ContextRequiredPanel,\n dangerButtonClassName,\n formatDate,\n formatDateTime,\n formatLocation,\n formatMoney,\n formatSourcingUnit,\n joinStorePath,\n primaryButtonClassName,\n secondaryButtonClassName,\n SourcingEmptyState,\n SourcingErrorState,\n SourcingLoadingState,\n SourcingPageHeader,\n SourcingStatusBadge,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\nimport { SourcingConfirmationDialog } from '../components/sourcing/SourcingConfirmationDialog';\n\nexport const SourcingRequestDetailPage: FC = () => {\n const navigate = useNavigate();\n const { requestId } = useParams<{ requestId: string }>();\n const { basePath } = useStore();\n const { hasOrganizationContext, getBuyerRequest, acceptOffer, cancelBuyerRequest } =\n useSourcingApi();\n const [request, setRequest] = useState<BuyerSourcingRequestDetail | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmOfferId, setConfirmOfferId] = useState<string | null>(null);\n const [confirmCancel, setConfirmCancel] = useState(false);\n const [acting, setActing] = useState(false);\n const [acceptedOrder, setAcceptedOrder] = useState<AcceptedSourcingOrder | null>(null);\n const acceptTriggerRef = useRef<HTMLButtonElement | null>(null);\n\n const listPath = joinStorePath(basePath, 'sourcing/requests');\n\n const load = useCallback(async () => {\n if (!hasOrganizationContext || !requestId) return;\n setLoading(true);\n setError(null);\n try {\n const result = await getBuyerRequest(requestId);\n setRequest(result);\n if (!result) setError('This sourcing request was not found in the active organization.');\n } catch (loadError) {\n setError(toErrorMessage(loadError, 'The sourcing request could not be loaded.'));\n } finally {\n setLoading(false);\n }\n }, [getBuyerRequest, hasOrganizationContext, requestId]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const closeOfferConfirmation = () => {\n setConfirmOfferId(null);\n window.requestAnimationFrame(() => acceptTriggerRef.current?.focus());\n };\n\n const offers = useMemo(() => {\n if (!request) return [];\n const rank: Record<SourcingOfferStatus, number> = {\n [SourcingOfferStatus.Accepted]: 0,\n [SourcingOfferStatus.Active]: 1,\n [SourcingOfferStatus.Rejected]: 2,\n };\n return [...request.offers].sort((left, right) => rank[left.status] - rank[right.status]);\n }, [request]);\n\n const confirmingOffer = confirmOfferId\n ? offers.find((offer) => offer.id === confirmOfferId)\n : undefined;\n\n const handleAccept = async (offerId: string) => {\n if (acting) return;\n setActing(true);\n setActionError(null);\n try {\n const order = await acceptOffer(offerId);\n setAcceptedOrder(order);\n setConfirmOfferId(null);\n await load();\n } catch (acceptError) {\n closeOfferConfirmation();\n setActionError(\n toErrorMessage(\n acceptError,\n 'This offer could not be accepted. The request may already have been matched.'\n )\n );\n } finally {\n setActing(false);\n }\n };\n\n const handleCancel = async () => {\n if (!request || acting) return;\n setActing(true);\n setActionError(null);\n try {\n await cancelBuyerRequest(request.id);\n setConfirmCancel(false);\n await load();\n } catch (cancelError) {\n setConfirmCancel(false);\n setActionError(toErrorMessage(cancelError, 'This sourcing request could not be canceled.'));\n } finally {\n setActing(false);\n }\n };\n\n const order = acceptedOrder ?? request?.acceptedOrder ?? null;\n\n return (\n <div className=\"min-h-full bg-surface-default\">\n <SourcingPageHeader\n title={request?.productName ?? 'Sourcing request'}\n description=\"Review request details and compare offers visible only to the owning buyer organization.\"\n icon={<FileText className=\"size-5\" aria-hidden=\"true\" />}\n onBack={() => navigate(listPath)}\n actions={\n request?.status === SourcingRequestStatus.Open ? (\n <button\n type=\"button\"\n onClick={() => setConfirmCancel(true)}\n disabled={acting}\n className={dangerButtonClassName}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Cancel request\n </button>\n ) : undefined\n }\n />\n\n <SourcingConfirmationDialog\n open={confirmCancel}\n title=\"Cancel this open request?\"\n description=\"Publishers will no longer be able to discover it or submit offers. Existing active offers will be rejected. This action cannot be undone.\"\n confirmLabel=\"Confirm cancellation\"\n cancelLabel=\"Keep request open\"\n busy={acting}\n destructive\n onConfirm={() => void handleCancel()}\n onCancel={() => setConfirmCancel(false)}\n />\n\n <SourcingConfirmationDialog\n open={Boolean(confirmingOffer)}\n title=\"Accept this offer?\"\n description={\n confirmingOffer ? (\n <>\n Accept {confirmingOffer.publisher?.name ?? 'this verified publisher'}’s offer for{' '}\n {confirmingOffer.quantity.toLocaleString()} {formatSourcingUnit(confirmingOffer.unit)}{' '}\n at {formatMoney(confirmingOffer.unitPrice, confirmingOffer.currency)} per{' '}\n {formatSourcingUnit(confirmingOffer.unit)}? Store will calculate the authoritative\n total from this persisted offer, create one unpaid order, and reject all competing\n offers. You cannot choose another offer afterward.\n </>\n ) : null\n }\n confirmLabel=\"Confirm and create order\"\n cancelLabel=\"Not yet\"\n busy={acting}\n onConfirm={() => {\n if (confirmingOffer) void handleAccept(confirmingOffer.id);\n }}\n onCancel={closeOfferConfirmation}\n />\n\n <main className=\"mx-auto max-w-6xl px-4 py-6 sm:px-6\">\n {!hasOrganizationContext ? (\n <ContextRequiredPanel\n title=\"Choose the request's organization\"\n description=\"Select the global organization that owns this sourcing request. A farm workspace is not required.\"\n />\n ) : loading && !request ? (\n <SourcingLoadingState label=\"Loading request and offers…\" />\n ) : error && !request ? (\n <SourcingErrorState\n title=\"Sourcing request unavailable\"\n message={error}\n onRetry={requestId ? () => void load() : undefined}\n />\n ) : request ? (\n <>\n <PagePurpose className=\"mb-6\">\n Compare the seller's unit price, promised delivery date, and message. Acceptance is\n atomic: Store claims this open request, creates exactly one order from the persisted\n offer, accepts the winner, and rejects every sibling offer.\n </PagePurpose>\n\n {actionError ? (\n <div\n role=\"alert\"\n className=\"mb-5 rounded-lg border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {actionError}\n </div>\n ) : null}\n\n {order ? (\n <EmphasisPanel className=\"mb-6\">\n <div\n aria-live=\"polite\"\n className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <Check className=\"size-5\" aria-hidden=\"true\" />\n </span>\n <div>\n <h2 className=\"font-semibold text-text-primary\">\n Offer accepted and order created\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Order {order.id} · {formatMoney(order.total, order.currency)} ·{' '}\n {order.status}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {order.status === 'PENDING' ? (\n <button\n type=\"button\"\n onClick={() => navigate(`/billing/checkout/store/${order.id}`)}\n className={primaryButtonClassName}\n >\n <ShoppingBag className=\"size-4\" aria-hidden=\"true\" />\n Continue to checkout\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={() => navigate(joinStorePath(basePath, 'orders'))}\n className={secondaryButtonClassName}\n >\n <Receipt className=\"size-4\" aria-hidden=\"true\" />\n View my orders\n </button>\n </div>\n </div>\n </EmphasisPanel>\n ) : null}\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]\">\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-medium text-text-accent\">{request.category}</p>\n <h2 className=\"mt-1 text-xl font-semibold text-text-primary\">\n {request.productName}\n </h2>\n </div>\n <SourcingStatusBadge status={request.status} />\n </div>\n {request.summary ? (\n <p className=\"mt-4 whitespace-pre-wrap text-sm leading-6 text-text-secondary\">\n {request.summary}\n </p>\n ) : null}\n\n <dl className=\"mt-6 grid gap-5 border-t border-border-seam pt-5 sm:grid-cols-2\">\n <div className=\"flex gap-3\">\n <PackageSearch\n className=\"mt-0.5 size-5 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=\"mt-0.5 font-medium text-text-primary\">\n {request.quantity.toLocaleString()} {formatSourcingUnit(request.unit)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CircleDollarSign\n className=\"mt-0.5 size-5 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Target unit price</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatMoney(request.targetUnitPrice, request.targetCurrency)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CalendarDays\n className=\"mt-0.5 size-5 shrink-0 text-text-tertiary\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-tertiary\">Delivery window</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(request.deliveryWindowStart)} –{' '}\n {formatDateTime(request.deliveryWindowEnd)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <MapPin\n className=\"mt-0.5 size-5 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=\"mt-0.5 font-medium text-text-primary\">\n {formatLocation({\n country: request.deliveryCountry,\n region: request.deliveryRegion,\n city: request.deliveryCity,\n })}\n </dd>\n </div>\n </div>\n </dl>\n </GlassCard>\n\n <GlassCard className=\"p-5\">\n <h2 className=\"font-semibold text-text-primary\">Request timeline</h2>\n <dl className=\"mt-4 space-y-4 text-sm\">\n <div>\n <dt className=\"text-text-tertiary\">Created</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-tertiary\">Last updated</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.updatedAt)}\n </dd>\n </div>\n {request.matchedAt ? (\n <div>\n <dt className=\"text-text-tertiary\">Matched</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.matchedAt)}\n </dd>\n </div>\n ) : null}\n {request.canceledAt ? (\n <div>\n <dt className=\"text-text-tertiary\">Canceled</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.canceledAt)}\n </dd>\n </div>\n ) : null}\n </dl>\n </GlassCard>\n </div>\n\n <section aria-labelledby=\"sourcing-offers-heading\" className=\"mt-8\">\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <h2\n id=\"sourcing-offers-heading\"\n className=\"text-xl font-semibold text-text-primary\"\n >\n Publisher offers\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {offers.length} {offers.length === 1 ? 'offer' : 'offers'} received\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading || acting}\n className={secondaryButtonClassName}\n >\n Refresh offers\n </button>\n </div>\n\n {offers.length === 0 ? (\n <div className=\"mt-5\">\n <SourcingEmptyState\n title={\n request.status === SourcingRequestStatus.Open\n ? 'No offers yet'\n : 'No offers were received'\n }\n description={\n request.status === SourcingRequestStatus.Open\n ? 'Eligible verified publishers can discover this request while it remains open.'\n : 'This request closed without any publisher offers.'\n }\n />\n </div>\n ) : (\n <ul className=\"mt-5 grid gap-4 lg:grid-cols-2\">\n {offers.map((offer) => {\n const canAccept =\n request.status === SourcingRequestStatus.Open &&\n offer.status === SourcingOfferStatus.Active;\n return (\n <li key={offer.id}>\n <GlassCard className=\"h-full p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n {offer.publisher?.logoUrl ? (\n <img\n src={offer.publisher.logoUrl}\n alt=\"\"\n className=\"size-11 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <span className=\"flex size-11 shrink-0 items-center justify-center rounded-lg bg-surface-muted text-text-tertiary\">\n <UserRound className=\"size-5\" aria-hidden=\"true\" />\n </span>\n )}\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate font-semibold text-text-primary\">\n {offer.publisher?.name ?? 'Verified publisher'}\n </h3>\n {offer.publisher?.isVerified ? (\n <BadgeCheck\n className=\"size-4 shrink-0 text-text-accent\"\n aria-label=\"Verified publisher\"\n />\n ) : null}\n </div>\n <p className=\"text-xs text-text-tertiary\">\n Offered {formatDate(offer.createdAt)}\n </p>\n </div>\n </div>\n <SourcingStatusBadge status={offer.status} />\n </div>\n\n <dl className=\"mt-5 grid grid-cols-2 gap-4 text-sm\">\n <div>\n <dt className=\"text-xs text-text-tertiary\">Unit price</dt>\n <dd className=\"mt-0.5 text-lg font-semibold text-text-primary\">\n {formatMoney(offer.unitPrice, offer.currency)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-tertiary\">Delivery date</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(offer.deliveryDate)}\n </dd>\n </div>\n </dl>\n\n {offer.message ? (\n <p className=\"mt-4 rounded-lg bg-surface-muted p-3 text-sm leading-6 text-text-secondary\">\n {offer.message}\n </p>\n ) : null}\n\n {canAccept ? (\n <button\n type=\"button\"\n onClick={(event) => {\n acceptTriggerRef.current = event.currentTarget;\n setActionError(null);\n setConfirmOfferId(offer.id);\n }}\n className={`${primaryButtonClassName} mt-5 w-full`}\n >\n <ShoppingBag className=\"size-4\" aria-hidden=\"true\" />\n Accept offer\n </button>\n ) : offer.order ? (\n <div className=\"mt-5 rounded-lg border border-status-success-border bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Order {offer.order.id} ·{' '}\n {formatMoney(offer.order.total, offer.order.currency)}\n </div>\n ) : null}\n </GlassCard>\n </li>\n );\n })}\n </ul>\n )}\n </section>\n </>\n ) : null}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SourcingRequestDetailPage.js","names":[],"sources":["../../src/pages/SourcingRequestDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router';\nimport {\n BadgeCheck,\n CalendarDays,\n Check,\n CircleDollarSign,\n FileText,\n MapPin,\n PackageSearch,\n Receipt,\n ShoppingBag,\n Trash2,\n UserRound,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { SourcingOfferStatus, SourcingRequestStatus } from '../generated/global-types';\nimport { useStore } from '../providers/StoreProvider';\nimport {\n type AcceptedSourcingOrder,\n type BuyerSourcingRequestDetail,\n useSourcingApi,\n} from '../hooks/useSourcing';\nimport {\n ContextRequiredPanel,\n dangerButtonClassName,\n formatDate,\n formatDateTime,\n formatLocation,\n formatMoney,\n formatSourcingUnit,\n joinStorePath,\n primaryButtonClassName,\n secondaryButtonClassName,\n SourcingEmptyState,\n SourcingErrorState,\n SourcingLoadingState,\n SourcingPageHeader,\n SourcingStatusBadge,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\nimport { SourcingConfirmationDialog } from '../components/sourcing/SourcingConfirmationDialog';\n\nexport const SourcingRequestDetailPage: FC = () => {\n const navigate = useNavigate();\n const { requestId } = useParams<{ requestId: string }>();\n const { basePath } = useStore();\n const { hasOrganizationContext, getBuyerRequest, acceptOffer, cancelBuyerRequest } =\n useSourcingApi();\n const [request, setRequest] = useState<BuyerSourcingRequestDetail | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmOfferId, setConfirmOfferId] = useState<string | null>(null);\n const [confirmCancel, setConfirmCancel] = useState(false);\n const [acting, setActing] = useState(false);\n const [acceptedOrder, setAcceptedOrder] = useState<AcceptedSourcingOrder | null>(null);\n const acceptTriggerRef = useRef<HTMLButtonElement | null>(null);\n\n const listPath = joinStorePath(basePath, 'sourcing/requests');\n\n const load = useCallback(async () => {\n if (!hasOrganizationContext || !requestId) return;\n setLoading(true);\n setError(null);\n try {\n const result = await getBuyerRequest(requestId);\n setRequest(result);\n if (!result) setError('This sourcing request was not found in the active organization.');\n } catch (loadError) {\n setError(toErrorMessage(loadError, 'The sourcing request could not be loaded.'));\n } finally {\n setLoading(false);\n }\n }, [getBuyerRequest, hasOrganizationContext, requestId]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const closeOfferConfirmation = () => {\n setConfirmOfferId(null);\n window.requestAnimationFrame(() => acceptTriggerRef.current?.focus());\n };\n\n const offers = useMemo(() => {\n if (!request) return [];\n const rank: Record<SourcingOfferStatus, number> = {\n [SourcingOfferStatus.Accepted]: 0,\n [SourcingOfferStatus.Active]: 1,\n [SourcingOfferStatus.Rejected]: 2,\n };\n return [...request.offers].sort((left, right) => rank[left.status] - rank[right.status]);\n }, [request]);\n\n const confirmingOffer = confirmOfferId\n ? offers.find((offer) => offer.id === confirmOfferId)\n : undefined;\n\n const handleAccept = async (offerId: string) => {\n if (acting) return;\n setActing(true);\n setActionError(null);\n try {\n const order = await acceptOffer(offerId);\n setAcceptedOrder(order);\n setConfirmOfferId(null);\n await load();\n } catch (acceptError) {\n closeOfferConfirmation();\n setActionError(\n toErrorMessage(\n acceptError,\n 'This offer could not be accepted. The request may already have been matched.'\n )\n );\n } finally {\n setActing(false);\n }\n };\n\n const handleCancel = async () => {\n if (!request || acting) return;\n setActing(true);\n setActionError(null);\n try {\n await cancelBuyerRequest(request.id);\n setConfirmCancel(false);\n await load();\n } catch (cancelError) {\n setConfirmCancel(false);\n setActionError(toErrorMessage(cancelError, 'This sourcing request could not be canceled.'));\n } finally {\n setActing(false);\n }\n };\n\n const order = acceptedOrder ?? request?.acceptedOrder ?? null;\n\n return (\n <div className=\"min-h-full bg-surface\">\n <SourcingPageHeader\n title={request?.productName ?? 'Sourcing request'}\n description=\"Review request details and compare offers visible only to the owning buyer organization.\"\n icon={<FileText className=\"size-5\" aria-hidden=\"true\" />}\n onBack={() => navigate(listPath)}\n actions={\n request?.status === SourcingRequestStatus.Open ? (\n <button\n type=\"button\"\n onClick={() => setConfirmCancel(true)}\n disabled={acting}\n className={dangerButtonClassName}\n >\n <Trash2 className=\"size-4\" aria-hidden=\"true\" />\n Cancel request\n </button>\n ) : undefined\n }\n />\n\n <SourcingConfirmationDialog\n open={confirmCancel}\n title=\"Cancel this open request?\"\n description=\"Publishers will no longer be able to discover it or submit offers. Existing active offers will be rejected. This action cannot be undone.\"\n confirmLabel=\"Confirm cancellation\"\n cancelLabel=\"Keep request open\"\n busy={acting}\n destructive\n onConfirm={() => void handleCancel()}\n onCancel={() => setConfirmCancel(false)}\n />\n\n <SourcingConfirmationDialog\n open={Boolean(confirmingOffer)}\n title=\"Accept this offer?\"\n description={\n confirmingOffer ? (\n <>\n Accept {confirmingOffer.publisher?.name ?? 'this verified publisher'}’s offer for{' '}\n {confirmingOffer.quantity.toLocaleString()} {formatSourcingUnit(confirmingOffer.unit)}{' '}\n at {formatMoney(confirmingOffer.unitPrice, confirmingOffer.currency)} per{' '}\n {formatSourcingUnit(confirmingOffer.unit)}? Store will calculate the authoritative\n total from this persisted offer, create one unpaid order, and reject all competing\n offers. You cannot choose another offer afterward.\n </>\n ) : null\n }\n confirmLabel=\"Confirm and create order\"\n cancelLabel=\"Not yet\"\n busy={acting}\n onConfirm={() => {\n if (confirmingOffer) void handleAccept(confirmingOffer.id);\n }}\n onCancel={closeOfferConfirmation}\n />\n\n <main className=\"mx-auto max-w-6xl px-4 py-6 sm:px-6\">\n {!hasOrganizationContext ? (\n <ContextRequiredPanel\n title=\"Choose the request's organization\"\n description=\"Select the global organization that owns this sourcing request. A farm workspace is not required.\"\n />\n ) : loading && !request ? (\n <SourcingLoadingState label=\"Loading request and offers…\" />\n ) : error && !request ? (\n <SourcingErrorState\n title=\"Sourcing request unavailable\"\n message={error}\n onRetry={requestId ? () => void load() : undefined}\n />\n ) : request ? (\n <>\n <PagePurpose className=\"mb-6\">\n Compare the seller's unit price, promised delivery date, and message. Acceptance is\n atomic: Store claims this open request, creates exactly one order from the persisted\n offer, accepts the winner, and rejects every sibling offer.\n </PagePurpose>\n\n {actionError ? (\n <div\n role=\"alert\"\n className=\"mb-5 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {actionError}\n </div>\n ) : null}\n\n {order ? (\n <EmphasisPanel className=\"mb-6\">\n <div\n aria-live=\"polite\"\n className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <Check className=\"size-5\" aria-hidden=\"true\" />\n </span>\n <div>\n <h2 className=\"font-semibold text-text-primary\">\n Offer accepted and order created\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Order {order.id} · {formatMoney(order.total, order.currency)} ·{' '}\n {order.status}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {order.status === 'PENDING' ? (\n <button\n type=\"button\"\n onClick={() => navigate(`/billing/checkout/store/${order.id}`)}\n className={primaryButtonClassName}\n >\n <ShoppingBag className=\"size-4\" aria-hidden=\"true\" />\n Continue to checkout\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={() => navigate(joinStorePath(basePath, 'orders'))}\n className={secondaryButtonClassName}\n >\n <Receipt className=\"size-4\" aria-hidden=\"true\" />\n View my orders\n </button>\n </div>\n </div>\n </EmphasisPanel>\n ) : null}\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]\">\n <GlassCard className=\"p-5 sm:p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-medium text-accent-primary\">{request.category}</p>\n <h2 className=\"mt-1 text-xl font-semibold text-text-primary\">\n {request.productName}\n </h2>\n </div>\n <SourcingStatusBadge status={request.status} />\n </div>\n {request.summary ? (\n <p className=\"mt-4 whitespace-pre-wrap text-sm leading-6 text-text-secondary\">\n {request.summary}\n </p>\n ) : null}\n\n <dl className=\"mt-6 grid gap-5 border-t border-border-subtle pt-5 sm:grid-cols-2\">\n <div className=\"flex gap-3\">\n <PackageSearch\n className=\"mt-0.5 size-5 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-muted\">Quantity</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {request.quantity.toLocaleString()} {formatSourcingUnit(request.unit)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CircleDollarSign\n className=\"mt-0.5 size-5 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-muted\">Target unit price</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatMoney(request.targetUnitPrice, request.targetCurrency)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <CalendarDays\n className=\"mt-0.5 size-5 shrink-0 text-text-muted\"\n aria-hidden=\"true\"\n />\n <div>\n <dt className=\"text-xs text-text-muted\">Delivery window</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(request.deliveryWindowStart)} –{' '}\n {formatDateTime(request.deliveryWindowEnd)}\n </dd>\n </div>\n </div>\n <div className=\"flex gap-3\">\n <MapPin className=\"mt-0.5 size-5 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n <div>\n <dt className=\"text-xs text-text-muted\">Destination</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatLocation({\n country: request.deliveryCountry,\n region: request.deliveryRegion,\n city: request.deliveryCity,\n })}\n </dd>\n </div>\n </div>\n </dl>\n </GlassCard>\n\n <GlassCard className=\"p-5\">\n <h2 className=\"font-semibold text-text-primary\">Request timeline</h2>\n <dl className=\"mt-4 space-y-4 text-sm\">\n <div>\n <dt className=\"text-text-muted\">Created</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-muted\">Last updated</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.updatedAt)}\n </dd>\n </div>\n {request.matchedAt ? (\n <div>\n <dt className=\"text-text-muted\">Matched</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.matchedAt)}\n </dd>\n </div>\n ) : null}\n {request.canceledAt ? (\n <div>\n <dt className=\"text-text-muted\">Canceled</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDateTime(request.canceledAt)}\n </dd>\n </div>\n ) : null}\n </dl>\n </GlassCard>\n </div>\n\n <section aria-labelledby=\"sourcing-offers-heading\" className=\"mt-8\">\n <div className=\"flex flex-wrap items-end justify-between gap-3\">\n <div>\n <h2\n id=\"sourcing-offers-heading\"\n className=\"text-xl font-semibold text-text-primary\"\n >\n Publisher offers\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {offers.length} {offers.length === 1 ? 'offer' : 'offers'} received\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading || acting}\n className={secondaryButtonClassName}\n >\n Refresh offers\n </button>\n </div>\n\n {offers.length === 0 ? (\n <div className=\"mt-5\">\n <SourcingEmptyState\n title={\n request.status === SourcingRequestStatus.Open\n ? 'No offers yet'\n : 'No offers were received'\n }\n description={\n request.status === SourcingRequestStatus.Open\n ? 'Eligible verified publishers can discover this request while it remains open.'\n : 'This request closed without any publisher offers.'\n }\n />\n </div>\n ) : (\n <ul className=\"mt-5 grid gap-4 lg:grid-cols-2\">\n {offers.map((offer) => {\n const canAccept =\n request.status === SourcingRequestStatus.Open &&\n offer.status === SourcingOfferStatus.Active;\n return (\n <li key={offer.id}>\n <GlassCard className=\"h-full p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n {offer.publisher?.logoUrl ? (\n <img\n src={offer.publisher.logoUrl}\n alt=\"\"\n className=\"size-11 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <span className=\"flex size-11 shrink-0 items-center justify-center rounded-lg bg-surface-sunken text-text-muted\">\n <UserRound className=\"size-5\" aria-hidden=\"true\" />\n </span>\n )}\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate font-semibold text-text-primary\">\n {offer.publisher?.name ?? 'Verified publisher'}\n </h3>\n {offer.publisher?.isVerified ? (\n <BadgeCheck\n className=\"size-4 shrink-0 text-accent-primary\"\n aria-label=\"Verified publisher\"\n />\n ) : null}\n </div>\n <p className=\"text-xs text-text-muted\">\n Offered {formatDate(offer.createdAt)}\n </p>\n </div>\n </div>\n <SourcingStatusBadge status={offer.status} />\n </div>\n\n <dl className=\"mt-5 grid grid-cols-2 gap-4 text-sm\">\n <div>\n <dt className=\"text-xs text-text-muted\">Unit price</dt>\n <dd className=\"mt-0.5 text-lg font-semibold text-text-primary\">\n {formatMoney(offer.unitPrice, offer.currency)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs text-text-muted\">Delivery date</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDateTime(offer.deliveryDate)}\n </dd>\n </div>\n </dl>\n\n {offer.message ? (\n <p className=\"mt-4 rounded-lg bg-surface-sunken p-3 text-sm leading-6 text-text-secondary\">\n {offer.message}\n </p>\n ) : null}\n\n {canAccept ? (\n <button\n type=\"button\"\n onClick={(event) => {\n acceptTriggerRef.current = event.currentTarget;\n setActionError(null);\n setConfirmOfferId(offer.id);\n }}\n className={`${primaryButtonClassName} mt-5 w-full`}\n >\n <ShoppingBag className=\"size-4\" aria-hidden=\"true\" />\n Accept offer\n </button>\n ) : offer.order ? (\n <div className=\"mt-5 rounded-lg border border-status-success-border bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Order {offer.order.id} ·{' '}\n {formatMoney(offer.order.total, offer.order.currency)}\n </div>\n ) : null}\n </GlassCard>\n </li>\n );\n })}\n </ul>\n )}\n </section>\n </>\n ) : null}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -40,7 +40,7 @@ var P = 30, F = () => {
|
|
|
40
40
|
offers: 0
|
|
41
41
|
}), [z]), $ = p(I, "sourcing/requests/new");
|
|
42
42
|
return /* @__PURE__ */ j("div", {
|
|
43
|
-
className: "min-h-full bg-surface
|
|
43
|
+
className: "min-h-full bg-surface",
|
|
44
44
|
children: [/* @__PURE__ */ A(s, {
|
|
45
45
|
title: "Sourcing requests",
|
|
46
46
|
description: "Request products from verified Store publishers and compare private offers for your organization.",
|
|
@@ -64,7 +64,7 @@ var P = 30, F = () => {
|
|
|
64
64
|
className: "mb-6",
|
|
65
65
|
children: "Create one request for the exact quantity, delivery window, and destination you need. Offers remain private to your organization. Accepting one offer creates a Store order and closes the request; all other offers are rejected automatically."
|
|
66
66
|
}), L ? /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ j("div", {
|
|
67
|
-
className: "mb-5 flex flex-col gap-3 rounded-xl border border-border-
|
|
67
|
+
className: "mb-5 flex flex-col gap-3 rounded-xl border border-border-subtle bg-surface-elevated p-4 sm:flex-row sm:items-end sm:justify-between",
|
|
68
68
|
children: [/* @__PURE__ */ j("label", {
|
|
69
69
|
className: "block sm:max-w-xs sm:flex-1",
|
|
70
70
|
children: [/* @__PURE__ */ A("span", {
|
|
@@ -73,7 +73,7 @@ var P = 30, F = () => {
|
|
|
73
73
|
}), /* @__PURE__ */ j("select", {
|
|
74
74
|
value: V,
|
|
75
75
|
onChange: (e) => H(e.target.value),
|
|
76
|
-
className: "mt-1.5 w-full rounded-lg border border-border-default bg-surface
|
|
76
|
+
className: "mt-1.5 w-full rounded-lg border border-border-default bg-surface px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-bg focus:ring-2 focus:ring-action-primary-bg/20",
|
|
77
77
|
children: [
|
|
78
78
|
/* @__PURE__ */ A("option", {
|
|
79
79
|
value: "",
|
|
@@ -133,7 +133,7 @@ var P = 30, F = () => {
|
|
|
133
133
|
}) : /* @__PURE__ */ j(k, { children: [
|
|
134
134
|
Y ? /* @__PURE__ */ A("div", {
|
|
135
135
|
role: "alert",
|
|
136
|
-
className: "mb-4 rounded-lg border border-border
|
|
136
|
+
className: "mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
137
137
|
children: Y
|
|
138
138
|
}) : null,
|
|
139
139
|
/* @__PURE__ */ A("ul", {
|
|
@@ -148,7 +148,7 @@ var P = 30, F = () => {
|
|
|
148
148
|
children: [/* @__PURE__ */ j("div", {
|
|
149
149
|
className: "min-w-0",
|
|
150
150
|
children: [/* @__PURE__ */ j("div", {
|
|
151
|
-
className: "flex items-center gap-2 text-xs font-medium text-
|
|
151
|
+
className: "flex items-center gap-2 text-xs font-medium text-accent-primary",
|
|
152
152
|
children: [/* @__PURE__ */ A(T, {
|
|
153
153
|
className: "size-4",
|
|
154
154
|
"aria-hidden": "true"
|
|
@@ -168,10 +168,10 @@ var P = 30, F = () => {
|
|
|
168
168
|
/* @__PURE__ */ j("div", {
|
|
169
169
|
className: "flex items-start gap-2",
|
|
170
170
|
children: [/* @__PURE__ */ A(D, {
|
|
171
|
-
className: "mt-0.5 size-4 shrink-0 text-text-
|
|
171
|
+
className: "mt-0.5 size-4 shrink-0 text-text-muted",
|
|
172
172
|
"aria-hidden": "true"
|
|
173
173
|
}), /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("dt", {
|
|
174
|
-
className: "text-xs text-text-
|
|
174
|
+
className: "text-xs text-text-muted",
|
|
175
175
|
children: "Quantity"
|
|
176
176
|
}), /* @__PURE__ */ j("dd", {
|
|
177
177
|
className: "font-medium text-text-primary",
|
|
@@ -185,10 +185,10 @@ var P = 30, F = () => {
|
|
|
185
185
|
/* @__PURE__ */ j("div", {
|
|
186
186
|
className: "flex items-start gap-2",
|
|
187
187
|
children: [/* @__PURE__ */ A(E, {
|
|
188
|
-
className: "mt-0.5 size-4 shrink-0 text-text-
|
|
188
|
+
className: "mt-0.5 size-4 shrink-0 text-text-muted",
|
|
189
189
|
"aria-hidden": "true"
|
|
190
190
|
}), /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("dt", {
|
|
191
|
-
className: "text-xs text-text-
|
|
191
|
+
className: "text-xs text-text-muted",
|
|
192
192
|
children: "Deliver to"
|
|
193
193
|
}), /* @__PURE__ */ A("dd", {
|
|
194
194
|
className: "font-medium text-text-primary",
|
|
@@ -202,10 +202,10 @@ var P = 30, F = () => {
|
|
|
202
202
|
/* @__PURE__ */ j("div", {
|
|
203
203
|
className: "flex items-start gap-2 sm:col-span-2",
|
|
204
204
|
children: [/* @__PURE__ */ A(C, {
|
|
205
|
-
className: "mt-0.5 size-4 shrink-0 text-text-
|
|
205
|
+
className: "mt-0.5 size-4 shrink-0 text-text-muted",
|
|
206
206
|
"aria-hidden": "true"
|
|
207
207
|
}), /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("dt", {
|
|
208
|
-
className: "text-xs text-text-
|
|
208
|
+
className: "text-xs text-text-muted",
|
|
209
209
|
children: "Delivery window"
|
|
210
210
|
}), /* @__PURE__ */ j("dd", {
|
|
211
211
|
className: "font-medium text-text-primary",
|
|
@@ -220,7 +220,7 @@ var P = 30, F = () => {
|
|
|
220
220
|
]
|
|
221
221
|
}),
|
|
222
222
|
/* @__PURE__ */ j("div", {
|
|
223
|
-
className: "mt-5 flex flex-wrap items-center justify-between gap-3 border-t border-border-
|
|
223
|
+
className: "mt-5 flex flex-wrap items-center justify-between gap-3 border-t border-border-subtle pt-4",
|
|
224
224
|
children: [/* @__PURE__ */ j("div", { children: [/* @__PURE__ */ j("p", {
|
|
225
225
|
className: "text-sm font-medium text-text-primary",
|
|
226
226
|
children: [
|
|
@@ -229,7 +229,7 @@ var P = 30, F = () => {
|
|
|
229
229
|
t === 1 ? "offer" : "offers"
|
|
230
230
|
]
|
|
231
231
|
}), /* @__PURE__ */ j("p", {
|
|
232
|
-
className: "text-xs text-text-
|
|
232
|
+
className: "text-xs text-text-muted",
|
|
233
233
|
children: [
|
|
234
234
|
"Target",
|
|
235
235
|
" ",
|