@burdenoff/microfe-devportal 2026.728.2 → 2026.801.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/utils/renderWithProviders.d.ts +1 -1
- package/dist/__tests__/utils/renderWithProviders.d.ts.map +1 -1
- package/dist/devportal/AdminDevportalRoutes.js +14 -14
- package/dist/devportal/AdminDevportalRoutes.js.map +1 -1
- package/dist/devportal/DevportalRoutes.js +115 -115
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/components/SectionNav.js +0 -1
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/components/TelemetryErrorBoundary.js +7 -7
- package/dist/devportal/components/TelemetryErrorBoundary.js.map +1 -1
- package/dist/devportal/pages/AnalyticsDashboardPage.js +41 -41
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +27 -27
- package/dist/devportal/pages/AppLogsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +929 -929
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +39 -39
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +55 -55
- package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +36 -36
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/EditPhysicalItemPage.js +40 -40
- package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/InvitationDecisionPage.js +25 -25
- package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +36 -36
- package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
- package/dist/devportal/pages/OAuthCallbackPage.js +10 -10
- package/dist/devportal/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js +27 -27
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +329 -329
- package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationMembersPage.js +181 -181
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/PublishPage.js +43 -43
- package/dist/devportal/pages/PublishPage.js.map +1 -1
- package/dist/devportal/pages/RevenueDashboardPage.js +65 -65
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
- package/dist/devportal/pages/StoreSubmissionPage.js +126 -126
- package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +28 -28
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +47 -47
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/providers/DevportalProvider.js +12 -12
- package/dist/devportal/providers/DevportalProvider.js.map +1 -1
- package/dist/node_modules/react-router/dist/production/lib/components.js +124 -0
- package/dist/node_modules/react-router/dist/production/lib/components.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/context.js +32 -0
- package/dist/node_modules/react-router/dist/production/lib/context.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/dom.js +83 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/dom.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/lib.js +358 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/lib.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/components.js +188 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/components.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/invariant.js +8 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/invariant.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/links.js +75 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/links.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/markup.js +15 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/markup.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/routeModules.js +18 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/routeModules.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/single-fetch.js +10 -0
- package/dist/node_modules/react-router/dist/production/lib/dom/ssr/single-fetch.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/errors.js +19 -0
- package/dist/node_modules/react-router/dist/production/lib/errors.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/hooks.js +296 -0
- package/dist/node_modules/react-router/dist/production/lib/hooks.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/router/history.js +166 -0
- package/dist/node_modules/react-router/dist/production/lib/router/history.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/router/router.js +33 -0
- package/dist/node_modules/react-router/dist/production/lib/router/router.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/router/url.js +9 -0
- package/dist/node_modules/react-router/dist/production/lib/router/url.js.map +1 -0
- package/dist/node_modules/react-router/dist/production/lib/router/utils.js +236 -0
- package/dist/node_modules/react-router/dist/production/lib/router/utils.js.map +1 -0
- package/package.json +3 -3
|
@@ -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(\n $id: ID!\n $input: UpdatePhysicalProductInput!\n ) {\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\n title=\"Edit Item\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\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\n title=\"Edit Item\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\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={\n !isValid ? \"Fill in all required fields to continue\" : 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 \"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\";\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(\n $id: ID!\n $input: UpdatePhysicalProductInput!\n ) {\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\n title=\"Edit Item\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\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\n title=\"Edit Item\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\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={\n !isValid ? \"Fill in all required fields to continue\" : 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 \"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,42 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useParams as e } from "../../node_modules/react-router/dist/production/lib/hooks.js";
|
|
2
|
+
import { useDevportal as t } from "../providers/DevportalProvider.js";
|
|
3
|
+
import { EmptyState as n } from "../components/EmptyState.js";
|
|
4
|
+
import { useAcceptInvitationMutation as r, useGetInvitationByTokenQuery as i, useRejectInvitationMutation as a } from "../../generated/global-operations.js";
|
|
5
|
+
import { PageLayout as o, PageSection as s } from "../components/PageLayout.js";
|
|
5
6
|
import "../hooks/index.js";
|
|
6
|
-
import { CheckCircle as
|
|
7
|
-
import { useParams as f } from "react-router-dom";
|
|
7
|
+
import { CheckCircle as c, Loader2 as l, Mail as u, Shield as d, XCircle as f } from "lucide-react";
|
|
8
8
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
9
9
|
//#region src/devportal/pages/InvitationDecisionPage.tsx
|
|
10
10
|
var h = () => {
|
|
11
|
-
let { token: h } =
|
|
11
|
+
let { token: h } = e(), { navigate: g, basePath: _ } = t(), { data: v, loading: y, error: b } = i({
|
|
12
12
|
variables: { token: h ?? "" },
|
|
13
13
|
skip: !h,
|
|
14
14
|
fetchPolicy: "cache-and-network"
|
|
15
|
-
}), [x, { loading: S, data: C }] =
|
|
15
|
+
}), [x, { loading: S, data: C }] = r(), [w, { loading: T, data: E }] = a(), D = v?.developerOrgInvitationByToken, O = C?.acceptDeveloperOrgInvitation, k = E?.rejectDeveloperOrgInvitation, A = async () => {
|
|
16
16
|
h && await x({ variables: { token: h } });
|
|
17
17
|
}, j = async () => {
|
|
18
18
|
h && await w({ variables: { token: h } });
|
|
19
19
|
};
|
|
20
|
-
return y && !D ? /* @__PURE__ */ p(
|
|
20
|
+
return y && !D ? /* @__PURE__ */ p(o, {
|
|
21
21
|
title: "Developer Team Invitation",
|
|
22
22
|
children: /* @__PURE__ */ p("div", {
|
|
23
23
|
className: "flex items-center justify-center py-16 text-text-secondary",
|
|
24
|
-
children: /* @__PURE__ */ p(
|
|
24
|
+
children: /* @__PURE__ */ p(l, { className: "h-8 w-8 animate-spin" })
|
|
25
25
|
})
|
|
26
|
-
}) : b || !D ? /* @__PURE__ */ p(
|
|
26
|
+
}) : b || !D ? /* @__PURE__ */ p(o, {
|
|
27
27
|
title: "Developer Team Invitation",
|
|
28
|
-
children: /* @__PURE__ */ p(
|
|
29
|
-
icon: /* @__PURE__ */ p(
|
|
28
|
+
children: /* @__PURE__ */ p(n, {
|
|
29
|
+
icon: /* @__PURE__ */ p(f, {
|
|
30
30
|
size: 32,
|
|
31
31
|
className: "text-status-error-text"
|
|
32
32
|
}),
|
|
33
33
|
title: "Invitation not available",
|
|
34
34
|
description: b?.message || "This invitation is invalid, expired, or not available to your signed-in account."
|
|
35
35
|
})
|
|
36
|
-
}) : O ? /* @__PURE__ */ p(
|
|
36
|
+
}) : O ? /* @__PURE__ */ p(o, {
|
|
37
37
|
title: "Invitation accepted",
|
|
38
|
-
children: /* @__PURE__ */ p(
|
|
39
|
-
icon: /* @__PURE__ */ p(
|
|
38
|
+
children: /* @__PURE__ */ p(n, {
|
|
39
|
+
icon: /* @__PURE__ */ p(c, {
|
|
40
40
|
size: 32,
|
|
41
41
|
className: "text-status-success-text"
|
|
42
42
|
}),
|
|
@@ -49,10 +49,10 @@ var h = () => {
|
|
|
49
49
|
children: "Open organization"
|
|
50
50
|
})
|
|
51
51
|
})
|
|
52
|
-
}) : k ? /* @__PURE__ */ p(
|
|
52
|
+
}) : k ? /* @__PURE__ */ p(o, {
|
|
53
53
|
title: "Invitation declined",
|
|
54
|
-
children: /* @__PURE__ */ p(
|
|
55
|
-
icon: /* @__PURE__ */ p(
|
|
54
|
+
children: /* @__PURE__ */ p(n, {
|
|
55
|
+
icon: /* @__PURE__ */ p(f, {
|
|
56
56
|
size: 32,
|
|
57
57
|
className: "text-status-warning-text"
|
|
58
58
|
}),
|
|
@@ -65,12 +65,12 @@ var h = () => {
|
|
|
65
65
|
children: "Return to Devportal"
|
|
66
66
|
})
|
|
67
67
|
})
|
|
68
|
-
}) : /* @__PURE__ */ p(
|
|
68
|
+
}) : /* @__PURE__ */ p(o, {
|
|
69
69
|
title: "Developer Team Invitation",
|
|
70
70
|
description: "Review your invitation to join a developer organization",
|
|
71
71
|
children: /* @__PURE__ */ m("div", {
|
|
72
72
|
className: "mx-auto max-w-2xl space-y-6",
|
|
73
|
-
children: [/* @__PURE__ */ p(
|
|
73
|
+
children: [/* @__PURE__ */ p(s, {
|
|
74
74
|
title: "Invitation details",
|
|
75
75
|
children: /* @__PURE__ */ m("div", {
|
|
76
76
|
className: "rounded-card border border-border-seam bg-bg-surface p-6 space-y-4",
|
|
@@ -89,13 +89,13 @@ var h = () => {
|
|
|
89
89
|
children: "Invited email"
|
|
90
90
|
}), /* @__PURE__ */ m("p", {
|
|
91
91
|
className: "flex items-center gap-2 text-text-primary",
|
|
92
|
-
children: [/* @__PURE__ */ p(
|
|
92
|
+
children: [/* @__PURE__ */ p(u, { size: 14 }), D.invitedEmail]
|
|
93
93
|
})] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
|
|
94
94
|
className: "text-sm text-text-secondary",
|
|
95
95
|
children: "Role"
|
|
96
96
|
}), /* @__PURE__ */ m("p", {
|
|
97
97
|
className: "flex items-center gap-2 text-text-primary",
|
|
98
|
-
children: [/* @__PURE__ */ p(
|
|
98
|
+
children: [/* @__PURE__ */ p(d, { size: 14 }), D.role]
|
|
99
99
|
})] })]
|
|
100
100
|
}),
|
|
101
101
|
D.message ? /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
|
|
@@ -118,7 +118,7 @@ var h = () => {
|
|
|
118
118
|
onClick: () => void j(),
|
|
119
119
|
disabled: S || T,
|
|
120
120
|
className: "inline-flex items-center gap-2 rounded-md border border-border-seam px-4 py-2 text-sm font-medium hover:bg-bg-sunken disabled:opacity-50",
|
|
121
|
-
children: [T ? /* @__PURE__ */ p(
|
|
121
|
+
children: [T ? /* @__PURE__ */ p(l, {
|
|
122
122
|
size: 14,
|
|
123
123
|
className: "animate-spin"
|
|
124
124
|
}) : null, "Decline"]
|
|
@@ -127,7 +127,7 @@ var h = () => {
|
|
|
127
127
|
onClick: () => void A(),
|
|
128
128
|
disabled: S || T,
|
|
129
129
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
130
|
-
children: [S ? /* @__PURE__ */ p(
|
|
130
|
+
children: [S ? /* @__PURE__ */ p(l, {
|
|
131
131
|
size: 14,
|
|
132
132
|
className: "animate-spin"
|
|
133
133
|
}) : null, "Accept invitation"]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvitationDecisionPage.js","names":[],"sources":["../../../src/devportal/pages/InvitationDecisionPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useParams } from \"react-router
|
|
1
|
+
{"version":3,"file":"InvitationDecisionPage.js","names":[],"sources":["../../../src/devportal/pages/InvitationDecisionPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useParams } from \"react-router\";\nimport { CheckCircle, Loader2, Mail, Shield, XCircle } from \"lucide-react\";\n\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n useAcceptInvitationMutation,\n useGetInvitationByTokenQuery,\n useRejectInvitationMutation,\n} from \"../hooks\";\n\nexport const InvitationDecisionPage: FC = () => {\n const { token } = useParams<{ token: string }>();\n const { navigate, basePath } = useDevportal();\n\n const { data, loading, error } = useGetInvitationByTokenQuery({\n variables: { token: token ?? \"\" },\n skip: !token,\n fetchPolicy: \"cache-and-network\",\n });\n\n const [acceptInvitation, { loading: accepting, data: acceptedData }] =\n useAcceptInvitationMutation();\n const [rejectInvitation, { loading: rejecting, data: rejectedData }] =\n useRejectInvitationMutation();\n\n const invitation = data?.developerOrgInvitationByToken;\n const acceptedMember = acceptedData?.acceptDeveloperOrgInvitation;\n const rejected = rejectedData?.rejectDeveloperOrgInvitation;\n\n const handleAccept = async () => {\n if (!token) return;\n await acceptInvitation({ variables: { token } });\n };\n\n const handleReject = async () => {\n if (!token) return;\n await rejectInvitation({ variables: { token } });\n };\n\n if (loading && !invitation) {\n return (\n <PageLayout title=\"Developer Team Invitation\">\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (error || !invitation) {\n return (\n <PageLayout title=\"Developer Team Invitation\">\n <EmptyState\n icon={<XCircle size={32} className=\"text-status-error-text\" />}\n title=\"Invitation not available\"\n description={\n error?.message ||\n \"This invitation is invalid, expired, or not available to your signed-in account.\"\n }\n />\n </PageLayout>\n );\n }\n\n if (acceptedMember) {\n return (\n <PageLayout title=\"Invitation accepted\">\n <EmptyState\n icon={<CheckCircle size={32} className=\"text-status-success-text\" />}\n title=\"You joined the developer organization\"\n description={`You now have ${invitation.role.toLowerCase()} access to ${invitation.organization.name}.`}\n action={\n <button\n type=\"button\"\n onClick={() =>\n navigate?.(\n `${basePath}/organizations/${invitation.organization.id}`,\n )\n }\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n Open organization\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n if (rejected) {\n return (\n <PageLayout title=\"Invitation declined\">\n <EmptyState\n icon={<XCircle size={32} className=\"text-status-warning-text\" />}\n title=\"Invitation declined\"\n description={`You declined the invitation to join ${invitation.organization.name}.`}\n action={\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}`)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-seam px-4 py-2 text-sm font-medium hover:bg-bg-sunken\"\n >\n Return to Devportal\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Developer Team Invitation\"\n description=\"Review your invitation to join a developer organization\"\n >\n <div className=\"mx-auto max-w-2xl space-y-6\">\n <PageSection title=\"Invitation details\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Organization</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {invitation.organization.name}\n </p>\n </div>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-sm text-text-secondary\">Invited email</p>\n <p className=\"flex items-center gap-2 text-text-primary\">\n <Mail size={14} />\n {invitation.invitedEmail}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Role</p>\n <p className=\"flex items-center gap-2 text-text-primary\">\n <Shield size={14} />\n {invitation.role}\n </p>\n </div>\n </div>\n {invitation.message ? (\n <div>\n <p className=\"text-sm text-text-secondary\">Message</p>\n <p className=\"rounded-md bg-bg-sunken px-3 py-2 text-sm text-text-primary whitespace-pre-line\">\n {invitation.message}\n </p>\n </div>\n ) : null}\n <p className=\"text-xs text-text-secondary\">\n Expires on {new Date(invitation.expiresAt).toLocaleString()}\n </p>\n </div>\n </PageSection>\n\n <div className=\"flex flex-wrap gap-3 justify-end\">\n <button\n type=\"button\"\n onClick={() => void handleReject()}\n disabled={accepting || rejecting}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-seam px-4 py-2 text-sm font-medium hover:bg-bg-sunken disabled:opacity-50\"\n >\n {rejecting ? <Loader2 size={14} className=\"animate-spin\" /> : null}\n Decline\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAccept()}\n disabled={accepting || rejecting}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {accepting ? <Loader2 size={14} className=\"animate-spin\" /> : null}\n Accept invitation\n </button>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { useParams as e } from "../../node_modules/react-router/dist/production/lib/hooks.js";
|
|
2
|
+
import { useDevportal as t } from "../providers/DevportalProvider.js";
|
|
3
|
+
import { useGetApplicationQuery as n } from "../../generated/global-operations.js";
|
|
4
|
+
import { PageLayout as r, PageSection as i } from "../components/PageLayout.js";
|
|
5
|
+
import { Alert as a } from "../components/ConfirmDialog.js";
|
|
6
|
+
import { PermissionGuard as o } from "../components/PermissionGuard.js";
|
|
6
7
|
import "../components/index.js";
|
|
7
|
-
import { useEffect as
|
|
8
|
-
import { Markdown as
|
|
9
|
-
import { useI18n as
|
|
10
|
-
import { CheckCircle as
|
|
11
|
-
import { useParams as y } from "react-router-dom";
|
|
8
|
+
import { useEffect as s } from "react";
|
|
9
|
+
import { Markdown as c } from "@burdenoff/fe-libs/shared/components";
|
|
10
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
+
import { CheckCircle as u, Download as d, ExternalLink as f, Eye as p, FileText as m, Globe as h, Loader2 as g, Shield as _, Star as v, Tag as y } from "lucide-react";
|
|
12
12
|
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
13
13
|
//#region src/devportal/pages/ListingPreviewPage.tsx
|
|
14
14
|
var S = {
|
|
@@ -35,44 +35,44 @@ var S = {
|
|
|
35
35
|
CLI_TOOL: "CLI Tool",
|
|
36
36
|
SERVICE: "Service"
|
|
37
37
|
}, w = () => {
|
|
38
|
-
let { appId: w } =
|
|
38
|
+
let { appId: w } = e(), { navigate: T, basePath: E } = t(), { t: D } = l(), O = (e, t) => {
|
|
39
39
|
let n = D(e);
|
|
40
40
|
return n === e ? t : n;
|
|
41
|
-
}, { data: k, loading: A, error: j } =
|
|
41
|
+
}, { data: k, loading: A, error: j } = n({
|
|
42
42
|
variables: { id: w },
|
|
43
43
|
skip: !w,
|
|
44
44
|
fetchPolicy: "cache-and-network"
|
|
45
45
|
}), M = k?.application;
|
|
46
|
-
if (
|
|
46
|
+
if (s(() => {
|
|
47
47
|
let e = M?.name;
|
|
48
48
|
if (!w || !e) return;
|
|
49
49
|
let t = window.__breadcrumbRegistry;
|
|
50
50
|
return t?.register(w, e), () => t?.unregister(w);
|
|
51
|
-
}, [w, M?.name]), A) return /* @__PURE__ */ b(
|
|
51
|
+
}, [w, M?.name]), A) return /* @__PURE__ */ b(r, {
|
|
52
52
|
title: O("devportal.marketplacePreview", "Marketplace Preview"),
|
|
53
53
|
description: O("devportal.loading", "Loading..."),
|
|
54
54
|
backLink: `/apps/${w}`,
|
|
55
55
|
backLabel: O("devportal.backToApplication", "Back to Application"),
|
|
56
56
|
children: /* @__PURE__ */ b("div", {
|
|
57
57
|
className: "flex items-center justify-center py-12",
|
|
58
|
-
children: /* @__PURE__ */ b(
|
|
58
|
+
children: /* @__PURE__ */ b(g, { className: "h-8 w-8 animate-spin text-text-secondary" })
|
|
59
59
|
})
|
|
60
60
|
});
|
|
61
|
-
if (j || !M) return /* @__PURE__ */ b(
|
|
61
|
+
if (j || !M) return /* @__PURE__ */ b(r, {
|
|
62
62
|
title: O("devportal.marketplacePreview", "Marketplace Preview"),
|
|
63
63
|
description: O("devportal.couldNotLoadApplication", "Could not load application"),
|
|
64
64
|
backLink: `/apps/${w}`,
|
|
65
65
|
backLabel: O("devportal.backToApplication", "Back to Application"),
|
|
66
|
-
children: /* @__PURE__ */ b(
|
|
66
|
+
children: /* @__PURE__ */ b(a, {
|
|
67
67
|
variant: "error",
|
|
68
68
|
title: O("devportal.failedToLoadApplication", "Failed to load application"),
|
|
69
69
|
children: j?.message ?? O("devportal.applicationNotFound", "Application not found")
|
|
70
70
|
})
|
|
71
71
|
});
|
|
72
72
|
let N = Array.isArray(M.screenshots) ? M.screenshots : [], P = M.status === "PUBLISHED";
|
|
73
|
-
return /* @__PURE__ */ b(
|
|
73
|
+
return /* @__PURE__ */ b(o, {
|
|
74
74
|
authOnly: !0,
|
|
75
|
-
children: /* @__PURE__ */ x(
|
|
75
|
+
children: /* @__PURE__ */ x(r, {
|
|
76
76
|
title: O("devportal.marketplacePreview", "Marketplace Preview"),
|
|
77
77
|
description: O("devportal.marketplacePreviewDescription", "Preview how your app will appear in the marketplace"),
|
|
78
78
|
purpose: O("devportal.marketplacePreviewPurpose", "See exactly how shoppers will experience your store listing — icon, screenshots, description, and pricing — before it goes live. Use it to catch missing assets or typos, then submit for review when the listing looks right."),
|
|
@@ -81,12 +81,12 @@ var S = {
|
|
|
81
81
|
actions: P ? void 0 : /* @__PURE__ */ x("button", {
|
|
82
82
|
onClick: () => T?.(`${E}/apps/${w}/publish`),
|
|
83
83
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
84
|
-
children: [/* @__PURE__ */ b(
|
|
84
|
+
children: [/* @__PURE__ */ b(p, { size: 16 }), O("devportal.submitForReview", "Submit for Review")]
|
|
85
85
|
}),
|
|
86
86
|
children: [
|
|
87
87
|
/* @__PURE__ */ x("div", {
|
|
88
88
|
className: "flex items-center gap-2 rounded-lg border border-border-subtle bg-status-warning-bg-subtle px-4 py-3 text-sm text-status-warning-text",
|
|
89
|
-
children: [/* @__PURE__ */ b(
|
|
89
|
+
children: [/* @__PURE__ */ b(p, {
|
|
90
90
|
size: 16,
|
|
91
91
|
className: "flex-shrink-0"
|
|
92
92
|
}), /* @__PURE__ */ b("span", { children: O("devportal.previewBannerText", "This is a preview of your marketplace listing. It shows how your app will appear to users after publication.") })]
|
|
@@ -129,7 +129,7 @@ var S = {
|
|
|
129
129
|
className: "text-2xl font-bold",
|
|
130
130
|
children: M.name
|
|
131
131
|
}),
|
|
132
|
-
M.isVerified && /* @__PURE__ */ b(
|
|
132
|
+
M.isVerified && /* @__PURE__ */ b(u, {
|
|
133
133
|
size: 20,
|
|
134
134
|
className: "text-primary"
|
|
135
135
|
}),
|
|
@@ -149,7 +149,7 @@ var S = {
|
|
|
149
149
|
M.averageRating != null && /* @__PURE__ */ x("div", {
|
|
150
150
|
className: "flex items-center gap-1",
|
|
151
151
|
children: [
|
|
152
|
-
/* @__PURE__ */ b(
|
|
152
|
+
/* @__PURE__ */ b(v, {
|
|
153
153
|
size: 14,
|
|
154
154
|
className: "text-status-warning-text fill-status-warning-text"
|
|
155
155
|
}),
|
|
@@ -168,7 +168,7 @@ var S = {
|
|
|
168
168
|
}),
|
|
169
169
|
/* @__PURE__ */ x("div", {
|
|
170
170
|
className: "flex items-center gap-1",
|
|
171
|
-
children: [/* @__PURE__ */ b(
|
|
171
|
+
children: [/* @__PURE__ */ b(d, { size: 14 }), /* @__PURE__ */ x("span", { children: [
|
|
172
172
|
(M.installCount ?? 0).toLocaleString(),
|
|
173
173
|
" ",
|
|
174
174
|
O("devportal.installs", "installs")
|
|
@@ -227,7 +227,7 @@ var S = {
|
|
|
227
227
|
children: [/* @__PURE__ */ b("h2", {
|
|
228
228
|
className: "text-base font-semibold mb-2",
|
|
229
229
|
children: O("devportal.about", "About")
|
|
230
|
-
}), M.description || M.shortDescription ? /* @__PURE__ */ b(
|
|
230
|
+
}), M.description || M.shortDescription ? /* @__PURE__ */ b(c, {
|
|
231
231
|
className: "text-sm text-text-secondary",
|
|
232
232
|
children: M.description ?? M.shortDescription ?? ""
|
|
233
233
|
}) : /* @__PURE__ */ b("p", {
|
|
@@ -241,7 +241,7 @@ var S = {
|
|
|
241
241
|
className: "flex flex-wrap gap-2",
|
|
242
242
|
children: M.tags.map((e) => /* @__PURE__ */ x("span", {
|
|
243
243
|
className: "inline-flex items-center gap-1 rounded-full border px-3 py-1 text-xs text-text-secondary",
|
|
244
|
-
children: [/* @__PURE__ */ b(
|
|
244
|
+
children: [/* @__PURE__ */ b(y, { size: 10 }), e]
|
|
245
245
|
}, e))
|
|
246
246
|
})
|
|
247
247
|
}),
|
|
@@ -254,9 +254,9 @@ var S = {
|
|
|
254
254
|
rel: "noopener noreferrer",
|
|
255
255
|
className: "flex items-center gap-2 text-primary hover:underline",
|
|
256
256
|
children: [
|
|
257
|
-
/* @__PURE__ */ b(
|
|
257
|
+
/* @__PURE__ */ b(h, { size: 14 }),
|
|
258
258
|
O("devportal.website", "Website"),
|
|
259
|
-
/* @__PURE__ */ b(
|
|
259
|
+
/* @__PURE__ */ b(f, { size: 12 })
|
|
260
260
|
]
|
|
261
261
|
}),
|
|
262
262
|
M.documentationUrl && /* @__PURE__ */ x("a", {
|
|
@@ -265,9 +265,9 @@ var S = {
|
|
|
265
265
|
rel: "noopener noreferrer",
|
|
266
266
|
className: "flex items-center gap-2 text-primary hover:underline",
|
|
267
267
|
children: [
|
|
268
|
-
/* @__PURE__ */ b(
|
|
268
|
+
/* @__PURE__ */ b(m, { size: 14 }),
|
|
269
269
|
O("devportal.documentation", "Documentation"),
|
|
270
|
-
/* @__PURE__ */ b(
|
|
270
|
+
/* @__PURE__ */ b(f, { size: 12 })
|
|
271
271
|
]
|
|
272
272
|
}),
|
|
273
273
|
M.privacyPolicyUrl && /* @__PURE__ */ x("a", {
|
|
@@ -276,9 +276,9 @@ var S = {
|
|
|
276
276
|
rel: "noopener noreferrer",
|
|
277
277
|
className: "flex items-center gap-2 text-primary hover:underline",
|
|
278
278
|
children: [
|
|
279
|
-
/* @__PURE__ */ b(
|
|
279
|
+
/* @__PURE__ */ b(_, { size: 14 }),
|
|
280
280
|
O("devportal.privacyPolicy", "Privacy Policy"),
|
|
281
|
-
/* @__PURE__ */ b(
|
|
281
|
+
/* @__PURE__ */ b(f, { size: 12 })
|
|
282
282
|
]
|
|
283
283
|
}),
|
|
284
284
|
M.termsOfServiceUrl && /* @__PURE__ */ x("a", {
|
|
@@ -287,16 +287,16 @@ var S = {
|
|
|
287
287
|
rel: "noopener noreferrer",
|
|
288
288
|
className: "flex items-center gap-2 text-primary hover:underline",
|
|
289
289
|
children: [
|
|
290
|
-
/* @__PURE__ */ b(
|
|
290
|
+
/* @__PURE__ */ b(m, { size: 14 }),
|
|
291
291
|
O("devportal.termsOfService", "Terms of Service"),
|
|
292
|
-
/* @__PURE__ */ b(
|
|
292
|
+
/* @__PURE__ */ b(f, { size: 12 })
|
|
293
293
|
]
|
|
294
294
|
})
|
|
295
295
|
]
|
|
296
296
|
})
|
|
297
297
|
]
|
|
298
298
|
}),
|
|
299
|
-
/* @__PURE__ */ x(
|
|
299
|
+
/* @__PURE__ */ x(i, {
|
|
300
300
|
title: O("devportal.listingCompleteness", "Listing Completeness"),
|
|
301
301
|
children: [/* @__PURE__ */ b("div", {
|
|
302
302
|
className: "space-y-2",
|
|
@@ -339,7 +339,7 @@ var S = {
|
|
|
339
339
|
}
|
|
340
340
|
].map(({ label: e, ok: t }) => /* @__PURE__ */ x("div", {
|
|
341
341
|
className: "flex items-center gap-3 text-sm",
|
|
342
|
-
children: [t ? /* @__PURE__ */ b(
|
|
342
|
+
children: [t ? /* @__PURE__ */ b(u, {
|
|
343
343
|
size: 16,
|
|
344
344
|
className: "text-status-success-text flex-shrink-0"
|
|
345
345
|
}) : /* @__PURE__ */ b("div", { className: "h-4 w-4 rounded-full border-2 border-muted-foreground flex-shrink-0" }), /* @__PURE__ */ b("span", {
|