@burdenoff/microfe-devportal 2026.713.1 → 2026.713.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ function n(e, t, n, r) {
7
7
  let s = e.endsWith("/graphql") ? e : `${e}/workspaces/graphql`, c = await fetch(s, {
8
8
  method: "POST",
9
9
  headers: o,
10
+ credentials: "include",
10
11
  body: JSON.stringify({
11
12
  query: i,
12
13
  variables: a
@@ -1 +1 @@
1
- {"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a zip package to wspace-files-svc and returns a\n * stable downloadUrl to store as ApplicationVersion.packageUrl.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(\"/DevPortal/Packages\") → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | { file?: { id?: string; downloadUrl?: string } }\n | undefined\n )?.file;\n if (!file?.id || !file.downloadUrl) {\n throw new Error(\"completeUpload did not return file id or downloadUrl\");\n }\n return { fileId: file.id, downloadUrl: file.downloadUrl };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a zip package to wspace-files-svc and returns a\n * stable downloadUrl to store as ApplicationVersion.packageUrl.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(\"/DevPortal/Packages\") → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | { file?: { id?: string; downloadUrl?: string } }\n | undefined\n )?.file;\n if (!file?.id || !file.downloadUrl) {\n throw new Error(\"completeUpload did not return file id or downloadUrl\");\n }\n return { fileId: file.id, downloadUrl: file.downloadUrl };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
@@ -140,6 +140,7 @@ var D = () => {
140
140
  "Content-Type": "application/json",
141
141
  ...k ? { Authorization: `Bearer ${k}` } : {}
142
142
  },
143
+ credentials: "include",
143
144
  body: JSON.stringify({
144
145
  query: e,
145
146
  variables: t
@@ -1 +1 @@
1
- {"version":3,"file":"CreatePhysicalItemPage.js","names":[],"sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent, DragEvent } from \"react\";\nimport { useState, useCallback, useRef } from \"react\";\nimport { Loader2, Upload, X, Info } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface ImagePreview {\n file: File;\n url: string;\n}\n\ninterface FormState {\n name: string;\n slug: string;\n type: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n requiresShipping: boolean;\n weightKg: string;\n brand: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst PRODUCT_CATEGORIES = [\n { value: \"MERCHANDISE\", label: \"Merchandise\" },\n { value: \"HARDWARE\", label: \"Hardware\" },\n { value: \"ACCESSORY\", label: \"Accessory\" },\n { value: \"BOOK\", label: \"Book\" },\n { value: \"EQUIPMENT\", label: \"Equipment\" },\n { value: \"APPAREL\", label: \"Apparel\" },\n { value: \"COLLECTIBLE\", label: \"Collectible\" },\n { value: \"CONSUMABLE\", label: \"Consumable\" },\n];\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"slug\",\n \"type\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL strings ──────────────────────────────────────────────────────────\n\nconst MY_PUBLISHER_QUERY = /* GraphQL */ `\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n`;\n\nconst ORG_QUERY = /* GraphQL */ `\n query SellerOrgForPublisher {\n developerOrganizations(limit: 1, offset: 0) {\n edges {\n node {\n name\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n }\n }\n`;\n\nconst REGISTER_PUBLISHER_MUTATION = /* GraphQL */ `\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n`;\n\nconst CREATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction deriveSlug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return value.trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const CreatePhysicalItemPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n requiresShipping: true,\n weightKg: \"\",\n brand: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [images, setImages] = useState<ImagePreview[]>([]);\n const [isDragOver, setIsDragOver] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const isValid = isFormValid(form);\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n slug: deriveSlug(name),\n }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Image handling ───────────────────────────────────────────────────────\n\n const addFiles = useCallback((files: FileList | File[]) => {\n const incoming = Array.from(files).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n const previews: ImagePreview[] = incoming.map((file) => ({\n file,\n url: URL.createObjectURL(file),\n }));\n setImages((prev) => [...prev, ...previews]);\n }, []);\n\n const handleFileInput = (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files) addFiles(e.target.files);\n e.target.value = \"\";\n };\n\n const handleDrop = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(false);\n if (e.dataTransfer.files) addFiles(e.dataTransfer.files);\n };\n\n const handleDragOver = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(true);\n };\n\n const handleDragLeave = () => setIsDragOver(false);\n\n const removeImage = (index: number) => {\n setImages((prev) => {\n const next = [...prev];\n URL.revokeObjectURL(next[index]!.url);\n next.splice(index, 1);\n return next;\n });\n };\n\n // ─── GraphQL fetch helper ─────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n // ─── Submit flow ──────────────────────────────────────────────────────────\n\n const handleSubmit = async () => {\n if (!isValid || submitting) return;\n setSubmitting(true);\n setSubmitError(null);\n\n try {\n // Step 1: check if publisher exists\n const publisherData = await gqlFetch<{\n myPublisher: { id: string } | null;\n }>(MY_PUBLISHER_QUERY);\n\n let publisherId = publisherData.myPublisher?.id ?? null;\n\n // Step 2: auto-register publisher if needed\n if (!publisherId) {\n const orgData = await gqlFetch<{\n developerOrganizations: {\n edges: {\n node: {\n name: string;\n contactEmail: string | null;\n websiteUrl: string | null;\n legalEntityName: string | null;\n taxCountry: string | null;\n };\n }[];\n };\n }>(ORG_QUERY);\n\n const org =\n orgData.developerOrganizations.edges[0]?.node ?? null;\n\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: org?.legalEntityName ?? org?.name ?? \"My Store\",\n email: org?.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: org?.websiteUrl ?? null,\n legalName: org?.legalEntityName ?? org?.name ?? \"My Store\",\n country: org?.taxCountry ?? null,\n contactEmail: org?.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 3: create the product\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n const productData = await gqlFetch<{\n createPhysicalProduct: { id: string };\n }>(CREATE_PHYSICAL_PRODUCT_MUTATION, {\n input: {\n name: form.name.trim(),\n slug: form.slug.trim(),\n type: form.type,\n shortDescription: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n requiresShipping: form.requiresShipping,\n weightKg:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n brand: form.brand.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n },\n });\n\n const productId = productData.createPhysicalProduct.id;\n\n // Step 4: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: productId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 5: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error ? err.message : \"Submission failed. Please try again.\",\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <PageLayout\n title=\"List a Physical Item\"\n description=\"Fill in all required details. Your listing will be reviewed before it appears in the store.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Your listing has been submitted for review! Redirecting...\n </div>\n )}\n\n {/* Error banner */}\n {submitError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Slug\"\n required\n value={form.slug}\n onChange={(e) => setField(\"slug\", e.target.value)}\n placeholder=\"auto-derived-from-name\"\n hint=\"URL-friendly identifier. Auto-derived from name; you can edit it.\"\n />\n\n <SelectField\n label=\"Product Category\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a category\"\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Product Images ── */}\n <PageSection\n title=\"Product Images\"\n description=\"The first image is used as the main icon.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-4\">\n {/* Drop zone */}\n <div\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onClick={() => fileInputRef.current?.click()}\n className={cn(\n \"flex flex-col items-center justify-center gap-3 rounded-input border-2 border-dashed px-6 py-10 cursor-pointer transition-colors\",\n isDragOver\n ? \"border-action-primary-bg bg-action-primary-bg/5\"\n : \"border-border-subtle hover:border-border-strong\",\n )}\n >\n <Upload\n size={28}\n className={isDragOver ? \"text-action-primary-bg\" : \"text-text-muted\"}\n />\n <div className=\"text-center space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n Drag and drop images here\n </p>\n <p className=\"text-xs text-text-muted\">\n or click to browse — accepts any image format, multiple files\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"sr-only\"\n onChange={handleFileInput}\n />\n </div>\n\n {/* Previews */}\n {images.length > 0 && (\n <div className=\"flex flex-wrap gap-3\">\n {images.map((img, i) => (\n <div key={img.url} className=\"relative group\">\n <img\n src={img.url}\n alt={`Product image ${i + 1}`}\n className=\"h-20 w-20 rounded-input object-cover border border-border-subtle\"\n />\n {i === 0 && (\n <span className=\"absolute bottom-0 left-0 right-0 rounded-b-input bg-bg-surface/80 text-center text-[10px] font-medium py-0.5 text-text-secondary\">\n Main\n </span>\n )}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeImage(i);\n }}\n className=\"absolute -top-1.5 -right-1.5 hidden group-hover:flex h-5 w-5 items-center justify-center rounded-full bg-bg-surface border border-border-subtle text-text-muted hover:text-status-error-text hover:border-status-error-text/40 transition-colors\"\n aria-label=\"Remove image\"\n >\n <X size={11} />\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Tip */}\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n At least 1 image helps buyers — high quality photos increase\n conversions.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 3: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords to help buyers find your item.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 5: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <InputField\n label=\"Weight (kg)\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n )}\n\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Delivery fees are configured at the organization level in store\n settings.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 6: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n </div>\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!isValid || submitting}\n aria-disabled={!isValid || submitting}\n title={\n !isValid\n ? \"Fill in all required fields to continue\"\n : undefined\n }\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\",\n )}\n >\n {submitting ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Submitting...\n </>\n ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreatePhysicalItemPage.js","names":[],"sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent, DragEvent } from \"react\";\nimport { useState, useCallback, useRef } from \"react\";\nimport { Loader2, Upload, X, Info } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface ImagePreview {\n file: File;\n url: string;\n}\n\ninterface FormState {\n name: string;\n slug: string;\n type: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n requiresShipping: boolean;\n weightKg: string;\n brand: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst PRODUCT_CATEGORIES = [\n { value: \"MERCHANDISE\", label: \"Merchandise\" },\n { value: \"HARDWARE\", label: \"Hardware\" },\n { value: \"ACCESSORY\", label: \"Accessory\" },\n { value: \"BOOK\", label: \"Book\" },\n { value: \"EQUIPMENT\", label: \"Equipment\" },\n { value: \"APPAREL\", label: \"Apparel\" },\n { value: \"COLLECTIBLE\", label: \"Collectible\" },\n { value: \"CONSUMABLE\", label: \"Consumable\" },\n];\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"slug\",\n \"type\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL strings ──────────────────────────────────────────────────────────\n\nconst MY_PUBLISHER_QUERY = /* GraphQL */ `\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n`;\n\nconst ORG_QUERY = /* GraphQL */ `\n query SellerOrgForPublisher {\n developerOrganizations(limit: 1, offset: 0) {\n edges {\n node {\n name\n contactEmail\n websiteUrl\n legalEntityName\n taxCountry\n }\n }\n }\n }\n`;\n\nconst REGISTER_PUBLISHER_MUTATION = /* GraphQL */ `\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n`;\n\nconst CREATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction deriveSlug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return value.trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const CreatePhysicalItemPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n requiresShipping: true,\n weightKg: \"\",\n brand: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [images, setImages] = useState<ImagePreview[]>([]);\n const [isDragOver, setIsDragOver] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const isValid = isFormValid(form);\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n slug: deriveSlug(name),\n }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Image handling ───────────────────────────────────────────────────────\n\n const addFiles = useCallback((files: FileList | File[]) => {\n const incoming = Array.from(files).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n const previews: ImagePreview[] = incoming.map((file) => ({\n file,\n url: URL.createObjectURL(file),\n }));\n setImages((prev) => [...prev, ...previews]);\n }, []);\n\n const handleFileInput = (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files) addFiles(e.target.files);\n e.target.value = \"\";\n };\n\n const handleDrop = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(false);\n if (e.dataTransfer.files) addFiles(e.dataTransfer.files);\n };\n\n const handleDragOver = (e: DragEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragOver(true);\n };\n\n const handleDragLeave = () => setIsDragOver(false);\n\n const removeImage = (index: number) => {\n setImages((prev) => {\n const next = [...prev];\n URL.revokeObjectURL(next[index]!.url);\n next.splice(index, 1);\n return next;\n });\n };\n\n // ─── GraphQL fetch helper ─────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n // ─── Submit flow ──────────────────────────────────────────────────────────\n\n const handleSubmit = async () => {\n if (!isValid || submitting) return;\n setSubmitting(true);\n setSubmitError(null);\n\n try {\n // Step 1: check if publisher exists\n const publisherData = await gqlFetch<{\n myPublisher: { id: string } | null;\n }>(MY_PUBLISHER_QUERY);\n\n let publisherId = publisherData.myPublisher?.id ?? null;\n\n // Step 2: auto-register publisher if needed\n if (!publisherId) {\n const orgData = await gqlFetch<{\n developerOrganizations: {\n edges: {\n node: {\n name: string;\n contactEmail: string | null;\n websiteUrl: string | null;\n legalEntityName: string | null;\n taxCountry: string | null;\n };\n }[];\n };\n }>(ORG_QUERY);\n\n const org =\n orgData.developerOrganizations.edges[0]?.node ?? null;\n\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: org?.legalEntityName ?? org?.name ?? \"My Store\",\n email: org?.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: org?.websiteUrl ?? null,\n legalName: org?.legalEntityName ?? org?.name ?? \"My Store\",\n country: org?.taxCountry ?? null,\n contactEmail: org?.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 3: create the product\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n const productData = await gqlFetch<{\n createPhysicalProduct: { id: string };\n }>(CREATE_PHYSICAL_PRODUCT_MUTATION, {\n input: {\n name: form.name.trim(),\n slug: form.slug.trim(),\n type: form.type,\n shortDescription: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n requiresShipping: form.requiresShipping,\n weightKg:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n brand: form.brand.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n },\n });\n\n const productId = productData.createPhysicalProduct.id;\n\n // Step 4: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: productId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 5: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error ? err.message : \"Submission failed. Please try again.\",\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <PageLayout\n title=\"List a Physical Item\"\n description=\"Fill in all required details. Your listing will be reviewed before it appears in the store.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Your listing has been submitted for review! Redirecting...\n </div>\n )}\n\n {/* Error banner */}\n {submitError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Slug\"\n required\n value={form.slug}\n onChange={(e) => setField(\"slug\", e.target.value)}\n placeholder=\"auto-derived-from-name\"\n hint=\"URL-friendly identifier. Auto-derived from name; you can edit it.\"\n />\n\n <SelectField\n label=\"Product Category\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a category\"\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Product Images ── */}\n <PageSection\n title=\"Product Images\"\n description=\"The first image is used as the main icon.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-4\">\n {/* Drop zone */}\n <div\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onClick={() => fileInputRef.current?.click()}\n className={cn(\n \"flex flex-col items-center justify-center gap-3 rounded-input border-2 border-dashed px-6 py-10 cursor-pointer transition-colors\",\n isDragOver\n ? \"border-action-primary-bg bg-action-primary-bg/5\"\n : \"border-border-subtle hover:border-border-strong\",\n )}\n >\n <Upload\n size={28}\n className={isDragOver ? \"text-action-primary-bg\" : \"text-text-muted\"}\n />\n <div className=\"text-center space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n Drag and drop images here\n </p>\n <p className=\"text-xs text-text-muted\">\n or click to browse — accepts any image format, multiple files\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"sr-only\"\n onChange={handleFileInput}\n />\n </div>\n\n {/* Previews */}\n {images.length > 0 && (\n <div className=\"flex flex-wrap gap-3\">\n {images.map((img, i) => (\n <div key={img.url} className=\"relative group\">\n <img\n src={img.url}\n alt={`Product image ${i + 1}`}\n className=\"h-20 w-20 rounded-input object-cover border border-border-subtle\"\n />\n {i === 0 && (\n <span className=\"absolute bottom-0 left-0 right-0 rounded-b-input bg-bg-surface/80 text-center text-[10px] font-medium py-0.5 text-text-secondary\">\n Main\n </span>\n )}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeImage(i);\n }}\n className=\"absolute -top-1.5 -right-1.5 hidden group-hover:flex h-5 w-5 items-center justify-center rounded-full bg-bg-surface border border-border-subtle text-text-muted hover:text-status-error-text hover:border-status-error-text/40 transition-colors\"\n aria-label=\"Remove image\"\n >\n <X size={11} />\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Tip */}\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n At least 1 image helps buyers — high quality photos increase\n conversions.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 3: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords to help buyers find your item.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 5: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <InputField\n label=\"Weight (kg)\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n )}\n\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Delivery fees are configured at the organization level in store\n settings.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 6: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n </div>\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!isValid || submitting}\n aria-disabled={!isValid || submitting}\n title={\n !isValid\n ? \"Fill in all required fields to continue\"\n : undefined\n }\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\",\n )}\n >\n {submitting ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Submitting...\n </>\n ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -70,6 +70,7 @@ var C = () => {
70
70
  "Content-Type": "application/json",
71
71
  ...T ? { Authorization: `Bearer ${T}` } : {}
72
72
  },
73
+ credentials: "include",
73
74
  body: JSON.stringify({
74
75
  query: e,
75
76
  variables: t
@@ -1 +1 @@
1
- {"version":3,"file":"EditPhysicalItemPage.js","names":[],"sources":["../../../src/devportal/pages/EditPhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent } from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { Loader2, AlertCircle, Info } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface FormState {\n name: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n requiresShipping: boolean;\n weightKg: string;\n brand: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL ──────────────────────────────────────────────────────────────────\n\nconst GET_PRODUCT_QUERY = /* GraphQL */ `\n query GetPhysicalProduct($id: ID!) {\n product(id: $id) {\n id\n name\n slug\n type\n status\n nature\n description\n longDescription\n price\n currency\n tags\n stockQuantity\n sku\n trackInventory\n requiresShipping\n weight\n brand\n countryOfOrigin\n warrantyInfo\n returnPolicy\n }\n }\n`;\n\nconst UPDATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation UpdatePhysicalProduct($id: ID!, $input: UpdatePhysicalProductInput!) {\n updatePhysicalProduct(id: $id, input: $input) {\n id\n name\n status\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProductForReview($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return String(value).trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const EditPhysicalItemPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n const { itemId } = useParams<{ itemId: string }>();\n\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n const [loadingProduct, setLoadingProduct] = useState(true);\n const [loadError, setLoadError] = useState<string | null>(null);\n const [productStatus, setProductStatus] = useState<string | null>(null);\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"USD\",\n tags: \"\",\n stockQuantity: \"0\",\n sku: \"\",\n trackInventory: true,\n requiresShipping: true,\n weightKg: \"\",\n brand: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saveSuccess, setSaveSuccess] = useState(false);\n\n const isValid = isFormValid(form);\n\n // ─── GQL helper ───────────────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n // ─── Load product ─────────────────────────────────────────────────────────\n\n useEffect(() => {\n if (!itemId) return;\n let cancelled = false;\n setLoadingProduct(true);\n setLoadError(null);\n\n const run = async () => {\n try {\n const data = await gqlFetch<{\n product: {\n id: string;\n name: string;\n status: string;\n description: string | null;\n longDescription: string | null;\n price: number;\n currency: string;\n tags: string[] | null;\n stockQuantity: number;\n sku: string | null;\n trackInventory: boolean | null;\n requiresShipping: boolean | null;\n weight: number | null;\n brand: string | null;\n countryOfOrigin: string | null;\n warrantyInfo: string | null;\n returnPolicy: string | null;\n } | null;\n }>(GET_PRODUCT_QUERY, { id: itemId });\n\n if (cancelled) return;\n const p = data.product;\n if (!p) {\n setLoadError(\"Product not found.\");\n return;\n }\n\n setProductStatus(p.status);\n setForm({\n name: p.name,\n shortDescription: p.description ?? \"\",\n longDescription: p.longDescription ?? \"\",\n price: String(p.price),\n currency: p.currency || \"USD\",\n tags: (p.tags ?? []).join(\", \"),\n stockQuantity: String(p.stockQuantity),\n sku: p.sku ?? \"\",\n trackInventory: p.trackInventory ?? true,\n requiresShipping: p.requiresShipping ?? true,\n weightKg: p.weight != null ? String(p.weight) : \"\",\n brand: p.brand ?? \"\",\n countryOfOrigin: p.countryOfOrigin ?? \"\",\n warrantyInfo: p.warrantyInfo ?? \"\",\n returnPolicy: p.returnPolicy ?? \"\",\n });\n } catch (err) {\n if (!cancelled) {\n setLoadError(\n err instanceof Error ? err.message : \"Failed to load product\",\n );\n }\n } finally {\n if (!cancelled) setLoadingProduct(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [itemId, endpoint, authToken]);\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Save ─────────────────────────────────────────────────────────────────\n\n const handleSave = async (andResubmit = false) => {\n if (!isValid || saving || !itemId) return;\n setSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n await gqlFetch(UPDATE_PHYSICAL_PRODUCT_MUTATION, {\n id: itemId,\n input: {\n name: form.name.trim(),\n description: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n requiresShipping: form.requiresShipping,\n weight:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n brand: form.brand.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n },\n });\n\n if (andResubmit) {\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: itemId,\n notes: \"Resubmitted after edits\",\n });\n }\n\n setSaveSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 1500);\n } catch (err) {\n setSaveError(\n err instanceof Error ? err.message : \"Save failed. Please try again.\",\n );\n } finally {\n setSaving(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n if (loadingProduct) {\n return (\n <PageLayout title=\"Edit Item\" backLink=\"/seller/items\" backLabel=\"Back to My Items\">\n <div className=\"flex items-center justify-center py-20\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n </PageLayout>\n );\n }\n\n if (loadError) {\n return (\n <PageLayout title=\"Edit Item\" backLink=\"/seller/items\" backLabel=\"Back to My Items\">\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-border/50 bg-status-error-bg/10 px-4 py-3 text-sm\">\n <AlertCircle size={16} className=\"shrink-0 text-status-error-text\" />\n <span className=\"text-status-error-text\">{loadError}</span>\n </div>\n </PageLayout>\n );\n }\n\n const isDraft = productStatus === \"DRAFT\" || productStatus === \"REJECTED\";\n const canResubmit = isDraft;\n\n return (\n <PageLayout\n title={`Edit: ${form.name || \"Item\"}`}\n description=\"Update your listing details. Changes take effect immediately for drafts; published items may require re-review.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {saveSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Saved successfully! Redirecting to your items...\n </div>\n )}\n\n {/* Error banner */}\n {saveError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {saveError}\n </div>\n )}\n\n {/* Status notice */}\n {productStatus && (\n <div className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Current status:{\" \"}\n <span className=\"font-medium text-text-primary\">\n {productStatus.replace(/_/g, \" \")}\n </span>\n {canResubmit && (\n <span className=\"ml-1\">\n — use \"Save & Resubmit\" to send it for review again.\n </span>\n )}\n </div>\n )}\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={(e) => setField(\"name\", e.target.value)}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 3: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <InputField\n label=\"Weight (kg)\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n )}\n </div>\n </PageSection>\n\n {/* ── Section 5: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n </div>\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex flex-wrap items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <button\n type=\"button\"\n onClick={() => void handleSave(false)}\n disabled={!isValid || saving}\n aria-disabled={!isValid || saving}\n title={!isValid ? \"Fill in all required fields to continue\" : undefined}\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"border border-border-strong bg-bg-surface text-text-primary\",\n \"hover:bg-bg-sunken\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n )}\n >\n {saving ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Saving...\n </>\n ) : (\n \"Save Changes\"\n )}\n </button>\n\n {canResubmit && (\n <button\n type=\"button\"\n onClick={() => void handleSave(true)}\n disabled={!isValid || saving}\n aria-disabled={!isValid || saving}\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n )}\n >\n {saving ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Saving...\n </>\n ) : (\n \"Save & Resubmit for Review\"\n )}\n </button>\n )}\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"EditPhysicalItemPage.js","names":[],"sources":["../../../src/devportal/pages/EditPhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent } from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport { Loader2, AlertCircle, Info } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { cn } from \"../utils/cn\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface FormState {\n name: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n requiresShipping: boolean;\n weightKg: string;\n brand: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL ──────────────────────────────────────────────────────────────────\n\nconst GET_PRODUCT_QUERY = /* GraphQL */ `\n query GetPhysicalProduct($id: ID!) {\n product(id: $id) {\n id\n name\n slug\n type\n status\n nature\n description\n longDescription\n price\n currency\n tags\n stockQuantity\n sku\n trackInventory\n requiresShipping\n weight\n brand\n countryOfOrigin\n warrantyInfo\n returnPolicy\n }\n }\n`;\n\nconst UPDATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation UpdatePhysicalProduct($id: ID!, $input: UpdatePhysicalProductInput!) {\n updatePhysicalProduct(id: $id, input: $input) {\n id\n name\n status\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProductForReview($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return String(value).trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const EditPhysicalItemPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n const { itemId } = useParams<{ itemId: string }>();\n\n const endpoint = `${apiGatewayUrl || \"\"}/global/graphql`;\n\n const [loadingProduct, setLoadingProduct] = useState(true);\n const [loadError, setLoadError] = useState<string | null>(null);\n const [productStatus, setProductStatus] = useState<string | null>(null);\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"USD\",\n tags: \"\",\n stockQuantity: \"0\",\n sku: \"\",\n trackInventory: true,\n requiresShipping: true,\n weightKg: \"\",\n brand: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saveSuccess, setSaveSuccess] = useState(false);\n\n const isValid = isFormValid(form);\n\n // ─── GQL helper ───────────────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n // ─── Load product ─────────────────────────────────────────────────────────\n\n useEffect(() => {\n if (!itemId) return;\n let cancelled = false;\n setLoadingProduct(true);\n setLoadError(null);\n\n const run = async () => {\n try {\n const data = await gqlFetch<{\n product: {\n id: string;\n name: string;\n status: string;\n description: string | null;\n longDescription: string | null;\n price: number;\n currency: string;\n tags: string[] | null;\n stockQuantity: number;\n sku: string | null;\n trackInventory: boolean | null;\n requiresShipping: boolean | null;\n weight: number | null;\n brand: string | null;\n countryOfOrigin: string | null;\n warrantyInfo: string | null;\n returnPolicy: string | null;\n } | null;\n }>(GET_PRODUCT_QUERY, { id: itemId });\n\n if (cancelled) return;\n const p = data.product;\n if (!p) {\n setLoadError(\"Product not found.\");\n return;\n }\n\n setProductStatus(p.status);\n setForm({\n name: p.name,\n shortDescription: p.description ?? \"\",\n longDescription: p.longDescription ?? \"\",\n price: String(p.price),\n currency: p.currency || \"USD\",\n tags: (p.tags ?? []).join(\", \"),\n stockQuantity: String(p.stockQuantity),\n sku: p.sku ?? \"\",\n trackInventory: p.trackInventory ?? true,\n requiresShipping: p.requiresShipping ?? true,\n weightKg: p.weight != null ? String(p.weight) : \"\",\n brand: p.brand ?? \"\",\n countryOfOrigin: p.countryOfOrigin ?? \"\",\n warrantyInfo: p.warrantyInfo ?? \"\",\n returnPolicy: p.returnPolicy ?? \"\",\n });\n } catch (err) {\n if (!cancelled) {\n setLoadError(\n err instanceof Error ? err.message : \"Failed to load product\",\n );\n }\n } finally {\n if (!cancelled) setLoadingProduct(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [itemId, endpoint, authToken]);\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── Save ─────────────────────────────────────────────────────────────────\n\n const handleSave = async (andResubmit = false) => {\n if (!isValid || saving || !itemId) return;\n setSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n await gqlFetch(UPDATE_PHYSICAL_PRODUCT_MUTATION, {\n id: itemId,\n input: {\n name: form.name.trim(),\n description: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n requiresShipping: form.requiresShipping,\n weight:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n brand: form.brand.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n },\n });\n\n if (andResubmit) {\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: itemId,\n notes: \"Resubmitted after edits\",\n });\n }\n\n setSaveSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 1500);\n } catch (err) {\n setSaveError(\n err instanceof Error ? err.message : \"Save failed. Please try again.\",\n );\n } finally {\n setSaving(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n if (loadingProduct) {\n return (\n <PageLayout title=\"Edit Item\" backLink=\"/seller/items\" backLabel=\"Back to My Items\">\n <div className=\"flex items-center justify-center py-20\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n </PageLayout>\n );\n }\n\n if (loadError) {\n return (\n <PageLayout title=\"Edit Item\" backLink=\"/seller/items\" backLabel=\"Back to My Items\">\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-border/50 bg-status-error-bg/10 px-4 py-3 text-sm\">\n <AlertCircle size={16} className=\"shrink-0 text-status-error-text\" />\n <span className=\"text-status-error-text\">{loadError}</span>\n </div>\n </PageLayout>\n );\n }\n\n const isDraft = productStatus === \"DRAFT\" || productStatus === \"REJECTED\";\n const canResubmit = isDraft;\n\n return (\n <PageLayout\n title={`Edit: ${form.name || \"Item\"}`}\n description=\"Update your listing details. Changes take effect immediately for drafts; published items may require re-review.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {saveSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Saved successfully! Redirecting to your items...\n </div>\n )}\n\n {/* Error banner */}\n {saveError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {saveError}\n </div>\n )}\n\n {/* Status notice */}\n {productStatus && (\n <div className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Current status:{\" \"}\n <span className=\"font-medium text-text-primary\">\n {productStatus.replace(/_/g, \" \")}\n </span>\n {canResubmit && (\n <span className=\"ml-1\">\n — use \"Save & Resubmit\" to send it for review again.\n </span>\n )}\n </div>\n )}\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={(e) => setField(\"name\", e.target.value)}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 3: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <InputField\n label=\"Weight (kg)\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n )}\n </div>\n </PageSection>\n\n {/* ── Section 5: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n </div>\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex flex-wrap items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <button\n type=\"button\"\n onClick={() => void handleSave(false)}\n disabled={!isValid || saving}\n aria-disabled={!isValid || saving}\n title={!isValid ? \"Fill in all required fields to continue\" : undefined}\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"border border-border-strong bg-bg-surface text-text-primary\",\n \"hover:bg-bg-sunken\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n )}\n >\n {saving ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Saving...\n </>\n ) : (\n \"Save Changes\"\n )}\n </button>\n\n {canResubmit && (\n <button\n type=\"button\"\n onClick={() => void handleSave(true)}\n disabled={!isValid || saving}\n aria-disabled={!isValid || saving}\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n )}\n >\n {saving ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Saving...\n </>\n ) : (\n \"Save & Resubmit for Review\"\n )}\n </button>\n )}\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-devportal",
3
- "version": "2026.713.1",
3
+ "version": "2026.713.2",
4
4
  "description": "Burdenoff Developer Portal Microfrontend - Complete developer operations interface",
5
5
  "type": "module",
6
6
  "files": [