@burdenoff/microfe-store 2026.1013.1 → 2026.1013.3
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/InstallAppModal.js +1 -1
- package/dist/components/InstallAppModal.js.map +1 -1
- package/dist/pages/AdminFulfillmentLogsPage.js +1 -1
- package/dist/pages/AdminFulfillmentLogsPage.js.map +1 -1
- package/dist/pages/AdminOrdersPage.js +1 -1
- package/dist/pages/AdminOrdersPage.js.map +1 -1
- package/dist/pages/AdminStoresPage.js +1 -1
- package/dist/pages/AdminStoresPage.js.map +1 -1
- package/dist/pages/CreateSourcingRequestPage.js +1 -1
- package/dist/pages/CreateSourcingRequestPage.js.map +1 -1
- package/dist/pages/MarketplaceHomePage.js +1 -1
- package/dist/pages/MarketplaceHomePage.js.map +1 -1
- package/dist/pages/SellerSourcingOffersPage.js +1 -1
- package/dist/pages/SellerSourcingOffersPage.js.map +1 -1
- package/dist/pages/SellerSourcingOpportunityDetailPage.js +1 -1
- package/dist/pages/SellerSourcingOpportunityDetailPage.js.map +1 -1
- package/dist/pages/SourcingRequestsPage.js +1 -1
- package/dist/pages/SourcingRequestsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminStoresPage.js","names":[],"sources":["../../src/pages/AdminStoresPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { ArrowLeft, Plus, Pencil, Trash2, RefreshCw, Store as StoreIcon } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport {\n AlertDialog,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogCancel,\n AlertDialogAction,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { MarkdownEditor } from '../components/MarkdownEditor';\n\ntype StoreRecord = {\n id: string;\n slug: string;\n name: string;\n type: 'PUBLIC' | 'PRIVATE' | 'ENTERPRISE';\n tenantId?: string | null;\n isPublic: boolean;\n requiresApproval: boolean;\n allowUnverifiedPublishers: boolean;\n currency: string;\n logoUrl?: string | null;\n description?: string | null;\n createdAt: string;\n};\n\nconst STORES_QUERY = /* GraphQL */ `\n query AdminStores {\n stores {\n id\n slug\n name\n type\n tenantId\n isPublic\n requiresApproval\n allowUnverifiedPublishers\n currency\n logoUrl\n description\n createdAt\n }\n }\n`;\n\nconst CREATE_STORE_MUTATION = /* GraphQL */ `\n mutation CreateStore($input: CreateStoreInput!) {\n createStore(input: $input) {\n id\n slug\n name\n type\n }\n }\n`;\n\nconst UPDATE_STORE_MUTATION = /* GraphQL */ `\n mutation UpdateStore($id: ID!, $input: UpdateStoreInput!) {\n updateStore(id: $id, input: $input) {\n id\n name\n isPublic\n requiresApproval\n }\n }\n`;\n\nconst DELETE_STORE_MUTATION = /* GraphQL */ `\n mutation DeleteStore($id: ID!) {\n deleteStore(id: $id)\n }\n`;\n\ntype FormState = {\n slug: string;\n name: string;\n type: StoreRecord['type'];\n tenantId: string;\n isPublic: boolean;\n requiresApproval: boolean;\n allowUnverifiedPublishers: boolean;\n currency: string;\n description: string;\n};\n\nconst emptyForm: FormState = {\n slug: '',\n name: '',\n type: 'PRIVATE',\n tenantId: '',\n isPublic: false,\n requiresApproval: false,\n allowUnverifiedPublishers: true,\n currency: 'USD',\n description: '',\n};\n\nexport const AdminStoresPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [stores, setStores] = useState<StoreRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [showForm, setShowForm] = useState(false);\n const [form, setForm] = useState<FormState>(emptyForm);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{ stores: StoreRecord[] }>({\n gateway: 'global',\n query: STORES_QUERY,\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load stores');\n }\n setStores(result.data?.stores ?? []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load stores');\n } finally {\n setLoading(false);\n }\n }, [authToken]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const openCreate = () => {\n setEditingId(null);\n setForm(emptyForm);\n setShowForm(true);\n };\n\n const openEdit = (s: StoreRecord) => {\n setEditingId(s.id);\n setForm({\n slug: s.slug,\n name: s.name,\n type: s.type,\n tenantId: s.tenantId ?? '',\n isPublic: s.isPublic,\n requiresApproval: s.requiresApproval,\n allowUnverifiedPublishers: s.allowUnverifiedPublishers,\n currency: s.currency,\n description: s.description ?? '',\n });\n setShowForm(true);\n };\n\n const submit = async () => {\n if (!form.slug.trim() || !form.name.trim()) {\n setError('Slug and name are required');\n return;\n }\n setError(null);\n try {\n if (editingId) {\n await graphqlFetch({\n gateway: 'global',\n query: UPDATE_STORE_MUTATION,\n variables: {\n id: editingId,\n input: {\n name: form.name,\n isPublic: form.isPublic,\n requiresApproval: form.requiresApproval,\n allowUnverifiedPublishers: form.allowUnverifiedPublishers,\n currency: form.currency,\n description: form.description || null,\n },\n },\n authToken: authToken || undefined,\n });\n } else {\n await graphqlFetch({\n gateway: 'global',\n query: CREATE_STORE_MUTATION,\n variables: {\n input: {\n slug: form.slug,\n name: form.name,\n type: form.type,\n tenantId: form.tenantId || null,\n isPublic: form.isPublic,\n requiresApproval: form.requiresApproval,\n allowUnverifiedPublishers: form.allowUnverifiedPublishers,\n currency: form.currency,\n description: form.description || null,\n },\n },\n authToken: authToken || undefined,\n });\n }\n setShowForm(false);\n setEditingId(null);\n setForm(emptyForm);\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Save failed');\n }\n };\n\n const remove = async (id: string) => {\n try {\n await graphqlFetch({\n gateway: 'global',\n query: DELETE_STORE_MUTATION,\n variables: { id },\n authToken: authToken || undefined,\n });\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Delete failed');\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-seam bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <StoreIcon className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Store registry</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {stores.length}\n </span>\n </div>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => void load()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-surface-accent px-3 py-1.5 text-sm font-medium text-text-on-accent hover:bg-surface-accent-hover\"\n >\n <Plus className=\"size-4\" />\n Create store\n </button>\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n <PagePurpose className=\"mb-4\">\n A store is a storefront that decides which products a given audience can see and buy. Use\n this registry to spin up a public marketplace, a tenant-scoped enterprise store, or a\n private opt-in catalog — and to control whether submissions auto-publish or wait for\n approval, and whether unverified publishers may submit. One store can power a whole\n product's app marketplace.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"store-admin-stores\"\n steps={[\n {\n id: 'create-store',\n label: 'Create your first store',\n description: 'Choose a type (public, private, or enterprise) and a slug.',\n onClick: openCreate,\n },\n {\n id: 'approval-policy',\n label: 'Set the submission policy',\n description: 'Decide if new products require approval before going live.',\n onClick: openCreate,\n },\n {\n id: 'moderate',\n label: 'Review incoming submissions',\n description: 'Head to the submission queue to approve or reject products.',\n onClick: () => navigate(`${basePath}/submissions`),\n },\n ]}\n />\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {showForm && (\n <GlassCard treatment=\"plain\" className=\"mb-6 p-4\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {editingId ? 'Edit store' : 'Create store'}\n </h2>\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Slug</span>\n <input\n type=\"text\"\n aria-label=\"Slug\"\n value={form.slug}\n onChange={(e) => setForm({ ...form, slug: e.target.value })}\n disabled={!!editingId}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary disabled:opacity-60\"\n />\n </label>\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Name</span>\n <input\n type=\"text\"\n aria-label=\"Name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </label>\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Type</span>\n <select\n aria-label=\"Store type\"\n value={form.type}\n onChange={(e) => setForm({ ...form, type: e.target.value as FormState['type'] })}\n disabled={!!editingId}\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary disabled:opacity-60\"\n >\n <option value=\"PUBLIC\">Public</option>\n <option value=\"PRIVATE\">Private (opt-in)</option>\n <option value=\"ENTERPRISE\">Enterprise (tenant-scoped)</option>\n </select>\n </label>\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Currency</span>\n <input\n type=\"text\"\n aria-label=\"Currency\"\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </label>\n {form.type !== 'PUBLIC' && (\n <label className=\"block md:col-span-2\">\n <span className=\"text-xs text-text-secondary\">Tenant ID (optional)</span>\n <input\n type=\"text\"\n aria-label=\"Tenant ID\"\n value={form.tenantId}\n onChange={(e) => setForm({ ...form, tenantId: e.target.value })}\n disabled={!!editingId}\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary disabled:opacity-60\"\n />\n </label>\n )}\n <MarkdownEditor\n className=\"md:col-span-2\"\n id=\"store-description\"\n label=\"Description\"\n value={form.description}\n onChange={(value) => setForm({ ...form, description: value })}\n rows={2}\n />\n <label className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n aria-label=\"Publicly visible\"\n checked={form.isPublic}\n onChange={(e) => setForm({ ...form, isPublic: e.target.checked })}\n />\n <span className=\"text-sm text-text-primary\">Publicly visible</span>\n </label>\n <label className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n aria-label=\"Requires admin approval\"\n checked={form.requiresApproval}\n onChange={(e) => setForm({ ...form, requiresApproval: e.target.checked })}\n />\n <span className=\"text-sm text-text-primary\">Requires admin approval</span>\n </label>\n <label className=\"flex items-center gap-2 md:col-span-2\">\n <input\n type=\"checkbox\"\n aria-label=\"Allow unverified publishers to submit\"\n checked={form.allowUnverifiedPublishers}\n onChange={(e) =>\n setForm({ ...form, allowUnverifiedPublishers: e.target.checked })\n }\n />\n <span className=\"text-sm text-text-primary\">\n Allow unverified publishers to submit\n </span>\n </label>\n </div>\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setShowForm(false);\n setEditingId(null);\n setForm(emptyForm);\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void submit()}\n className=\"rounded-md bg-surface-accent px-3 py-1.5 text-sm font-medium text-text-on-accent hover:bg-surface-accent-hover\"\n >\n {editingId ? 'Save changes' : 'Create'}\n </button>\n </div>\n </GlassCard>\n )}\n\n {loading && stores.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">Loading stores…</output>\n ) : stores.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No stores yet\"\n description=\"Create your first store to give an audience a place to discover and install products.\"\n action={\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-surface-accent px-3 py-1.5 text-sm font-medium text-text-on-accent hover:bg-surface-accent-hover\"\n >\n <Plus className=\"size-4\" />\n Create store\n </button>\n }\n />\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary\">\n <tr>\n <th className=\"px-4 py-2\">Slug</th>\n <th className=\"px-4 py-2\">Name</th>\n <th className=\"px-4 py-2\">Type</th>\n <th className=\"px-4 py-2\">Public</th>\n <th className=\"px-4 py-2\">Approval</th>\n <th className=\"px-4 py-2\">Currency</th>\n <th className=\"px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-surface-raised\">\n {stores.map((s) => (\n <tr key={s.id} className=\"text-text-primary\">\n <td className=\"px-4 py-2 font-mono text-xs\">{s.slug}</td>\n <td className=\"px-4 py-2\">{s.name}</td>\n <td className=\"px-4 py-2\">\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {s.type}\n </span>\n </td>\n <td className=\"px-4 py-2 text-xs\">{s.isPublic ? 'Yes' : 'No'}</td>\n <td className=\"px-4 py-2 text-xs\">\n {s.requiresApproval ? 'Required' : 'Auto-publish'}\n </td>\n <td className=\"px-4 py-2 text-xs\">{s.currency}</td>\n <td className=\"px-4 py-2 text-right\">\n <button\n type=\"button\"\n onClick={() => openEdit(s)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-2 py-1 text-xs text-text-primary hover:bg-surface-hover\"\n >\n <Pencil className=\"size-3\" />\n Edit\n </button>\n {s.id !== 'store_public_default' && (\n <button\n type=\"button\"\n onClick={() => setPendingDeleteId(s.id)}\n className=\"ml-2 inline-flex items-center gap-1 rounded-md border border-border-danger px-2 py-1 text-xs text-text-danger hover:bg-surface-danger-muted\"\n >\n <Trash2 className=\"size-3\" />\n Delete\n </button>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </main>\n\n <AlertDialog\n open={pendingDeleteId !== null}\n onOpenChange={(open) => !open && setPendingDeleteId(null)}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete this store?</AlertDialogTitle>\n <AlertDialogDescription>This is a soft delete.</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel onClick={() => setPendingDeleteId(null)}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-surface-danger text-text-on-danger hover:bg-surface-danger-hover\"\n onClick={() => {\n if (pendingDeleteId) void remove(pendingDeleteId);\n setPendingDeleteId(null);\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminStoresPage.js","names":[],"sources":["../../src/pages/AdminStoresPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { ArrowLeft, Plus, Pencil, Trash2, RefreshCw, Store as StoreIcon } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport {\n AlertDialog,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogCancel,\n AlertDialogAction,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { MarkdownEditor } from '../components/MarkdownEditor';\n\ntype StoreRecord = {\n id: string;\n slug: string;\n name: string;\n type: 'PUBLIC' | 'PRIVATE' | 'ENTERPRISE';\n tenantId?: string | null;\n isPublic: boolean;\n requiresApproval: boolean;\n allowUnverifiedPublishers: boolean;\n currency: string;\n logoUrl?: string | null;\n description?: string | null;\n createdAt: string;\n};\n\nconst STORES_QUERY = /* GraphQL */ `\n query AdminStores {\n stores {\n id\n slug\n name\n type\n tenantId\n isPublic\n requiresApproval\n allowUnverifiedPublishers\n currency\n logoUrl\n description\n createdAt\n }\n }\n`;\n\nconst CREATE_STORE_MUTATION = /* GraphQL */ `\n mutation CreateStore($input: CreateStoreInput!) {\n createStore(input: $input) {\n id\n slug\n name\n type\n }\n }\n`;\n\nconst UPDATE_STORE_MUTATION = /* GraphQL */ `\n mutation UpdateStore($id: ID!, $input: UpdateStoreInput!) {\n updateStore(id: $id, input: $input) {\n id\n name\n isPublic\n requiresApproval\n }\n }\n`;\n\nconst DELETE_STORE_MUTATION = /* GraphQL */ `\n mutation DeleteStore($id: ID!) {\n deleteStore(id: $id)\n }\n`;\n\ntype FormState = {\n slug: string;\n name: string;\n type: StoreRecord['type'];\n tenantId: string;\n isPublic: boolean;\n requiresApproval: boolean;\n allowUnverifiedPublishers: boolean;\n currency: string;\n description: string;\n};\n\nconst emptyForm: FormState = {\n slug: '',\n name: '',\n type: 'PRIVATE',\n tenantId: '',\n isPublic: false,\n requiresApproval: false,\n allowUnverifiedPublishers: true,\n currency: 'USD',\n description: '',\n};\n\nexport const AdminStoresPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [stores, setStores] = useState<StoreRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [showForm, setShowForm] = useState(false);\n const [form, setForm] = useState<FormState>(emptyForm);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{ stores: StoreRecord[] }>({\n gateway: 'global',\n query: STORES_QUERY,\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load stores');\n }\n setStores(result.data?.stores ?? []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load stores');\n } finally {\n setLoading(false);\n }\n }, [authToken]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const openCreate = () => {\n setEditingId(null);\n setForm(emptyForm);\n setShowForm(true);\n };\n\n const openEdit = (s: StoreRecord) => {\n setEditingId(s.id);\n setForm({\n slug: s.slug,\n name: s.name,\n type: s.type,\n tenantId: s.tenantId ?? '',\n isPublic: s.isPublic,\n requiresApproval: s.requiresApproval,\n allowUnverifiedPublishers: s.allowUnverifiedPublishers,\n currency: s.currency,\n description: s.description ?? '',\n });\n setShowForm(true);\n };\n\n const submit = async () => {\n if (!form.slug.trim() || !form.name.trim()) {\n setError('Slug and name are required');\n return;\n }\n setError(null);\n try {\n if (editingId) {\n await graphqlFetch({\n gateway: 'global',\n query: UPDATE_STORE_MUTATION,\n variables: {\n id: editingId,\n input: {\n name: form.name,\n isPublic: form.isPublic,\n requiresApproval: form.requiresApproval,\n allowUnverifiedPublishers: form.allowUnverifiedPublishers,\n currency: form.currency,\n description: form.description || null,\n },\n },\n authToken: authToken || undefined,\n });\n } else {\n await graphqlFetch({\n gateway: 'global',\n query: CREATE_STORE_MUTATION,\n variables: {\n input: {\n slug: form.slug,\n name: form.name,\n type: form.type,\n tenantId: form.tenantId || null,\n isPublic: form.isPublic,\n requiresApproval: form.requiresApproval,\n allowUnverifiedPublishers: form.allowUnverifiedPublishers,\n currency: form.currency,\n description: form.description || null,\n },\n },\n authToken: authToken || undefined,\n });\n }\n setShowForm(false);\n setEditingId(null);\n setForm(emptyForm);\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Save failed');\n }\n };\n\n const remove = async (id: string) => {\n try {\n await graphqlFetch({\n gateway: 'global',\n query: DELETE_STORE_MUTATION,\n variables: { id },\n authToken: authToken || undefined,\n });\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Delete failed');\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-seam bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <StoreIcon className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Store registry</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {stores.length}\n </span>\n </div>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => void load()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-surface-accent px-3 py-1.5 text-sm font-medium text-text-on-accent hover:bg-surface-accent-hover\"\n >\n <Plus className=\"size-4\" />\n Create store\n </button>\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n <PagePurpose className=\"mb-4\">\n A store is a storefront that decides which products a given audience can see and buy. Use\n this registry to spin up a public marketplace, a tenant-scoped enterprise store, or a\n private opt-in catalog — and to control whether submissions auto-publish or wait for\n approval, and whether unverified publishers may submit. One store can power a whole\n product's app marketplace.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"store-admin-stores\"\n steps={[\n {\n id: 'create-store',\n label: 'Create your first store',\n description: 'Choose a type (public, private, or enterprise) and a slug.',\n onClick: openCreate,\n },\n {\n id: 'approval-policy',\n label: 'Set the submission policy',\n description: 'Decide if new products require approval before going live.',\n onClick: openCreate,\n },\n {\n id: 'moderate',\n label: 'Review incoming submissions',\n description: 'Head to the submission queue to approve or reject products.',\n onClick: () => navigate(`${basePath}/submissions`),\n },\n ]}\n />\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {showForm && (\n <GlassCard treatment=\"plain\" className=\"mb-6 p-4\">\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {editingId ? 'Edit store' : 'Create store'}\n </h2>\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Slug</span>\n <input\n type=\"text\"\n aria-label=\"Slug\"\n value={form.slug}\n onChange={(e) => setForm({ ...form, slug: e.target.value })}\n disabled={!!editingId}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary disabled:opacity-60\"\n />\n </label>\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Name</span>\n <input\n type=\"text\"\n aria-label=\"Name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </label>\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Type</span>\n <select\n aria-label=\"Store type\"\n value={form.type}\n onChange={(e) => setForm({ ...form, type: e.target.value as FormState['type'] })}\n disabled={!!editingId}\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary disabled:opacity-60 scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"PUBLIC\">Public</option>\n <option value=\"PRIVATE\">Private (opt-in)</option>\n <option value=\"ENTERPRISE\">Enterprise (tenant-scoped)</option>\n </select>\n </label>\n <label className=\"block\">\n <span className=\"text-xs text-text-secondary\">Currency</span>\n <input\n type=\"text\"\n aria-label=\"Currency\"\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value })}\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </label>\n {form.type !== 'PUBLIC' && (\n <label className=\"block md:col-span-2\">\n <span className=\"text-xs text-text-secondary\">Tenant ID (optional)</span>\n <input\n type=\"text\"\n aria-label=\"Tenant ID\"\n value={form.tenantId}\n onChange={(e) => setForm({ ...form, tenantId: e.target.value })}\n disabled={!!editingId}\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary disabled:opacity-60\"\n />\n </label>\n )}\n <MarkdownEditor\n className=\"md:col-span-2\"\n id=\"store-description\"\n label=\"Description\"\n value={form.description}\n onChange={(value) => setForm({ ...form, description: value })}\n rows={2}\n />\n <label className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n aria-label=\"Publicly visible\"\n checked={form.isPublic}\n onChange={(e) => setForm({ ...form, isPublic: e.target.checked })}\n />\n <span className=\"text-sm text-text-primary\">Publicly visible</span>\n </label>\n <label className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n aria-label=\"Requires admin approval\"\n checked={form.requiresApproval}\n onChange={(e) => setForm({ ...form, requiresApproval: e.target.checked })}\n />\n <span className=\"text-sm text-text-primary\">Requires admin approval</span>\n </label>\n <label className=\"flex items-center gap-2 md:col-span-2\">\n <input\n type=\"checkbox\"\n aria-label=\"Allow unverified publishers to submit\"\n checked={form.allowUnverifiedPublishers}\n onChange={(e) =>\n setForm({ ...form, allowUnverifiedPublishers: e.target.checked })\n }\n />\n <span className=\"text-sm text-text-primary\">\n Allow unverified publishers to submit\n </span>\n </label>\n </div>\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setShowForm(false);\n setEditingId(null);\n setForm(emptyForm);\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void submit()}\n className=\"rounded-md bg-surface-accent px-3 py-1.5 text-sm font-medium text-text-on-accent hover:bg-surface-accent-hover\"\n >\n {editingId ? 'Save changes' : 'Create'}\n </button>\n </div>\n </GlassCard>\n )}\n\n {loading && stores.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">Loading stores…</output>\n ) : stores.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No stores yet\"\n description=\"Create your first store to give an audience a place to discover and install products.\"\n action={\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-surface-accent px-3 py-1.5 text-sm font-medium text-text-on-accent hover:bg-surface-accent-hover\"\n >\n <Plus className=\"size-4\" />\n Create store\n </button>\n }\n />\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary\">\n <tr>\n <th className=\"px-4 py-2\">Slug</th>\n <th className=\"px-4 py-2\">Name</th>\n <th className=\"px-4 py-2\">Type</th>\n <th className=\"px-4 py-2\">Public</th>\n <th className=\"px-4 py-2\">Approval</th>\n <th className=\"px-4 py-2\">Currency</th>\n <th className=\"px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-surface-raised\">\n {stores.map((s) => (\n <tr key={s.id} className=\"text-text-primary\">\n <td className=\"px-4 py-2 font-mono text-xs\">{s.slug}</td>\n <td className=\"px-4 py-2\">{s.name}</td>\n <td className=\"px-4 py-2\">\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {s.type}\n </span>\n </td>\n <td className=\"px-4 py-2 text-xs\">{s.isPublic ? 'Yes' : 'No'}</td>\n <td className=\"px-4 py-2 text-xs\">\n {s.requiresApproval ? 'Required' : 'Auto-publish'}\n </td>\n <td className=\"px-4 py-2 text-xs\">{s.currency}</td>\n <td className=\"px-4 py-2 text-right\">\n <button\n type=\"button\"\n onClick={() => openEdit(s)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-2 py-1 text-xs text-text-primary hover:bg-surface-hover\"\n >\n <Pencil className=\"size-3\" />\n Edit\n </button>\n {s.id !== 'store_public_default' && (\n <button\n type=\"button\"\n onClick={() => setPendingDeleteId(s.id)}\n className=\"ml-2 inline-flex items-center gap-1 rounded-md border border-border-danger px-2 py-1 text-xs text-text-danger hover:bg-surface-danger-muted\"\n >\n <Trash2 className=\"size-3\" />\n Delete\n </button>\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </main>\n\n <AlertDialog\n open={pendingDeleteId !== null}\n onOpenChange={(open) => !open && setPendingDeleteId(null)}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete this store?</AlertDialogTitle>\n <AlertDialogDescription>This is a soft delete.</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel onClick={() => setPendingDeleteId(null)}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-surface-danger text-text-on-danger hover:bg-surface-danger-hover\"\n onClick={() => {\n if (pendingDeleteId) void remove(pendingDeleteId);\n setPendingDeleteId(null);\n }}\n >\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -180,7 +180,7 @@ var O = () => {
|
|
|
180
180
|
children: ["Unit", /* @__PURE__ */ y("select", {
|
|
181
181
|
value: k.unit,
|
|
182
182
|
onChange: R("unit"),
|
|
183
|
-
className: c
|
|
183
|
+
className: `${c} scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary`,
|
|
184
184
|
children: i.map((e) => /* @__PURE__ */ y("option", {
|
|
185
185
|
value: e,
|
|
186
186
|
children: o(e)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateSourcingRequestPage.js","names":[],"sources":["../../src/pages/CreateSourcingRequestPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { ChangeEvent, FC, FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport { FilePlus2, Loader2, PackageSearch } from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { SourcingUnit } from '../generated/global-types';\nimport type { CreateSourcingRequestInput } from '../generated/global-types';\nimport { useStore } from '../providers/StoreProvider';\nimport { useSourcingApi } from '../hooks/useSourcing';\nimport {\n ContextRequiredPanel,\n helpClassName,\n inputClassName,\n joinStorePath,\n labelClassName,\n primaryButtonClassName,\n secondaryButtonClassName,\n SOURCING_UNITS,\n SourcingPageHeader,\n formatSourcingUnit,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\n\ninterface RequestFormState {\n productName: string;\n category: string;\n summary: string;\n quantity: string;\n unit: SourcingUnit;\n deliveryWindowStart: string;\n deliveryWindowEnd: string;\n deliveryCountry: string;\n deliveryRegion: string;\n deliveryCity: string;\n targetUnitPrice: string;\n}\n\ntype FieldName = keyof RequestFormState;\ntype FieldErrors = Partial<Record<FieldName, string>>;\n\nconst INITIAL_FORM: RequestFormState = {\n productName: '',\n category: '',\n summary: '',\n quantity: '',\n unit: SourcingUnit.Unit,\n deliveryWindowStart: '',\n deliveryWindowEnd: '',\n deliveryCountry: '',\n deliveryRegion: '',\n deliveryCity: '',\n targetUnitPrice: '',\n};\n\nconst MONEY_PATTERN = /^(?:0|[1-9]\\d{0,15})(?:\\.\\d{1,2})?$/;\nconst CATEGORY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nfunction normalizeCategory(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replaceAll(/[_\\s]+/g, '-')\n .replaceAll(/-+/g, '-');\n}\n\nfunction validate(form: RequestFormState): FieldErrors {\n const errors: FieldErrors = {};\n const productName = form.productName.trim();\n const category = normalizeCategory(form.category);\n const quantity = Number(form.quantity);\n const start = new Date(form.deliveryWindowStart);\n const end = new Date(form.deliveryWindowEnd);\n\n if (productName.length < 2 || productName.length > 160) {\n errors.productName = 'Enter a product name between 2 and 160 characters.';\n }\n if (category.length < 2 || category.length > 64 || !CATEGORY_PATTERN.test(category)) {\n errors.category = 'Use 2–64 lowercase letters, numbers, and single hyphens.';\n }\n if (form.summary.trim().length > 1000) {\n errors.summary = 'Summary cannot exceed 1,000 characters.';\n }\n if (!Number.isSafeInteger(quantity) || quantity < 1 || quantity > 1_000_000) {\n errors.quantity = 'Quantity must be a whole number from 1 to 1,000,000.';\n }\n if (!form.deliveryWindowStart || Number.isNaN(start.getTime())) {\n errors.deliveryWindowStart = 'Choose a valid delivery window start.';\n }\n if (!form.deliveryWindowEnd || Number.isNaN(end.getTime()) || end <= start) {\n errors.deliveryWindowEnd = 'Delivery window end must be after its start.';\n }\n if (!/^[A-Za-z]{2}$/.test(form.deliveryCountry.trim())) {\n errors.deliveryCountry = 'Use a two-letter country code, such as IN or US.';\n }\n if (form.deliveryRegion.trim().length > 120) {\n errors.deliveryRegion = 'Region cannot exceed 120 characters.';\n }\n if (form.deliveryCity.trim().length > 120) {\n errors.deliveryCity = 'City cannot exceed 120 characters.';\n }\n if (\n form.targetUnitPrice &&\n (!MONEY_PATTERN.test(form.targetUnitPrice.trim()) || Number(form.targetUnitPrice) <= 0)\n ) {\n errors.targetUnitPrice = 'Enter a positive amount with no more than two decimal places.';\n }\n\n return errors;\n}\n\nexport const CreateSourcingRequestPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { createBuyerRequest, hasOrganizationContext } = useSourcingApi();\n const [form, setForm] = useState<RequestFormState>(INITIAL_FORM);\n const [errors, setErrors] = useState<FieldErrors>({});\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n const listPath = joinStorePath(basePath, 'sourcing/requests');\n\n const updateField =\n <TField extends FieldName>(field: TField) =>\n (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {\n const value = event.target.value;\n setForm((current) => ({ ...current, [field]: value }));\n setErrors((current) => ({ ...current, [field]: undefined }));\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!hasOrganizationContext || submitting) return;\n\n const validationErrors = validate(form);\n setErrors(validationErrors);\n setSubmitError(null);\n if (Object.keys(validationErrors).length > 0) return;\n\n const optional = (value: string) => value.trim() || undefined;\n const input: CreateSourcingRequestInput = {\n productName: form.productName.trim(),\n category: normalizeCategory(form.category),\n summary: optional(form.summary),\n quantity: Number(form.quantity),\n unit: form.unit,\n deliveryWindowStart: new Date(form.deliveryWindowStart).toISOString(),\n deliveryWindowEnd: new Date(form.deliveryWindowEnd).toISOString(),\n deliveryCountry: form.deliveryCountry.trim().toUpperCase(),\n deliveryRegion: optional(form.deliveryRegion),\n deliveryCity: optional(form.deliveryCity),\n targetUnitPrice: optional(form.targetUnitPrice),\n targetCurrency: form.targetUnitPrice.trim() ? 'USD' : undefined,\n };\n\n setSubmitting(true);\n try {\n const request = await createBuyerRequest(input);\n navigate(joinStorePath(basePath, `sourcing/requests/${request.id}`), { replace: true });\n } catch (error) {\n setSubmitError(toErrorMessage(error, 'The sourcing request could not be created.'));\n } finally {\n setSubmitting(false);\n }\n };\n\n const fieldError = (field: FieldName) => {\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 return (\n <div className=\"min-h-full bg-surface\">\n <SourcingPageHeader\n title=\"Create sourcing request\"\n description=\"Share only the purchase details verified publishers need to prepare an offer.\"\n icon={<FilePlus2 className=\"size-5\" aria-hidden=\"true\" />}\n onBack={() => navigate(listPath)}\n />\n\n <main className=\"mx-auto max-w-4xl px-4 py-6 sm:px-6\">\n <PagePurpose className=\"mb-6\">\n Your user and active organization own this request. Billing identity is resolved securely\n by Store; it is never entered in this form. Publishers see the product, quantity,\n destination, delivery window, and optional target price only.\n </PagePurpose>\n\n {!hasOrganizationContext ? (\n <ContextRequiredPanel\n title=\"Choose an organization before creating a request\"\n description=\"Use the app context switcher to select a global organization with a billing account. You do not need to select or create a farm workspace.\"\n />\n ) : (\n <form onSubmit={(event) => void handleSubmit(event)} noValidate>\n <GlassCard className=\"p-5 sm:p-6\">\n <section aria-labelledby=\"request-product-heading\">\n <div className=\"flex items-center gap-2\">\n <PackageSearch className=\"size-5 text-accent-primary\" aria-hidden=\"true\" />\n <h2\n id=\"request-product-heading\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Product needed\n </h2>\n </div>\n <div className=\"mt-5 grid gap-5 sm:grid-cols-2\">\n <label className={`${labelClassName} sm:col-span-2`}>\n Product name\n <input\n type=\"text\"\n value={form.productName}\n onChange={updateField('productName')}\n required\n maxLength={160}\n autoComplete=\"off\"\n aria-invalid={Boolean(errors.productName)}\n aria-describedby={errors.productName ? 'productName-error' : undefined}\n placeholder=\"Example: Organic basmati rice\"\n className={inputClassName}\n />\n {fieldError('productName')}\n </label>\n\n <label className={labelClassName}>\n Category\n <input\n type=\"text\"\n value={form.category}\n onChange={updateField('category')}\n onBlur={() =>\n setForm((current) => ({\n ...current,\n category: normalizeCategory(current.category),\n }))\n }\n required\n maxLength={64}\n autoComplete=\"off\"\n aria-invalid={Boolean(errors.category)}\n aria-describedby={errors.category ? 'category-error' : 'category-help'}\n placeholder=\"food-grains\"\n className={inputClassName}\n />\n {fieldError('category')}\n {!errors.category ? (\n <p id=\"category-help\" className={helpClassName}>\n A short category slug; spaces are converted to hyphens.\n </p>\n ) : null}\n </label>\n\n <label className={labelClassName}>\n Quantity\n <input\n type=\"number\"\n value={form.quantity}\n onChange={updateField('quantity')}\n required\n min={1}\n max={1_000_000}\n step={1}\n inputMode=\"numeric\"\n aria-invalid={Boolean(errors.quantity)}\n aria-describedby={errors.quantity ? 'quantity-error' : undefined}\n className={inputClassName}\n />\n {fieldError('quantity')}\n </label>\n\n <label className={labelClassName}>\n Unit\n <select\n value={form.unit}\n onChange={updateField('unit')}\n className={inputClassName}\n >\n {SOURCING_UNITS.map((unit) => (\n <option key={unit} value={unit}>\n {formatSourcingUnit(unit)}\n </option>\n ))}\n </select>\n </label>\n\n <label className={`${labelClassName} sm:col-span-2`}>\n Summary <span className=\"font-normal text-text-muted\">(optional)</span>\n <textarea\n value={form.summary}\n onChange={updateField('summary')}\n maxLength={1000}\n rows={4}\n aria-invalid={Boolean(errors.summary)}\n aria-describedby={errors.summary ? 'summary-error' : 'summary-help'}\n placeholder=\"Add grade, packaging, certification, or other offer requirements.\"\n className={`${inputClassName} resize-y`}\n />\n {fieldError('summary')}\n {!errors.summary ? (\n <p id=\"summary-help\" className={helpClassName}>\n {form.summary.length}/1,000 characters\n </p>\n ) : null}\n </label>\n <div\n role=\"note\"\n className=\"rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text sm:col-span-2\"\n >\n <p className=\"font-semibold\">This summary is visible to eligible publishers.</p>\n <p className=\"mt-1 leading-6\">\n Do not include personal or contact details, trust or compliance records,\n payment or billing information, credentials, private addresses, or other\n confidential data. Share only product and delivery requirements needed to\n prepare an offer.\n </p>\n </div>\n </div>\n </section>\n\n <section\n aria-labelledby=\"request-delivery-heading\"\n className=\"mt-8 border-t border-border-subtle pt-6\"\n >\n <h2\n id=\"request-delivery-heading\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Delivery\n </h2>\n <div className=\"mt-5 grid gap-5 sm:grid-cols-2\">\n <label className={labelClassName}>\n Window starts\n <input\n type=\"datetime-local\"\n value={form.deliveryWindowStart}\n onChange={updateField('deliveryWindowStart')}\n required\n aria-invalid={Boolean(errors.deliveryWindowStart)}\n aria-describedby={\n errors.deliveryWindowStart ? 'deliveryWindowStart-error' : undefined\n }\n className={inputClassName}\n />\n {fieldError('deliveryWindowStart')}\n </label>\n <label className={labelClassName}>\n Window ends\n <input\n type=\"datetime-local\"\n value={form.deliveryWindowEnd}\n onChange={updateField('deliveryWindowEnd')}\n required\n min={form.deliveryWindowStart || undefined}\n aria-invalid={Boolean(errors.deliveryWindowEnd)}\n aria-describedby={\n errors.deliveryWindowEnd ? 'deliveryWindowEnd-error' : undefined\n }\n className={inputClassName}\n />\n {fieldError('deliveryWindowEnd')}\n </label>\n <label className={labelClassName}>\n Country code\n <input\n type=\"text\"\n value={form.deliveryCountry}\n onChange={updateField('deliveryCountry')}\n required\n minLength={2}\n maxLength={2}\n autoComplete=\"country\"\n aria-invalid={Boolean(errors.deliveryCountry)}\n aria-describedby={\n errors.deliveryCountry ? 'deliveryCountry-error' : 'deliveryCountry-help'\n }\n placeholder=\"IN\"\n className={`${inputClassName} uppercase`}\n />\n {fieldError('deliveryCountry')}\n {!errors.deliveryCountry ? (\n <p id=\"deliveryCountry-help\" className={helpClassName}>\n ISO two-letter country code.\n </p>\n ) : null}\n </label>\n <label className={labelClassName}>\n Region or state <span className=\"font-normal text-text-muted\">(optional)</span>\n <input\n type=\"text\"\n value={form.deliveryRegion}\n onChange={updateField('deliveryRegion')}\n maxLength={120}\n autoComplete=\"address-level1\"\n aria-invalid={Boolean(errors.deliveryRegion)}\n aria-describedby={errors.deliveryRegion ? 'deliveryRegion-error' : undefined}\n className={inputClassName}\n />\n {fieldError('deliveryRegion')}\n </label>\n <label className={labelClassName}>\n City <span className=\"font-normal text-text-muted\">(optional)</span>\n <input\n type=\"text\"\n value={form.deliveryCity}\n onChange={updateField('deliveryCity')}\n maxLength={120}\n autoComplete=\"address-level2\"\n aria-invalid={Boolean(errors.deliveryCity)}\n aria-describedby={errors.deliveryCity ? 'deliveryCity-error' : undefined}\n className={inputClassName}\n />\n {fieldError('deliveryCity')}\n </label>\n </div>\n </section>\n\n <section\n aria-labelledby=\"request-budget-heading\"\n className=\"mt-8 border-t border-border-subtle pt-6\"\n >\n <h2 id=\"request-budget-heading\" className=\"text-lg font-semibold text-text-primary\">\n Target price <span className=\"font-normal text-text-muted\">(optional)</span>\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n This is a target unit price, not an order total. Store calculates the accepted\n order total from the seller's persisted offer.\n </p>\n <div className=\"mt-5 grid gap-5 sm:grid-cols-2\">\n <label className={labelClassName}>\n Target unit price\n <input\n type=\"text\"\n value={form.targetUnitPrice}\n onChange={updateField('targetUnitPrice')}\n inputMode=\"decimal\"\n maxLength={19}\n aria-invalid={Boolean(errors.targetUnitPrice)}\n aria-describedby={\n errors.targetUnitPrice ? 'targetUnitPrice-error' : 'targetUnitPrice-help'\n }\n placeholder=\"125.50\"\n className={inputClassName}\n />\n {fieldError('targetUnitPrice')}\n {!errors.targetUnitPrice ? (\n <p id=\"targetUnitPrice-help\" className={helpClassName}>\n Positive amount with up to two decimal places.\n </p>\n ) : null}\n </label>\n <div>\n <span className={labelClassName}>Settlement currency</span>\n <div\n className={`${inputClassName} flex items-center bg-surface-sunken font-semibold`}\n >\n USD\n </div>\n <p className={helpClassName}>\n Store sourcing offers, orders, earnings, and payouts currently settle in USD.\n </p>\n </div>\n </div>\n </section>\n\n {submitError ? (\n <div\n role=\"alert\"\n className=\"mt-6 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\n <div className=\"mt-7 flex flex-col-reverse gap-3 border-t border-border-subtle pt-5 sm:flex-row sm:justify-end\">\n <button\n type=\"button\"\n onClick={() => navigate(listPath)}\n disabled={submitting}\n className={secondaryButtonClassName}\n >\n Cancel\n </button>\n <button type=\"submit\" disabled={submitting} className={primaryButtonClassName}>\n {submitting ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <FilePlus2 className=\"size-4\" aria-hidden=\"true\" />\n )}\n {submitting ? 'Creating request…' : 'Create request'}\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateSourcingRequestPage.js","names":[],"sources":["../../src/pages/CreateSourcingRequestPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport type { ChangeEvent, FC, FormEvent } from 'react';\nimport { useNavigate } from 'react-router';\nimport { FilePlus2, Loader2, PackageSearch } from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { SourcingUnit } from '../generated/global-types';\nimport type { CreateSourcingRequestInput } from '../generated/global-types';\nimport { useStore } from '../providers/StoreProvider';\nimport { useSourcingApi } from '../hooks/useSourcing';\nimport {\n ContextRequiredPanel,\n helpClassName,\n inputClassName,\n joinStorePath,\n labelClassName,\n primaryButtonClassName,\n secondaryButtonClassName,\n SOURCING_UNITS,\n SourcingPageHeader,\n formatSourcingUnit,\n toErrorMessage,\n} from '../components/sourcing/SourcingUi';\n\ninterface RequestFormState {\n productName: string;\n category: string;\n summary: string;\n quantity: string;\n unit: SourcingUnit;\n deliveryWindowStart: string;\n deliveryWindowEnd: string;\n deliveryCountry: string;\n deliveryRegion: string;\n deliveryCity: string;\n targetUnitPrice: string;\n}\n\ntype FieldName = keyof RequestFormState;\ntype FieldErrors = Partial<Record<FieldName, string>>;\n\nconst INITIAL_FORM: RequestFormState = {\n productName: '',\n category: '',\n summary: '',\n quantity: '',\n unit: SourcingUnit.Unit,\n deliveryWindowStart: '',\n deliveryWindowEnd: '',\n deliveryCountry: '',\n deliveryRegion: '',\n deliveryCity: '',\n targetUnitPrice: '',\n};\n\nconst MONEY_PATTERN = /^(?:0|[1-9]\\d{0,15})(?:\\.\\d{1,2})?$/;\nconst CATEGORY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nfunction normalizeCategory(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replaceAll(/[_\\s]+/g, '-')\n .replaceAll(/-+/g, '-');\n}\n\nfunction validate(form: RequestFormState): FieldErrors {\n const errors: FieldErrors = {};\n const productName = form.productName.trim();\n const category = normalizeCategory(form.category);\n const quantity = Number(form.quantity);\n const start = new Date(form.deliveryWindowStart);\n const end = new Date(form.deliveryWindowEnd);\n\n if (productName.length < 2 || productName.length > 160) {\n errors.productName = 'Enter a product name between 2 and 160 characters.';\n }\n if (category.length < 2 || category.length > 64 || !CATEGORY_PATTERN.test(category)) {\n errors.category = 'Use 2–64 lowercase letters, numbers, and single hyphens.';\n }\n if (form.summary.trim().length > 1000) {\n errors.summary = 'Summary cannot exceed 1,000 characters.';\n }\n if (!Number.isSafeInteger(quantity) || quantity < 1 || quantity > 1_000_000) {\n errors.quantity = 'Quantity must be a whole number from 1 to 1,000,000.';\n }\n if (!form.deliveryWindowStart || Number.isNaN(start.getTime())) {\n errors.deliveryWindowStart = 'Choose a valid delivery window start.';\n }\n if (!form.deliveryWindowEnd || Number.isNaN(end.getTime()) || end <= start) {\n errors.deliveryWindowEnd = 'Delivery window end must be after its start.';\n }\n if (!/^[A-Za-z]{2}$/.test(form.deliveryCountry.trim())) {\n errors.deliveryCountry = 'Use a two-letter country code, such as IN or US.';\n }\n if (form.deliveryRegion.trim().length > 120) {\n errors.deliveryRegion = 'Region cannot exceed 120 characters.';\n }\n if (form.deliveryCity.trim().length > 120) {\n errors.deliveryCity = 'City cannot exceed 120 characters.';\n }\n if (\n form.targetUnitPrice &&\n (!MONEY_PATTERN.test(form.targetUnitPrice.trim()) || Number(form.targetUnitPrice) <= 0)\n ) {\n errors.targetUnitPrice = 'Enter a positive amount with no more than two decimal places.';\n }\n\n return errors;\n}\n\nexport const CreateSourcingRequestPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { createBuyerRequest, hasOrganizationContext } = useSourcingApi();\n const [form, setForm] = useState<RequestFormState>(INITIAL_FORM);\n const [errors, setErrors] = useState<FieldErrors>({});\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n const listPath = joinStorePath(basePath, 'sourcing/requests');\n\n const updateField =\n <TField extends FieldName>(field: TField) =>\n (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {\n const value = event.target.value;\n setForm((current) => ({ ...current, [field]: value }));\n setErrors((current) => ({ ...current, [field]: undefined }));\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!hasOrganizationContext || submitting) return;\n\n const validationErrors = validate(form);\n setErrors(validationErrors);\n setSubmitError(null);\n if (Object.keys(validationErrors).length > 0) return;\n\n const optional = (value: string) => value.trim() || undefined;\n const input: CreateSourcingRequestInput = {\n productName: form.productName.trim(),\n category: normalizeCategory(form.category),\n summary: optional(form.summary),\n quantity: Number(form.quantity),\n unit: form.unit,\n deliveryWindowStart: new Date(form.deliveryWindowStart).toISOString(),\n deliveryWindowEnd: new Date(form.deliveryWindowEnd).toISOString(),\n deliveryCountry: form.deliveryCountry.trim().toUpperCase(),\n deliveryRegion: optional(form.deliveryRegion),\n deliveryCity: optional(form.deliveryCity),\n targetUnitPrice: optional(form.targetUnitPrice),\n targetCurrency: form.targetUnitPrice.trim() ? 'USD' : undefined,\n };\n\n setSubmitting(true);\n try {\n const request = await createBuyerRequest(input);\n navigate(joinStorePath(basePath, `sourcing/requests/${request.id}`), { replace: true });\n } catch (error) {\n setSubmitError(toErrorMessage(error, 'The sourcing request could not be created.'));\n } finally {\n setSubmitting(false);\n }\n };\n\n const fieldError = (field: FieldName) => {\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 return (\n <div className=\"min-h-full bg-surface\">\n <SourcingPageHeader\n title=\"Create sourcing request\"\n description=\"Share only the purchase details verified publishers need to prepare an offer.\"\n icon={<FilePlus2 className=\"size-5\" aria-hidden=\"true\" />}\n onBack={() => navigate(listPath)}\n />\n\n <main className=\"mx-auto max-w-4xl px-4 py-6 sm:px-6\">\n <PagePurpose className=\"mb-6\">\n Your user and active organization own this request. Billing identity is resolved securely\n by Store; it is never entered in this form. Publishers see the product, quantity,\n destination, delivery window, and optional target price only.\n </PagePurpose>\n\n {!hasOrganizationContext ? (\n <ContextRequiredPanel\n title=\"Choose an organization before creating a request\"\n description=\"Use the app context switcher to select a global organization with a billing account. You do not need to select or create a farm workspace.\"\n />\n ) : (\n <form onSubmit={(event) => void handleSubmit(event)} noValidate>\n <GlassCard className=\"p-5 sm:p-6\">\n <section aria-labelledby=\"request-product-heading\">\n <div className=\"flex items-center gap-2\">\n <PackageSearch className=\"size-5 text-accent-primary\" aria-hidden=\"true\" />\n <h2\n id=\"request-product-heading\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Product needed\n </h2>\n </div>\n <div className=\"mt-5 grid gap-5 sm:grid-cols-2\">\n <label className={`${labelClassName} sm:col-span-2`}>\n Product name\n <input\n type=\"text\"\n value={form.productName}\n onChange={updateField('productName')}\n required\n maxLength={160}\n autoComplete=\"off\"\n aria-invalid={Boolean(errors.productName)}\n aria-describedby={errors.productName ? 'productName-error' : undefined}\n placeholder=\"Example: Organic basmati rice\"\n className={inputClassName}\n />\n {fieldError('productName')}\n </label>\n\n <label className={labelClassName}>\n Category\n <input\n type=\"text\"\n value={form.category}\n onChange={updateField('category')}\n onBlur={() =>\n setForm((current) => ({\n ...current,\n category: normalizeCategory(current.category),\n }))\n }\n required\n maxLength={64}\n autoComplete=\"off\"\n aria-invalid={Boolean(errors.category)}\n aria-describedby={errors.category ? 'category-error' : 'category-help'}\n placeholder=\"food-grains\"\n className={inputClassName}\n />\n {fieldError('category')}\n {!errors.category ? (\n <p id=\"category-help\" className={helpClassName}>\n A short category slug; spaces are converted to hyphens.\n </p>\n ) : null}\n </label>\n\n <label className={labelClassName}>\n Quantity\n <input\n type=\"number\"\n value={form.quantity}\n onChange={updateField('quantity')}\n required\n min={1}\n max={1_000_000}\n step={1}\n inputMode=\"numeric\"\n aria-invalid={Boolean(errors.quantity)}\n aria-describedby={errors.quantity ? 'quantity-error' : undefined}\n className={inputClassName}\n />\n {fieldError('quantity')}\n </label>\n\n <label className={labelClassName}>\n Unit\n <select\n value={form.unit}\n onChange={updateField('unit')}\n className={`${inputClassName} scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary`}\n >\n {SOURCING_UNITS.map((unit) => (\n <option key={unit} value={unit}>\n {formatSourcingUnit(unit)}\n </option>\n ))}\n </select>\n </label>\n\n <label className={`${labelClassName} sm:col-span-2`}>\n Summary <span className=\"font-normal text-text-muted\">(optional)</span>\n <textarea\n value={form.summary}\n onChange={updateField('summary')}\n maxLength={1000}\n rows={4}\n aria-invalid={Boolean(errors.summary)}\n aria-describedby={errors.summary ? 'summary-error' : 'summary-help'}\n placeholder=\"Add grade, packaging, certification, or other offer requirements.\"\n className={`${inputClassName} resize-y`}\n />\n {fieldError('summary')}\n {!errors.summary ? (\n <p id=\"summary-help\" className={helpClassName}>\n {form.summary.length}/1,000 characters\n </p>\n ) : null}\n </label>\n <div\n role=\"note\"\n className=\"rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text sm:col-span-2\"\n >\n <p className=\"font-semibold\">This summary is visible to eligible publishers.</p>\n <p className=\"mt-1 leading-6\">\n Do not include personal or contact details, trust or compliance records,\n payment or billing information, credentials, private addresses, or other\n confidential data. Share only product and delivery requirements needed to\n prepare an offer.\n </p>\n </div>\n </div>\n </section>\n\n <section\n aria-labelledby=\"request-delivery-heading\"\n className=\"mt-8 border-t border-border-subtle pt-6\"\n >\n <h2\n id=\"request-delivery-heading\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Delivery\n </h2>\n <div className=\"mt-5 grid gap-5 sm:grid-cols-2\">\n <label className={labelClassName}>\n Window starts\n <input\n type=\"datetime-local\"\n value={form.deliveryWindowStart}\n onChange={updateField('deliveryWindowStart')}\n required\n aria-invalid={Boolean(errors.deliveryWindowStart)}\n aria-describedby={\n errors.deliveryWindowStart ? 'deliveryWindowStart-error' : undefined\n }\n className={inputClassName}\n />\n {fieldError('deliveryWindowStart')}\n </label>\n <label className={labelClassName}>\n Window ends\n <input\n type=\"datetime-local\"\n value={form.deliveryWindowEnd}\n onChange={updateField('deliveryWindowEnd')}\n required\n min={form.deliveryWindowStart || undefined}\n aria-invalid={Boolean(errors.deliveryWindowEnd)}\n aria-describedby={\n errors.deliveryWindowEnd ? 'deliveryWindowEnd-error' : undefined\n }\n className={inputClassName}\n />\n {fieldError('deliveryWindowEnd')}\n </label>\n <label className={labelClassName}>\n Country code\n <input\n type=\"text\"\n value={form.deliveryCountry}\n onChange={updateField('deliveryCountry')}\n required\n minLength={2}\n maxLength={2}\n autoComplete=\"country\"\n aria-invalid={Boolean(errors.deliveryCountry)}\n aria-describedby={\n errors.deliveryCountry ? 'deliveryCountry-error' : 'deliveryCountry-help'\n }\n placeholder=\"IN\"\n className={`${inputClassName} uppercase`}\n />\n {fieldError('deliveryCountry')}\n {!errors.deliveryCountry ? (\n <p id=\"deliveryCountry-help\" className={helpClassName}>\n ISO two-letter country code.\n </p>\n ) : null}\n </label>\n <label className={labelClassName}>\n Region or state <span className=\"font-normal text-text-muted\">(optional)</span>\n <input\n type=\"text\"\n value={form.deliveryRegion}\n onChange={updateField('deliveryRegion')}\n maxLength={120}\n autoComplete=\"address-level1\"\n aria-invalid={Boolean(errors.deliveryRegion)}\n aria-describedby={errors.deliveryRegion ? 'deliveryRegion-error' : undefined}\n className={inputClassName}\n />\n {fieldError('deliveryRegion')}\n </label>\n <label className={labelClassName}>\n City <span className=\"font-normal text-text-muted\">(optional)</span>\n <input\n type=\"text\"\n value={form.deliveryCity}\n onChange={updateField('deliveryCity')}\n maxLength={120}\n autoComplete=\"address-level2\"\n aria-invalid={Boolean(errors.deliveryCity)}\n aria-describedby={errors.deliveryCity ? 'deliveryCity-error' : undefined}\n className={inputClassName}\n />\n {fieldError('deliveryCity')}\n </label>\n </div>\n </section>\n\n <section\n aria-labelledby=\"request-budget-heading\"\n className=\"mt-8 border-t border-border-subtle pt-6\"\n >\n <h2 id=\"request-budget-heading\" className=\"text-lg font-semibold text-text-primary\">\n Target price <span className=\"font-normal text-text-muted\">(optional)</span>\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n This is a target unit price, not an order total. Store calculates the accepted\n order total from the seller's persisted offer.\n </p>\n <div className=\"mt-5 grid gap-5 sm:grid-cols-2\">\n <label className={labelClassName}>\n Target unit price\n <input\n type=\"text\"\n value={form.targetUnitPrice}\n onChange={updateField('targetUnitPrice')}\n inputMode=\"decimal\"\n maxLength={19}\n aria-invalid={Boolean(errors.targetUnitPrice)}\n aria-describedby={\n errors.targetUnitPrice ? 'targetUnitPrice-error' : 'targetUnitPrice-help'\n }\n placeholder=\"125.50\"\n className={inputClassName}\n />\n {fieldError('targetUnitPrice')}\n {!errors.targetUnitPrice ? (\n <p id=\"targetUnitPrice-help\" className={helpClassName}>\n Positive amount with up to two decimal places.\n </p>\n ) : null}\n </label>\n <div>\n <span className={labelClassName}>Settlement currency</span>\n <div\n className={`${inputClassName} flex items-center bg-surface-sunken font-semibold`}\n >\n USD\n </div>\n <p className={helpClassName}>\n Store sourcing offers, orders, earnings, and payouts currently settle in USD.\n </p>\n </div>\n </div>\n </section>\n\n {submitError ? (\n <div\n role=\"alert\"\n className=\"mt-6 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\n <div className=\"mt-7 flex flex-col-reverse gap-3 border-t border-border-subtle pt-5 sm:flex-row sm:justify-end\">\n <button\n type=\"button\"\n onClick={() => navigate(listPath)}\n disabled={submitting}\n className={secondaryButtonClassName}\n >\n Cancel\n </button>\n <button type=\"submit\" disabled={submitting} className={primaryButtonClassName}>\n {submitting ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : (\n <FilePlus2 className=\"size-4\" aria-hidden=\"true\" />\n )}\n {submitting ? 'Creating request…' : 'Create request'}\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -523,7 +523,7 @@ var C = (e) => e >= 1e6 ? `${(e / 1e6).toFixed(1)}M` : e >= 1e3 ? `${(e / 1e3).t
|
|
|
523
523
|
"aria-label": e,
|
|
524
524
|
value: t,
|
|
525
525
|
onChange: (e) => r(e.target.value),
|
|
526
|
-
className: "min-w-[8rem] appearance-none bg-
|
|
526
|
+
className: "min-w-[8rem] appearance-none bg-bg-surface pr-6 text-sm font-medium text-text-primary scheme-normal dark:scheme-dark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
527
527
|
children: n.map((e) => /* @__PURE__ */ x("option", {
|
|
528
528
|
value: e.value,
|
|
529
529
|
children: e.label
|