@aglyn/plugins-marketplace 1.2.0 → 1.4.0
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/README.md +1 -1
- package/package.json +14 -14
- package/src/lib/components/listing-content.component.js +11 -3
- package/src/lib/components/listing-content.component.js.map +1 -1
- package/src/lib/components/marketplace-pay-panel.component.d.ts +67 -0
- package/src/lib/components/marketplace-pay-panel.component.js +228 -0
- package/src/lib/components/marketplace-pay-panel.component.js.map +1 -0
- package/src/lib/components/publish-artifact-dialog.component.d.ts +1 -1
- package/src/lib/components/publish-artifact-dialog.component.js +144 -20
- package/src/lib/components/publish-artifact-dialog.component.js.map +1 -1
- package/src/lib/components/publish-starter-fields.component.d.ts +35 -0
- package/src/lib/components/publish-starter-fields.component.js +176 -0
- package/src/lib/components/publish-starter-fields.component.js.map +1 -0
- package/src/lib/components/starter-picker-shelf.component.d.ts +2 -2
- package/src/lib/components/starter-picker-shelf.component.js +28 -6
- package/src/lib/components/starter-picker-shelf.component.js.map +1 -1
- package/src/lib/hooks/use-marketplace-actions.d.ts +9 -3
- package/src/lib/hooks/use-marketplace-actions.js +47 -89
- package/src/lib/hooks/use-marketplace-actions.js.map +1 -1
- package/src/lib/hooks/use-marketplace-pay.d.ts +70 -0
- package/src/lib/hooks/use-marketplace-pay.js +359 -0
- package/src/lib/hooks/use-marketplace-pay.js.map +1 -0
- package/src/lib/hooks/use-starter-publish-facts.d.ts +25 -0
- package/src/lib/hooks/use-starter-publish-facts.js +116 -0
- package/src/lib/hooks/use-starter-publish-facts.js.map +1 -0
- package/src/lib/hooks/use-starter-purchase.d.ts +17 -22
- package/src/lib/hooks/use-starter-purchase.js +26 -73
- package/src/lib/hooks/use-starter-purchase.js.map +1 -1
- package/src/lib/model/marketplace-invoice.d.ts +52 -0
- package/src/lib/model/marketplace-invoice.js +75 -0
- package/src/lib/model/marketplace-invoice.js.map +1 -0
- package/src/lib/model/marketplace-pay.d.ts +42 -0
- package/src/lib/model/marketplace-pay.js +33 -0
- package/src/lib/model/marketplace-pay.js.map +1 -0
- package/src/lib/model/platform-seller.d.ts +37 -0
- package/src/lib/model/platform-seller.js +36 -0
- package/src/lib/model/platform-seller.js.map +1 -0
- package/src/lib/model/publisher-agreement.d.ts +15 -3
- package/src/lib/model/publisher-agreement.js +23 -3
- package/src/lib/model/publisher-agreement.js.map +1 -1
- package/src/lib/model/starter-publish.d.ts +159 -0
- package/src/lib/model/starter-publish.js +189 -0
- package/src/lib/model/starter-publish.js.map +1 -0
- package/src/lib/model/starter-query.d.ts +0 -8
- package/src/lib/model/starter-query.js +0 -10
- package/src/lib/model/starter-query.js.map +1 -1
- package/src/lib/server/billing-webhook.d.ts +6 -0
- package/src/lib/server/billing-webhook.js +269 -211
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/checkout.d.ts +2 -48
- package/src/lib/server/checkout.js +21 -241
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/pay.d.ts +18 -0
- package/src/lib/server/pay.js +540 -0
- package/src/lib/server/pay.js.map +1 -0
- package/src/lib/server/platform-org.d.ts +23 -0
- package/src/lib/server/platform-org.js +27 -0
- package/src/lib/server/platform-org.js.map +1 -0
- package/src/lib/server/publish-preconditions.js +4 -1
- package/src/lib/server/publish-preconditions.js.map +1 -1
- package/src/lib/server/publish-starter.js +5 -2
- package/src/lib/server/publish-starter.js.map +1 -1
- package/src/lib/server/publisher-profile.d.ts +5 -0
- package/src/lib/server/publisher-profile.js +20 -1
- package/src/lib/server/publisher-profile.js.map +1 -1
- package/src/lib/server/publisher-standing.d.ts +26 -0
- package/src/lib/server/publisher-standing.js +66 -0
- package/src/lib/server/publisher-standing.js.map +1 -0
- package/src/lib/server/sale.d.ts +119 -0
- package/src/lib/server/sale.js +304 -0
- package/src/lib/server/sale.js.map +1 -0
- package/src/lib/server/starter-publish-facts.d.ts +36 -0
- package/src/lib/server/starter-publish-facts.js +135 -0
- package/src/lib/server/starter-publish-facts.js.map +1 -0
- package/src/lib/server.js +8 -0
- package/src/lib/server.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/publish-artifact-dialog.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n isBelowMarketplacePriceFloor,\n marketplacePriceCostNote,\n marketplacePriceFloorHint,\n} from '../model/listing-price-floor'\nimport type {\n ConsoleArtifactPublishZoneProps,\n ConsolePublishableArtifact,\n} from '@aglyn/aglyn'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n PublisherAgreementCheckbox,\n usePublisherAgreementConsent,\n} from './publisher-agreement-checkbox.component'\n\n/** One kind's publish route, and the words a person reads about it. */\ninterface PublishRequest {\n /** Plugin API path, e.g. `marketplace/publish-layout`. */\n endpoint: string\n /** Extra body fields identifying what is being published. */\n payload: Record<string, unknown>\n /** Noun used in copy: \"layout\", \"component\", … */\n noun: string\n categoryPlaceholder?: string\n}\n\n/**\n * WHERE EACH KIND GOES, which is the knowledge that used to sit in the app\n * (AGL-3080).\n *\n * A console page hands over what it has — a kind, the scope holding it, the\n * document — and this is the only place that turns it into a request. The\n * pages that offer publishing named the endpoint, the payload key and the\n * noun themselves, which made them files that could not compile without a\n * marketplace and could not be right without being kept in step with one.\n *\n * ⚠️ A KIND THIS DOES NOT KNOW IS SAID OUT LOUD, not drawn as nothing. It\n * means a console offering to publish something this marketplace does not\n * sell — the two lists have gone out of step — and the person has already\n * clicked. Drawing nothing would make the control look broken with no way to\n * tell what happened, and guessing an endpoint from the kind would post to a\n * route that does not exist and report the 404 to the publisher.\n *\n * The control itself is a separate matter: a page only offers the publish\n * when this zone has a widget at all, so a workspace with no marketplace\n * never gets here.\n */\nfunction publishRequestFor(\n artifact: ConsolePublishableArtifact,\n): PublishRequest | null {\n const hostId = artifact.hostId ?? ''\n const orgId = artifact.orgId ?? ''\n const artifactId = artifact.artifactId ?? ''\n switch (artifact.kind) {\n case 'layout':\n return {\n endpoint: 'marketplace/publish-layout',\n payload: { hostId, layoutId: artifactId },\n noun: 'layout',\n categoryPlaceholder: 'e.g. Marketing, Docs, Storefront',\n }\n case 'component':\n return {\n endpoint: 'marketplace/publish',\n payload: { hostId, componentId: artifactId },\n noun: 'component',\n categoryPlaceholder: 'e.g. Hero, Footer, Pricing',\n }\n case 'site':\n return {\n endpoint: 'marketplace/publish-template',\n payload: { hostId },\n noun: 'site template',\n categoryPlaceholder: 'e.g. Portfolio, Restaurant',\n }\n case 'theme':\n return {\n endpoint: 'marketplace/publish-theme',\n payload: { hostId },\n noun: 'theme',\n }\n // Datasets are org-scoped, so this route takes orgId rather than hostId.\n case 'datasetSchema':\n return {\n endpoint: 'marketplace/publish-dataset-schema',\n payload: { orgId, datasetId: artifactId },\n noun: 'dataset schema',\n }\n case 'emailTemplate':\n return {\n endpoint: 'marketplace/publish-email-template',\n payload: { hostId, templateKey: artifactId },\n noun: 'email template',\n }\n case 'emailStarter':\n return {\n endpoint: 'marketplace/publish-email-starter',\n payload: { hostId, screenId: artifactId },\n noun: 'email starter',\n }\n default:\n return null\n }\n}\n\n/**\n * Publish an artifact to the marketplace (AGL-672, moved out of the console\n * app by AGL-3080).\n *\n * The publish form is identical across artifact types — name, description,\n * category, price — and only the endpoint and identifying fields differ, so\n * this takes both rather than being copied per type. Every gate that\n * matters (plan, publisher profile, payouts, host role) lives on the server;\n * this surfaces the server's message rather than trying to predict it.\n *\n * Drawn through the `hostArtifactPublish` zone, so the page that offers the\n * publish keeps the control that opens it and never imports this. A page\n * with no widget on that zone leaves the offer out entirely, which is why\n * this may safely draw nothing for a kind it does not sell.\n */\nexport function PublishArtifactDialog({\n artifact,\n onClose,\n}: ConsoleArtifactPublishZoneProps) {\n // Memoized because it is a dependency of the publish callback: rebuilt\n // every render, it would rebuild that callback every render too.\n const target = useMemo(\n () => (artifact ? publishRequestFor(artifact) : null),\n [artifact],\n )\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [name, setName] = useState('')\n const [description, setDescription] = useState('')\n const [category, setCategory] = useState('')\n const [price, setPrice] = useState('')\n const [busy, setBusy] = useState(false)\n // The publisher agreement, agreed to on this publish (AGL-4105): the box\n // above the button, unticked for every artifact the dialog opens on.\n const agreement = usePublisherAgreementConsent()\n const { reset: resetAgreement } = agreement\n\n useEffect(() => {\n if (!artifact) return\n setName(artifact.displayName ?? '')\n setDescription(artifact.description ?? '')\n setCategory('')\n setPrice('')\n resetAgreement()\n }, [artifact, resetAgreement])\n\n const handlePublish = useCallback(async () => {\n if (!target || !name.trim() || busy || !agreement.consent) return\n setBusy(true)\n try {\n const response = await authorizedFetch(user, `/api/${target.endpoint}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...target.payload,\n displayName: name.trim(),\n description: description.trim(),\n category: category.trim(),\n priceUsd: Number(price) || 0,\n agreement: agreement.consent,\n }),\n })\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n // 412 means a setup step is missing (publisher profile, payouts) —\n // actionable, so it reads as a warning rather than an error.\n return void enqueueSnackbar(payload?.error ?? 'Publish failed', {\n variant: response.status === 412 ? 'warning' : 'error',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar(`Published v${payload.version} to the marketplace`, {\n variant: 'success',\n persist: false,\n })\n // Each publish is its own agreement: the next one ticks the box again.\n resetAgreement()\n onClose()\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setBusy(false)\n }\n }, [\n target,\n name,\n description,\n category,\n price,\n busy,\n user,\n enqueueSnackbar,\n onClose,\n agreement.consent,\n resetAgreement,\n ])\n\n if (artifact && !target) {\n return (\n <Dialog open onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'Nothing publishes this'}</DialogTitle>\n <DialogContent>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`This marketplace does not publish a ${artifact.kind}, so there ` +\n 'is nowhere to send it. Nothing was changed.'}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button size=\"small\" onClick={onClose}>\n {'Close'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n }\n\n return (\n <>\n <Dialog\n open={!!target}\n onClose={busy ? undefined : onClose}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{`Publish ${target?.noun ?? 'artifact'}`}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Publishes the current published version of this ` +\n `${target?.noun ?? 'artifact'} so other organizations can ` +\n 'install it. Your site is unaffected.'}\n </Typography>\n <TextField\n autoFocus\n size=\"small\"\n label=\"Listing name\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n disabled={busy}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Description\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n disabled={busy}\n multiline\n minRows={2}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Category\"\n placeholder={\n target?.categoryPlaceholder ?? 'e.g. Marketing, Docs'\n }\n value={category}\n onChange={(event) => setCategory(event.target.value)}\n disabled={busy}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Price (USD)\"\n placeholder=\"0 = free\"\n // The minimum price (AGL-2343). Marketplace checkout is a\n // destination charge, so Stripe's fee comes out of the PLATFORM's\n // balance while the seller is transferred a fixed share — at $1 the\n // fee is larger than the whole platform cut. The publish route\n // refuses anything under the floor, so the field says so before the\n // publisher gets there and the button holds.\n error={isBelowMarketplacePriceFloor(price)}\n helperText={\n marketplacePriceCostNote(price) ??\n marketplacePriceFloorHint(\n 'Paid listings need payouts set up on your marketplace profile.',\n )\n }\n value={price}\n onChange={(event) =>\n setPrice(event.target.value.replace(/[^0-9]/g, ''))\n }\n disabled={busy}\n fullWidth\n />\n {/* Directly above Publish (AGL-4105), which waits for it. */}\n <PublisherAgreementCheckbox\n checked={agreement.agreed}\n onChange={agreement.setAgreed}\n disabled={busy}\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button size=\"small\" onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n disabled={\n busy ||\n !name.trim() ||\n isBelowMarketplacePriceFloor(price) ||\n !agreement.agreed\n }\n onClick={() => void handlePublish()}\n >\n {busy ? 'Publishing…' : 'Publish'}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n )\n}\n\nPublishArtifactDialog.displayName = 'PublishArtifactDialog'\n\nexport default PublishArtifactDialog\n"],"names":["isBelowMarketplacePriceFloor","marketplacePriceCostNote","marketplacePriceFloorHint","useSnackbar","useUser","authorizedFetch","Button","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useMemo","useState","PublisherAgreementCheckbox","usePublisherAgreementConsent","publishRequestFor","artifact","hostId","orgId","artifactId","kind","endpoint","payload","layoutId","noun","categoryPlaceholder","componentId","datasetId","templateKey","screenId","PublishArtifactDialog","onClose","target","data","user","enqueueSnackbar","name","setName","description","setDescription","category","setCategory","price","setPrice","busy","setBusy","agreement","reset","resetAgreement","displayName","handlePublish","trim","consent","response","method","headers","body","JSON","stringify","priceUsd","Number","json","catch","ok","error","variant","status","allowDuplicate","version","persist","console","open","maxWidth","fullWidth","color","size","onClick","undefined","spacing","sx","pt","autoFocus","label","value","onChange","event","disabled","multiline","minRows","placeholder","helperText","replace","checked","agreed","setAgreed"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/publish-artifact-dialog.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n isBelowMarketplacePriceFloor,\n marketplacePriceCostNote,\n marketplacePriceFloorHint,\n} from '../model/listing-price-floor'\nimport type {\n ConsoleArtifactPublishZoneProps,\n ConsolePublishableArtifact,\n} from '@aglyn/aglyn'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { BlockedControl } from '@aglyn/shared-ui-jsx/components/blocked-control.component'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n useStarterPublishFacts,\n type StarterPublishFactsState,\n} from '../hooks/use-starter-publish-facts'\nimport {\n starterPublishBody,\n starterPublishSteps,\n starterRefusalLink,\n type StarterThemeChoice,\n} from '../model/starter-publish'\nimport {\n PublisherAgreementCheckbox,\n usePublisherAgreementConsent,\n} from './publisher-agreement-checkbox.component'\nimport { PublishStarterFields } from './publish-starter-fields.component'\n\n/** One kind's publish route, and the words a person reads about it. */\ninterface PublishRequest {\n /** Plugin API path, e.g. `marketplace/publish-layout`. */\n endpoint: string\n /** Extra body fields identifying what is being published. */\n payload: Record<string, unknown>\n /** Noun used in copy: \"layout\", \"component\", … */\n noun: string\n categoryPlaceholder?: string\n /** The category field's label, where the kind names it otherwise. */\n categoryLabel?: string\n}\n\n/**\n * WHERE EACH KIND GOES, which is the knowledge that used to sit in the app\n * (AGL-3080).\n *\n * A console page hands over what it has — a kind, the scope holding it, the\n * document — and this is the only place that turns it into a request. The\n * pages that offer publishing named the endpoint, the payload key and the\n * noun themselves, which made them files that could not compile without a\n * marketplace and could not be right without being kept in step with one.\n *\n * ⚠️ A KIND THIS DOES NOT KNOW IS SAID OUT LOUD, not drawn as nothing. It\n * means a console offering to publish something this marketplace does not\n * sell — the two lists have gone out of step — and the person has already\n * clicked. Drawing nothing would make the control look broken with no way to\n * tell what happened, and guessing an endpoint from the kind would post to a\n * route that does not exist and report the 404 to the publisher.\n *\n * The control itself is a separate matter: a page only offers the publish\n * when this zone has a widget at all, so a workspace with no marketplace\n * never gets here.\n */\nfunction publishRequestFor(\n artifact: ConsolePublishableArtifact,\n): PublishRequest | null {\n const hostId = artifact.hostId ?? ''\n const orgId = artifact.orgId ?? ''\n const artifactId = artifact.artifactId ?? ''\n switch (artifact.kind) {\n case 'layout':\n return {\n endpoint: 'marketplace/publish-layout',\n payload: { hostId, layoutId: artifactId },\n noun: 'layout',\n categoryPlaceholder: 'e.g. Marketing, Docs, Storefront',\n }\n case 'component':\n return {\n endpoint: 'marketplace/publish',\n payload: { hostId, componentId: artifactId },\n noun: 'component',\n categoryPlaceholder: 'e.g. Hero, Footer, Pricing',\n }\n case 'site':\n return {\n endpoint: 'marketplace/publish-template',\n payload: { hostId },\n noun: 'site template',\n categoryPlaceholder: 'e.g. Portfolio, Restaurant',\n }\n case 'theme':\n return {\n endpoint: 'marketplace/publish-theme',\n payload: { hostId },\n noun: 'theme',\n }\n // Datasets are org-scoped, so this route takes orgId rather than hostId.\n case 'datasetSchema':\n return {\n endpoint: 'marketplace/publish-dataset-schema',\n payload: { orgId, datasetId: artifactId },\n noun: 'dataset schema',\n }\n case 'emailTemplate':\n return {\n endpoint: 'marketplace/publish-email-template',\n payload: { hostId, templateKey: artifactId },\n noun: 'email template',\n }\n case 'emailStarter':\n return {\n endpoint: 'marketplace/publish-email-starter',\n payload: { hostId, screenId: artifactId },\n noun: 'email starter',\n }\n // A whole site, as a site starter people create a new site from (AGL-4049).\n case 'siteStarter':\n return {\n endpoint: 'marketplace/publish-starter',\n payload: { hostId },\n noun: 'starter',\n categoryLabel: 'What kind of site is it?',\n categoryPlaceholder: 'e.g. Bakery, Law firm, Wedding',\n }\n default:\n return null\n }\n}\n\n/**\n * Publish an artifact to the marketplace (AGL-672, moved out of the console\n * app by AGL-3080).\n *\n * The publish form is identical across artifact types — name, description,\n * category, price — and only the endpoint and identifying fields differ, so\n * this takes both rather than being copied per type. Every gate that\n * matters (plan, publisher profile, payouts, host role) lives on the server;\n * this surfaces the server's message rather than trying to predict it.\n *\n * Drawn through the `hostArtifactPublish` zone, so the page that offers the\n * publish keeps the control that opens it and never imports this. A page\n * with no widget on that zone leaves the offer out entirely, which is why\n * this may safely draw nothing for a kind it does not sell.\n */\nexport function PublishArtifactDialog(props: ConsoleArtifactPublishZoneProps) {\n return props.artifact?.kind === 'siteStarter' ? (\n <PublishStarterDialog {...props} />\n ) : (\n <PublishArtifactForm {...props} starterReads={null} />\n )\n}\n\nPublishArtifactDialog.displayName = 'PublishArtifactDialog'\n\n/**\n * The form for a site starter (AGL-4049), with what it reads before the\n * click: the site's theme and pages, the org's publisher profile and plan.\n * Its own component so no other kind ever issues those reads.\n */\nfunction PublishStarterDialog(props: ConsoleArtifactPublishZoneProps) {\n const reads = useStarterPublishFacts({\n hostId: props.artifact?.hostId,\n orgId: props.artifact?.orgId,\n })\n return <PublishArtifactForm {...props} starterReads={reads} />\n}\n\n/** The listing form every kind shares, with a starter's half when it has one. */\nfunction PublishArtifactForm({\n artifact,\n onClose,\n starterReads,\n}: ConsoleArtifactPublishZoneProps & {\n /** A site starter's reads, or `null` for every other kind. */\n starterReads: StarterPublishFactsState | null\n}) {\n // Memoized because it is a dependency of the publish callback: rebuilt\n // every render, it would rebuild that callback every render too.\n const target = useMemo(\n () => (artifact ? publishRequestFor(artifact) : null),\n [artifact],\n )\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [name, setName] = useState('')\n const [description, setDescription] = useState('')\n const [category, setCategory] = useState('')\n const [price, setPrice] = useState('')\n const [busy, setBusy] = useState(false)\n // The publisher agreement, agreed to on this publish (AGL-4105): the box\n // above the button, unticked for every artifact the dialog opens on.\n const agreement = usePublisherAgreementConsent()\n const { reset: resetAgreement } = agreement\n\n /*\n * A SITE STARTER'S OWN HALF (AGL-4049). Its theme shape is the\n * publisher's choice and starts unchosen; what it captures and every setup\n * step in its way are read before the click, so they are said with where\n * each is fixed rather than refused after it. For any other kind the facts\n * hook is handed no site and reads nothing.\n */\n const starter = !!starterReads && !!target\n const [themeChoice, setThemeChoice] = useState<StarterThemeChoice | null>(null)\n const [styleKind, setStyleKind] = useState('')\n // What the door last refused, in its words, shown in the form to fix it.\n const [refusal, setRefusal] = useState<{\n status: number\n error: string\n more: string[]\n } | null>(null)\n const starterFacts = starterReads\n ? { ...starterReads.facts, priceUsd: Number(price) || 0 }\n : null\n const steps = starter && starterFacts ? starterPublishSteps(starterFacts) : []\n const starterBody =\n starter && starterReads\n ? starterPublishBody({\n choice: themeChoice,\n builtin: starterReads.builtin,\n kind: styleKind || null,\n })\n : null\n const agreementRefused = refusal && (refusal.status === 428 || refusal.status === 409)\n\n useEffect(() => {\n if (!artifact) return\n setName(artifact.displayName ?? '')\n setDescription(artifact.description ?? '')\n setCategory('')\n setPrice('')\n setThemeChoice(null)\n setStyleKind('')\n setRefusal(null)\n resetAgreement()\n }, [artifact, resetAgreement])\n\n const handlePublish = useCallback(async () => {\n if (!target || !name.trim() || busy || !agreement.consent) return\n if (starter && (!starterBody || steps.length)) return\n setBusy(true)\n setRefusal(null)\n try {\n const response = await authorizedFetch(user, `/api/${target.endpoint}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...target.payload,\n displayName: name.trim(),\n description: description.trim(),\n category: category.trim(),\n priceUsd: Number(price) || 0,\n agreement: agreement.consent,\n ...starterBody,\n }),\n })\n const payload = await response.json().catch(() => ({}))\n if (!response.ok && starter) {\n // In the form, in the door's words, so the publisher can fix it\n // there: another theme, a page, a setup step. Every problem a\n // theme check names is listed, not only the first.\n const error = typeof payload?.error === 'string' && payload.error ? payload.error : 'Publish failed'\n const more = Array.isArray(payload?.errors)\n ? payload.errors\n .map((entry: { message?: unknown }) => entry?.message)\n .filter((message: unknown): message is string => typeof message === 'string' && message !== error)\n : []\n setRefusal({ status: response.status, error, more })\n // An agreement refused is asked for again, by a fresh tick.\n if (response.status === 428 || response.status === 409) resetAgreement()\n return\n }\n if (!response.ok) {\n // 412 means a setup step is missing (publisher profile, payouts) —\n // actionable, so it reads as a warning rather than an error.\n return void enqueueSnackbar(payload?.error ?? 'Publish failed', {\n variant: response.status === 412 ? 'warning' : 'error',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar(\n starter\n ? Number(payload.version) > 1\n ? `Published v${payload.version} of your starter`\n : 'Sent your starter for review. It is listed once it has been reviewed.'\n : `Published v${payload.version} to the marketplace`,\n { variant: 'success', persist: false },\n )\n // Each publish is its own agreement: the next one ticks the box again.\n resetAgreement()\n onClose()\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setBusy(false)\n }\n }, [\n target,\n name,\n description,\n category,\n price,\n busy,\n user,\n enqueueSnackbar,\n onClose,\n agreement.consent,\n resetAgreement,\n starter,\n starterBody,\n steps.length,\n ])\n\n if (artifact && !target) {\n return (\n <Dialog open onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{'Nothing publishes this'}</DialogTitle>\n <DialogContent>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`This marketplace does not publish a ${artifact.kind}, so there ` +\n 'is nowhere to send it. Nothing was changed.'}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button size=\"small\" onClick={onClose}>\n {'Close'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n }\n\n return (\n <>\n <Dialog\n open={!!target}\n onClose={busy ? undefined : onClose}\n maxWidth={starter ? 'sm' : 'xs'}\n fullWidth\n >\n <DialogTitle>\n {starter ? 'Publish as a starter' : `Publish ${target?.noun ?? 'artifact'}`}\n </DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {starter\n ? 'Publishes this site as a site starter: a ready-made website ' +\n 'people create a new site from. A new starter is reviewed ' +\n 'before anyone else can see it. Your site is unaffected.'\n : `Publishes the current published version of this ` +\n `${target?.noun ?? 'artifact'} so other organizations can ` +\n 'install it. Your site is unaffected.'}\n </Typography>\n <TextField\n autoFocus\n size=\"small\"\n label=\"Listing name\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n disabled={busy}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Description\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n disabled={busy}\n multiline\n minRows={2}\n fullWidth\n />\n <TextField\n size=\"small\"\n label={target?.categoryLabel ?? 'Category'}\n placeholder={\n target?.categoryPlaceholder ?? 'e.g. Marketing, Docs'\n }\n value={category}\n onChange={(event) => setCategory(event.target.value)}\n disabled={busy}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Price (USD)\"\n placeholder=\"0 = free\"\n // The minimum price (AGL-2343). Marketplace checkout is a\n // destination charge, so Stripe's fee comes out of the PLATFORM's\n // balance while the seller is transferred a fixed share — at $1 the\n // fee is larger than the whole platform cut. The publish route\n // refuses anything under the floor, so the field says so before the\n // publisher gets there and the button holds.\n error={isBelowMarketplacePriceFloor(price)}\n helperText={\n marketplacePriceCostNote(price) ??\n marketplacePriceFloorHint(\n 'Paid listings need payouts set up on your marketplace profile.',\n )\n }\n value={price}\n onChange={(event) =>\n setPrice(event.target.value.replace(/[^0-9]/g, ''))\n }\n disabled={busy}\n fullWidth\n />\n {starter && starterReads ? (\n <PublishStarterFields\n builtin={starterReads.builtin}\n hostReady={starterReads.hostReady}\n pages={starterReads.facts.pages}\n steps={steps}\n choice={themeChoice}\n onChoiceChange={setThemeChoice}\n styleKind={styleKind}\n onStyleKindChange={setStyleKind}\n disabled={busy}\n />\n ) : null}\n {refusal && !agreementRefused ? (\n refusal.status === 412 && starterFacts ? (\n <PrerequisiteNotice\n severity=\"warning\"\n href={starterRefusalLink(starterFacts).href ?? undefined}\n actionLabel={starterRefusalLink(starterFacts).actionLabel}\n >\n {refusal.error}\n </PrerequisiteNotice>\n ) : (\n <Alert severity=\"error\" data-starter-publish=\"refusal\">\n {refusal.error}\n {refusal.more.length ? (\n <Stack component=\"ul\" spacing={0.5} sx={{ m: 0, mt: 1, pl: 2.5 }}>\n {refusal.more.map((message) => (\n <Typography key={message} component=\"li\" variant=\"body2\">\n {message}\n </Typography>\n ))}\n </Stack>\n ) : null}\n </Alert>\n )\n ) : null}\n {/* Directly above Publish (AGL-4105), which waits for it. */}\n <PublisherAgreementCheckbox\n checked={agreement.agreed}\n onChange={agreement.setAgreed}\n disabled={busy}\n error={agreementRefused ? refusal.error : null}\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button size=\"small\" onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <BlockedControl blocked={steps.length > 0} reason={steps[0]?.text ?? ''}>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n disabled={\n busy ||\n !name.trim() ||\n isBelowMarketplacePriceFloor(price) ||\n !agreement.agreed ||\n (starter && !starterBody)\n }\n onClick={() => void handlePublish()}\n >\n {busy ? 'Publishing…' : 'Publish'}\n </Button>\n </BlockedControl>\n </DialogActions>\n </Dialog>\n </>\n )\n}\n\nPublishArtifactForm.displayName = 'PublishArtifactForm'\n\nexport default PublishArtifactDialog\n"],"names":["isBelowMarketplacePriceFloor","marketplacePriceCostNote","marketplacePriceFloorHint","useSnackbar","BlockedControl","PrerequisiteNotice","useUser","authorizedFetch","Alert","Button","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useMemo","useState","useStarterPublishFacts","starterPublishBody","starterPublishSteps","starterRefusalLink","PublisherAgreementCheckbox","usePublisherAgreementConsent","PublishStarterFields","publishRequestFor","artifact","hostId","orgId","artifactId","kind","endpoint","payload","layoutId","noun","categoryPlaceholder","componentId","datasetId","templateKey","screenId","categoryLabel","PublishArtifactDialog","props","PublishStarterDialog","PublishArtifactForm","starterReads","displayName","reads","onClose","steps","target","data","user","enqueueSnackbar","name","setName","description","setDescription","category","setCategory","price","setPrice","busy","setBusy","agreement","reset","resetAgreement","starter","themeChoice","setThemeChoice","styleKind","setStyleKind","refusal","setRefusal","starterFacts","facts","priceUsd","Number","starterBody","choice","builtin","agreementRefused","status","handlePublish","trim","consent","length","response","method","headers","body","JSON","stringify","json","catch","ok","error","more","Array","isArray","errors","map","entry","message","filter","variant","allowDuplicate","version","persist","console","open","maxWidth","fullWidth","color","size","onClick","undefined","spacing","sx","pt","autoFocus","label","value","onChange","event","disabled","multiline","minRows","placeholder","helperText","replace","hostReady","pages","onChoiceChange","onStyleKindChange","severity","href","actionLabel","data-starter-publish","component","m","mt","pl","checked","agreed","setAgreed","blocked","reason","text"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SiteStarterBuiltinReading } from '@aglyn/aglyn/app-utils/site-starter-theme';
|
|
2
|
+
import { type StarterPublishPages, type StarterPublishStep, type StarterThemeChoice } from '../model/starter-publish';
|
|
3
|
+
export interface PublishStarterFieldsProps {
|
|
4
|
+
/** The site's built-in as a starter would carry it, or `null` when it wears none. */
|
|
5
|
+
builtin: SiteStarterBuiltinReading | null;
|
|
6
|
+
/** The host has been read, so `builtin` is an answer. */
|
|
7
|
+
hostReady: boolean;
|
|
8
|
+
/** The pages the publish will capture, or `null` while they load. */
|
|
9
|
+
pages: StarterPublishPages | null;
|
|
10
|
+
/** What stands in the way, each with where it is fixed. */
|
|
11
|
+
steps: readonly StarterPublishStep[];
|
|
12
|
+
choice: StarterThemeChoice | null;
|
|
13
|
+
onChoiceChange: (choice: StarterThemeChoice) => void;
|
|
14
|
+
/** The closest style's id, or `''` for none. */
|
|
15
|
+
styleKind: string;
|
|
16
|
+
onStyleKindChange: (kind: string) => void;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The half of the publish form only a site starter has (AGL-4049): what it
|
|
21
|
+
* captures, the theme it carries (chosen by the publisher; neither shape is
|
|
22
|
+
* chosen for them), the style it is listed under, and every setup step that
|
|
23
|
+
* stands in the way, said before the click with where it is fixed.
|
|
24
|
+
*
|
|
25
|
+
* A theme shape this site cannot be published as stays on screen, disabled,
|
|
26
|
+
* with the reason: a site that wears its own theme has no built-in to name.
|
|
27
|
+
*
|
|
28
|
+
* Figma: "Aglyn Console (web)" → page "Marketplace · Publish & review" →
|
|
29
|
+
* section "✅ CURRENT · Publish as a starter, behind release_site_starters".
|
|
30
|
+
*/
|
|
31
|
+
export declare function PublishStarterFields(props: PublishStarterFieldsProps): import("react").JSX.Element;
|
|
32
|
+
export declare namespace PublishStarterFields {
|
|
33
|
+
var displayName: string;
|
|
34
|
+
}
|
|
35
|
+
export default PublishStarterFields;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import { SITE_STYLES } from "@aglyn/aglyn/app-utils/site-styles";
|
|
19
|
+
import { PrerequisiteNotice } from "@aglyn/shared-ui-jsx/components/prerequisite-notice.component";
|
|
20
|
+
import { Alert, FormControl, FormControlLabel, FormHelperText, FormLabel, MenuItem, Radio, RadioGroup, Stack, TextField, Typography } from "@mui/material";
|
|
21
|
+
import { useId } from "react";
|
|
22
|
+
import { starterBuiltinDetail, starterCapturesSentence, starterFontsNote } from "../model/starter-publish.js";
|
|
23
|
+
/** One theme option: its name, and a line on what it carries. */ function OptionLabel(props) {
|
|
24
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
25
|
+
spacing: 0.25,
|
|
26
|
+
sx: {
|
|
27
|
+
py: 0.5
|
|
28
|
+
},
|
|
29
|
+
children: [
|
|
30
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
31
|
+
variant: "body2",
|
|
32
|
+
component: "span",
|
|
33
|
+
sx: {
|
|
34
|
+
fontWeight: 'fontWeightMedium'
|
|
35
|
+
},
|
|
36
|
+
children: props.title
|
|
37
|
+
}),
|
|
38
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
39
|
+
variant: "caption",
|
|
40
|
+
component: "span",
|
|
41
|
+
color: "text.secondary",
|
|
42
|
+
children: props.detail
|
|
43
|
+
})
|
|
44
|
+
]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The half of the publish form only a site starter has (AGL-4049): what it
|
|
49
|
+
* captures, the theme it carries (chosen by the publisher; neither shape is
|
|
50
|
+
* chosen for them), the style it is listed under, and every setup step that
|
|
51
|
+
* stands in the way, said before the click with where it is fixed.
|
|
52
|
+
*
|
|
53
|
+
* A theme shape this site cannot be published as stays on screen, disabled,
|
|
54
|
+
* with the reason: a site that wears its own theme has no built-in to name.
|
|
55
|
+
*
|
|
56
|
+
* Figma: "Aglyn Console (web)" → page "Marketplace · Publish & review" →
|
|
57
|
+
* section "✅ CURRENT · Publish as a starter, behind release_site_starters".
|
|
58
|
+
*/ export function PublishStarterFields(props) {
|
|
59
|
+
const { builtin, hostReady, pages, steps, choice, onChoiceChange, styleKind, onStyleKindChange, disabled = false } = props;
|
|
60
|
+
const themeLabelId = useId();
|
|
61
|
+
const builtinDetail = starterBuiltinDetail(builtin, hostReady);
|
|
62
|
+
const fontsNote = starterFontsNote(builtin);
|
|
63
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
64
|
+
spacing: 2,
|
|
65
|
+
children: [
|
|
66
|
+
pages ? /*#__PURE__*/ _jsx(Typography, {
|
|
67
|
+
variant: "body2",
|
|
68
|
+
color: "text.secondary",
|
|
69
|
+
"data-starter-publish": "pages",
|
|
70
|
+
children: starterCapturesSentence(pages)
|
|
71
|
+
}) : null,
|
|
72
|
+
/*#__PURE__*/ _jsxs(FormControl, {
|
|
73
|
+
required: true,
|
|
74
|
+
disabled: disabled,
|
|
75
|
+
children: [
|
|
76
|
+
/*#__PURE__*/ _jsx(FormLabel, {
|
|
77
|
+
id: themeLabelId,
|
|
78
|
+
sx: {
|
|
79
|
+
typography: 'body2'
|
|
80
|
+
},
|
|
81
|
+
children: 'Theme'
|
|
82
|
+
}),
|
|
83
|
+
/*#__PURE__*/ _jsxs(RadioGroup, {
|
|
84
|
+
"aria-labelledby": themeLabelId,
|
|
85
|
+
value: choice != null ? choice : '',
|
|
86
|
+
onChange: (event)=>onChoiceChange(event.target.value),
|
|
87
|
+
children: [
|
|
88
|
+
/*#__PURE__*/ _jsx(FormControlLabel, {
|
|
89
|
+
value: "builtin",
|
|
90
|
+
disabled: disabled || !builtin,
|
|
91
|
+
sx: {
|
|
92
|
+
alignItems: 'flex-start',
|
|
93
|
+
mr: 0
|
|
94
|
+
},
|
|
95
|
+
control: /*#__PURE__*/ _jsx(Radio, {
|
|
96
|
+
size: "small",
|
|
97
|
+
sx: {
|
|
98
|
+
mt: 0.25
|
|
99
|
+
}
|
|
100
|
+
}),
|
|
101
|
+
label: /*#__PURE__*/ _jsx(OptionLabel, {
|
|
102
|
+
title: "This site's built-in theme with its brand colors",
|
|
103
|
+
detail: builtinDetail
|
|
104
|
+
})
|
|
105
|
+
}),
|
|
106
|
+
/*#__PURE__*/ _jsx(FormControlLabel, {
|
|
107
|
+
value: "full",
|
|
108
|
+
disabled: disabled,
|
|
109
|
+
sx: {
|
|
110
|
+
alignItems: 'flex-start',
|
|
111
|
+
mr: 0
|
|
112
|
+
},
|
|
113
|
+
control: /*#__PURE__*/ _jsx(Radio, {
|
|
114
|
+
size: "small",
|
|
115
|
+
sx: {
|
|
116
|
+
mt: 0.25
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
label: /*#__PURE__*/ _jsx(OptionLabel, {
|
|
120
|
+
title: "The site's full theme",
|
|
121
|
+
detail: "Every color, font and style this site's theme sets. A site created from it gets it as one of its themes, named after the starter."
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
]
|
|
125
|
+
}),
|
|
126
|
+
!choice ? /*#__PURE__*/ _jsx(FormHelperText, {
|
|
127
|
+
sx: {
|
|
128
|
+
mx: 0
|
|
129
|
+
},
|
|
130
|
+
children: "Choose the starter's theme to publish it."
|
|
131
|
+
}) : null
|
|
132
|
+
]
|
|
133
|
+
}),
|
|
134
|
+
choice === 'builtin' && fontsNote ? /*#__PURE__*/ _jsx(Alert, {
|
|
135
|
+
severity: "info",
|
|
136
|
+
children: fontsNote
|
|
137
|
+
}) : null,
|
|
138
|
+
/*#__PURE__*/ _jsxs(TextField, {
|
|
139
|
+
select: true,
|
|
140
|
+
size: "small",
|
|
141
|
+
label: "Closest style",
|
|
142
|
+
value: styleKind,
|
|
143
|
+
onChange: (event)=>onStyleKindChange(event.target.value),
|
|
144
|
+
disabled: disabled,
|
|
145
|
+
helperText: "The kind of site it is listed under when people choose a starter. Optional.",
|
|
146
|
+
fullWidth: true,
|
|
147
|
+
children: [
|
|
148
|
+
/*#__PURE__*/ _jsx(MenuItem, {
|
|
149
|
+
value: "",
|
|
150
|
+
children: 'None'
|
|
151
|
+
}),
|
|
152
|
+
SITE_STYLES.map((style)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
153
|
+
value: style.id,
|
|
154
|
+
children: style.name
|
|
155
|
+
}, style.id))
|
|
156
|
+
]
|
|
157
|
+
}),
|
|
158
|
+
steps.map((step)=>{
|
|
159
|
+
var _step_href;
|
|
160
|
+
return /*#__PURE__*/ _jsx(PrerequisiteNotice, {
|
|
161
|
+
severity: "warning",
|
|
162
|
+
href: (_step_href = step.href) != null ? _step_href : undefined,
|
|
163
|
+
actionLabel: step.actionLabel,
|
|
164
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
165
|
+
"data-starter-publish-step": step.id,
|
|
166
|
+
children: step.text
|
|
167
|
+
})
|
|
168
|
+
}, step.id);
|
|
169
|
+
})
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
PublishStarterFields.displayName = 'PublishStarterFields';
|
|
174
|
+
export default PublishStarterFields;
|
|
175
|
+
|
|
176
|
+
//# sourceMappingURL=publish-starter-fields.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/publish-starter-fields.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { SiteStarterBuiltinReading } from '@aglyn/aglyn/app-utils/site-starter-theme'\nimport { SITE_STYLES } from '@aglyn/aglyn/app-utils/site-styles'\nimport { PrerequisiteNotice } from '@aglyn/shared-ui-jsx/components/prerequisite-notice.component'\nimport {\n Alert,\n FormControl,\n FormControlLabel,\n FormHelperText,\n FormLabel,\n MenuItem,\n Radio,\n RadioGroup,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useId, type ReactNode } from 'react'\nimport {\n starterBuiltinDetail,\n starterCapturesSentence,\n starterFontsNote,\n type StarterPublishPages,\n type StarterPublishStep,\n type StarterThemeChoice,\n} from '../model/starter-publish'\n\nexport interface PublishStarterFieldsProps {\n /** The site's built-in as a starter would carry it, or `null` when it wears none. */\n builtin: SiteStarterBuiltinReading | null\n /** The host has been read, so `builtin` is an answer. */\n hostReady: boolean\n /** The pages the publish will capture, or `null` while they load. */\n pages: StarterPublishPages | null\n /** What stands in the way, each with where it is fixed. */\n steps: readonly StarterPublishStep[]\n choice: StarterThemeChoice | null\n onChoiceChange: (choice: StarterThemeChoice) => void\n /** The closest style's id, or `''` for none. */\n styleKind: string\n onStyleKindChange: (kind: string) => void\n disabled?: boolean\n}\n\n/** One theme option: its name, and a line on what it carries. */\nfunction OptionLabel(props: { title: string; detail: ReactNode }) {\n return (\n <Stack spacing={0.25} sx={{ py: 0.5 }}>\n <Typography variant=\"body2\" component=\"span\" sx={{ fontWeight: 'fontWeightMedium' }}>\n {props.title}\n </Typography>\n <Typography variant=\"caption\" component=\"span\" color=\"text.secondary\">\n {props.detail}\n </Typography>\n </Stack>\n )\n}\n\n/**\n * The half of the publish form only a site starter has (AGL-4049): what it\n * captures, the theme it carries (chosen by the publisher; neither shape is\n * chosen for them), the style it is listed under, and every setup step that\n * stands in the way, said before the click with where it is fixed.\n *\n * A theme shape this site cannot be published as stays on screen, disabled,\n * with the reason: a site that wears its own theme has no built-in to name.\n *\n * Figma: \"Aglyn Console (web)\" → page \"Marketplace · Publish & review\" →\n * section \"✅ CURRENT · Publish as a starter, behind release_site_starters\".\n */\nexport function PublishStarterFields(props: PublishStarterFieldsProps) {\n const {\n builtin,\n hostReady,\n pages,\n steps,\n choice,\n onChoiceChange,\n styleKind,\n onStyleKindChange,\n disabled = false,\n } = props\n const themeLabelId = useId()\n const builtinDetail = starterBuiltinDetail(builtin, hostReady)\n const fontsNote = starterFontsNote(builtin)\n\n return (\n <Stack spacing={2}>\n {pages ? (\n <Typography variant=\"body2\" color=\"text.secondary\" data-starter-publish=\"pages\">\n {starterCapturesSentence(pages)}\n </Typography>\n ) : null}\n\n <FormControl required disabled={disabled}>\n <FormLabel id={themeLabelId} sx={{ typography: 'body2' }}>\n {'Theme'}\n </FormLabel>\n <RadioGroup\n aria-labelledby={themeLabelId}\n value={choice ?? ''}\n onChange={(event) => onChoiceChange(event.target.value as StarterThemeChoice)}\n >\n <FormControlLabel\n value=\"builtin\"\n disabled={disabled || !builtin}\n sx={{ alignItems: 'flex-start', mr: 0 }}\n control={<Radio size=\"small\" sx={{ mt: 0.25 }} />}\n label={\n <OptionLabel title=\"This site's built-in theme with its brand colors\" detail={builtinDetail} />\n }\n />\n <FormControlLabel\n value=\"full\"\n disabled={disabled}\n sx={{ alignItems: 'flex-start', mr: 0 }}\n control={<Radio size=\"small\" sx={{ mt: 0.25 }} />}\n label={\n <OptionLabel\n title=\"The site's full theme\"\n detail=\"Every color, font and style this site's theme sets. A site created from it gets it as one of its themes, named after the starter.\"\n />\n }\n />\n </RadioGroup>\n {!choice ? (\n <FormHelperText sx={{ mx: 0 }}>{\"Choose the starter's theme to publish it.\"}</FormHelperText>\n ) : null}\n </FormControl>\n\n {choice === 'builtin' && fontsNote ? <Alert severity=\"info\">{fontsNote}</Alert> : null}\n\n <TextField\n select\n size=\"small\"\n label=\"Closest style\"\n value={styleKind}\n onChange={(event) => onStyleKindChange(event.target.value)}\n disabled={disabled}\n helperText=\"The kind of site it is listed under when people choose a starter. Optional.\"\n fullWidth\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {SITE_STYLES.map((style) => (\n <MenuItem key={style.id} value={style.id}>\n {style.name}\n </MenuItem>\n ))}\n </TextField>\n\n {steps.map((step) => (\n <PrerequisiteNotice\n key={step.id}\n severity=\"warning\"\n href={step.href ?? undefined}\n actionLabel={step.actionLabel}\n >\n <span data-starter-publish-step={step.id}>{step.text}</span>\n </PrerequisiteNotice>\n ))}\n </Stack>\n )\n}\n\nPublishStarterFields.displayName = 'PublishStarterFields'\n\nexport default PublishStarterFields\n"],"names":["SITE_STYLES","PrerequisiteNotice","Alert","FormControl","FormControlLabel","FormHelperText","FormLabel","MenuItem","Radio","RadioGroup","Stack","TextField","Typography","useId","starterBuiltinDetail","starterCapturesSentence","starterFontsNote","OptionLabel","props","spacing","sx","py","variant","component","fontWeight","title","color","detail","PublishStarterFields","builtin","hostReady","pages","steps","choice","onChoiceChange","styleKind","onStyleKindChange","disabled","themeLabelId","builtinDetail","fontsNote","data-starter-publish","required","id","typography","aria-labelledby","value","onChange","event","target","alignItems","mr","control","size","mt","label","mx","severity","select","helperText","fullWidth","map","style","name","step","href","undefined","actionLabel","span","data-starter-publish-step","text","displayName"],"mappings":"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"}
|
|
@@ -19,8 +19,8 @@ import type { ConsoleHostStarterPickerZoneProps } from '@aglyn/aglyn/plugin-mana
|
|
|
19
19
|
* failure is said here with a way to ask again.
|
|
20
20
|
*
|
|
21
21
|
* A picked starter with a price the workspace has not paid says so under
|
|
22
|
-
* the cards, with "Buy and continue": the marketplace's payment,
|
|
23
|
-
* the
|
|
22
|
+
* the cards, with "Buy and continue": the marketplace's payment, in place, on
|
|
23
|
+
* the card on file, with the site still to be created. One it owns says that
|
|
24
24
|
* instead. The dialog's own `selected` wins over the shelf's pick: it is how
|
|
25
25
|
* a pick restored after paying is shown chosen, and how one the create
|
|
26
26
|
* refused is cleared.
|
|
@@ -36,6 +36,21 @@ import { starterCardFacts, starterPriceLabel } from "../model/starter-query.js";
|
|
|
36
36
|
zIndex: 1,
|
|
37
37
|
boxShadow: 3
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* The payment, where the note was, on its own surface. It may fill the window
|
|
41
|
+
* below the app bar, so a form that has grown keeps its button on screen; a
|
|
42
|
+
* smaller window scrolls inside it.
|
|
43
|
+
*/ const PAY_PANEL_SX = {
|
|
44
|
+
position: 'sticky',
|
|
45
|
+
bottom: (theme)=>theme.spacing(2),
|
|
46
|
+
zIndex: 1,
|
|
47
|
+
boxShadow: 3,
|
|
48
|
+
p: 2,
|
|
49
|
+
borderRadius: 1,
|
|
50
|
+
bgcolor: 'background.paper',
|
|
51
|
+
maxHeight: (theme)=>`calc(100dvh - ${theme.spacing(10)})`,
|
|
52
|
+
overflow: 'auto'
|
|
53
|
+
};
|
|
39
54
|
/** The guide to site starters, linked from what the shelf says when it has nothing to show. */ const STARTERS_HELP = pluginDocsHelp('siteStarters');
|
|
40
55
|
/**
|
|
41
56
|
* A starter's preview as the card draws it: the size the platform's own
|
|
@@ -136,8 +151,8 @@ const narrowed = (filter)=>[
|
|
|
136
151
|
* failure is said here with a way to ask again.
|
|
137
152
|
*
|
|
138
153
|
* A picked starter with a price the workspace has not paid says so under
|
|
139
|
-
* the cards, with "Buy and continue": the marketplace's payment,
|
|
140
|
-
* the
|
|
154
|
+
* the cards, with "Buy and continue": the marketplace's payment, in place, on
|
|
155
|
+
* the card on file, with the site still to be created. One it owns says that
|
|
141
156
|
* instead. The dialog's own `selected` wins over the shelf's pick: it is how
|
|
142
157
|
* a pick restored after paying is shown chosen, and how one the create
|
|
143
158
|
* refused is cleared.
|
|
@@ -150,7 +165,7 @@ const narrowed = (filter)=>[
|
|
|
150
165
|
* for them. A door that still asks is told back to the dialog as unowned.
|
|
151
166
|
*/ export function StarterPickerShelf(props) {
|
|
152
167
|
var _ref, _found_rows_find;
|
|
153
|
-
const { orgId, mode, search, filter, onPick, ready, selected } = props;
|
|
168
|
+
const { orgId, orgSlug, mode, search, filter, onPick, ready, selected } = props;
|
|
154
169
|
// The pick the dialog opened on, restored after paying for it: asked for
|
|
155
170
|
// by every search from then on, so it is listed (first) wherever the
|
|
156
171
|
// search would have put it, and never moves under the person's cursor.
|
|
@@ -171,7 +186,7 @@ const narrowed = (filter)=>[
|
|
|
171
186
|
const [ownedHere, setOwnedHere] = useState(()=>new Set());
|
|
172
187
|
// AI starters whose answers the door still asked payment for (AGL-4049).
|
|
173
188
|
const [unpaidHere, setUnpaidHere] = useState(()=>new Set());
|
|
174
|
-
const purchase = useStarterPurchase(orgId);
|
|
189
|
+
const purchase = useStarterPurchase(orgId, orgSlug);
|
|
175
190
|
const starterAnswers = useStarterAnswers(orgId);
|
|
176
191
|
const value = selected !== undefined ? (_ref = selected == null ? void 0 : selected.listingId) != null ? _ref : null : picked;
|
|
177
192
|
const pickedRow = value ? (_found_rows_find = found.rows.find((row)=>row.listingId === value)) != null ? _found_rows_find : null : null;
|
|
@@ -263,7 +278,10 @@ const narrowed = (filter)=>[
|
|
|
263
278
|
found.rows
|
|
264
279
|
]);
|
|
265
280
|
const buy = async (row)=>{
|
|
266
|
-
const outcome = await purchase.buy(
|
|
281
|
+
const outcome = await purchase.buy({
|
|
282
|
+
listingId: row.listingId,
|
|
283
|
+
displayName: row.displayName
|
|
284
|
+
});
|
|
267
285
|
if (outcome !== 'owned') return;
|
|
268
286
|
setOwnedHere((current)=>new Set(current).add(row.listingId));
|
|
269
287
|
setUnpaidHere((current)=>{
|
|
@@ -353,6 +371,10 @@ const narrowed = (filter)=>[
|
|
|
353
371
|
"data-starter-owned": pickedRow.listingId,
|
|
354
372
|
sx: PICK_NOTE_SX,
|
|
355
373
|
children: 'You own this starter. Create your site from it whenever you are ready.'
|
|
374
|
+
}) : purchase.panel ? /*#__PURE__*/ _jsx(Box, {
|
|
375
|
+
"data-starter-pay": pickedRow.listingId,
|
|
376
|
+
sx: PAY_PANEL_SX,
|
|
377
|
+
children: purchase.panel
|
|
356
378
|
}) : /*#__PURE__*/ _jsx(Alert, {
|
|
357
379
|
severity: "info",
|
|
358
380
|
"data-starter-buy": pickedRow.listingId,
|
|
@@ -364,7 +386,7 @@ const narrowed = (filter)=>[
|
|
|
364
386
|
onClick: ()=>void buy(pickedRow),
|
|
365
387
|
children: purchase.busy ? 'Opening…' : 'Buy and continue'
|
|
366
388
|
}),
|
|
367
|
-
children: `${pickedRow.displayName || 'This starter'} costs ${starterPriceLabel(pickedRow.priceUsd)}. ` + 'Buy it
|
|
389
|
+
children: `${pickedRow.displayName || 'This starter'} costs ${starterPriceLabel(pickedRow.priceUsd)}. ` + 'Buy it here, then create your site from it.'
|
|
368
390
|
}) : null,
|
|
369
391
|
purchase.error ? /*#__PURE__*/ _jsx(Alert, {
|
|
370
392
|
severity: "error",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/starter-picker-shelf.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { resolveMediaSrc } from '@aglyn/aglyn/app-utils/media-ref'\nimport type { SiteStarterFacets } from '@aglyn/aglyn/app-utils/site-starter'\nimport type { ConsoleHostStarterPickerZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport type { SiteStarterSummary } from '@aglyn/aglyn/plugin-manager/plugin-site-starters'\nimport { mdiWeb } from '@aglyn/shared-data-mdi'\nimport {\n OptionCardGrid,\n type OptionCard,\n type OptionCardChip,\n type OptionCardImage,\n} from '@aglyn/shared-ui-jsx/components/option-card-grid.component'\nimport {\n Alert,\n Box,\n Button,\n Link,\n LinearProgress,\n Skeleton,\n Stack,\n type SxProps,\n type Theme,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { useStarterAnswers } from '../hooks/use-starter-answers'\nimport { useStarterPurchase } from '../hooks/use-starter-purchase'\nimport { useStarterSearch } from '../hooks/use-starter-search'\nimport { starterCardFacts, starterPriceLabel, type StarterCardFact } from '../model/starter-query'\n\n/**\n * What a picked starter's price or licence says, held at the foot of the\n * visible cards while the shelf is in view, so \"Buy and continue\" is beside\n * whichever card was clicked rather than below all of them.\n */\nconst PICK_NOTE_SX: SxProps<Theme> = {\n position: 'sticky',\n bottom: (theme) => theme.spacing(2),\n zIndex: 1,\n boxShadow: 3,\n}\n\n/** The guide to site starters, linked from what the shelf says when it has nothing to show. */\nconst STARTERS_HELP = pluginDocsHelp('siteStarters')\n\n/**\n * A starter's preview as the card draws it: the size the platform's own\n * style previews are rendered at (square), so a row of cards stays level and\n * a publisher's picture is cropped to the same shape from its top.\n */\nconst PREVIEW_SIZE = { width: 800, height: 800 } as const\n\n/** How many placeholder cards stand in for the first answer. */\nconst PLACEHOLDER_CARDS = 3\n\n/** Statuses that mean this workspace may not have starters offered here: the dialog's own choice instead. */\nconst REFUSED = new Set([401, 403, 503])\n\n/** How each fact's chip is colored, from the theme: the price as the marketplace's own cards color it. */\nfunction chipOf(fact: StarterCardFact, row: SiteStarterSummary): OptionCardChip {\n switch (fact.key) {\n case 'price':\n return row.priceUsd > 0\n ? { label: fact.label, color: 'primary', variant: 'filled' }\n : { label: fact.label, color: 'success', variant: 'outlined' }\n case 'official':\n return { label: fact.label, color: 'primary', variant: 'outlined' }\n case 'ai':\n return { label: fact.label, color: 'secondary', variant: 'outlined' }\n default:\n return { label: fact.label }\n }\n}\n\n/** The picture a card draws, resolved the way the listing's own pages resolve it. */\nfunction previewOf(row: SiteStarterSummary): OptionCardImage | null {\n const src = resolveMediaSrc(row.previewImageUrl)\n if (!src) return null\n return { src, alt: `A preview of ${row.displayName || 'this starter'}`, ...PREVIEW_SIZE }\n}\n\n/** One starter as a card in the shared option grid. */\nfunction cardOf(row: SiteStarterSummary): OptionCard {\n const image = previewOf(row)\n return {\n id: row.listingId,\n title: row.displayName || 'Untitled starter',\n ...(row.authorName ? { description: `By ${row.authorName}` } : {}),\n ...(image ? { image } : { icon: mdiWeb.path }),\n chips: starterCardFacts(row).map((fact) => chipOf(fact, row)),\n }\n}\n\nconst narrowed = (filter: Partial<SiteStarterFacets>): boolean =>\n (['kind', 'base', 'fonts'] as const).some((key) => typeof filter[key] === 'string' && filter[key] !== '')\n\n/** A row with what the dialog or a purchase knows about owning it laid over the search's answer. */\nconst ownedOf = (row: SiteStarterSummary, override: boolean | undefined): boolean =>\n override ?? row.owned === true\n\n/** What an answer with no starters says, in the words of what was asked. */\nfunction emptyMessage(props: ConsoleHostStarterPickerZoneProps): string {\n if (props.search.trim()) return 'No starters match — try fewer words'\n if (narrowed(props.filter ?? {})) return 'No starters match these choices — try another'\n return props.mode === 'ai' ? 'No AI starters yet' : 'No starters yet'\n}\n\n/**\n * The marketplace's starters in \"Choose your starter\" (AGL-4049): the\n * `hostStarterPicker` zone, drawn before the site exists.\n *\n * Lists what the dialog asks for — its mode, its search words and its\n * filters — as cards of the shared option grid: a picture, the name, who made\n * it, and its price, kind, page count and the \"AI\" and platform marks as\n * chips. NOTHING is selected when it opens; a person picks, and the pick goes\n * to the dialog through `onPick`. It creates nothing: the dialog creates the\n * site from the starter it was handed.\n *\n * `ready` tells the dialog whether a picker is on screen. The first answer\n * decides: starters, or a search that found none (its empty state is a\n * picker with nothing in it yet), answer `true`; a workspace refused the\n * starters (no permission, the release flag off, no source) or a catalog with\n * nothing in it answers `false`, and the dialog offers its own choice\n * instead. Once shown, a later refusal hands back the same way; a later\n * failure is said here with a way to ask again.\n *\n * A picked starter with a price the workspace has not paid says so under\n * the cards, with \"Buy and continue\": the marketplace's payment, which brings\n * the person back to Create site on the same pick. One it owns says that\n * instead. The dialog's own `selected` wins over the shelf's pick: it is how\n * a pick restored after paying is shown chosen, and how one the create\n * refused is cleared.\n *\n * An AI starter the workspace may use — free, owned, or just bought — has\n * the answers it carries read once it is picked (AGL-4049 phase 4), and\n * handed to the dialog with it (`onPick(pick, { answers })`), which goes on\n * to the AI start's questions with them. A priced one not yet bought is\n * never read: its answers are what it sells, and the door would ask payment\n * for them. A door that still asks is told back to the dialog as unowned.\n */\nexport function StarterPickerShelf(props: ConsoleHostStarterPickerZoneProps) {\n const { orgId, mode, search, filter, onPick, ready, selected } = props\n // The pick the dialog opened on, restored after paying for it: asked for\n // by every search from then on, so it is listed (first) wherever the\n // search would have put it, and never moves under the person's cursor.\n const [restored] = useState(() => selected?.listingId ?? null)\n const found = useStarterSearch({ orgId, mode, word: search, filter, include: restored })\n const [picked, setPicked] = useState<string | null>(null)\n const [shown, setShown] = useState(false)\n // Starters a purchase attempt found this workspace already owns.\n const [ownedHere, setOwnedHere] = useState<ReadonlySet<string>>(() => new Set())\n // AI starters whose answers the door still asked payment for (AGL-4049).\n const [unpaidHere, setUnpaidHere] = useState<ReadonlySet<string>>(() => new Set())\n const purchase = useStarterPurchase(orgId)\n const starterAnswers = useStarterAnswers(orgId)\n const value = selected !== undefined ? (selected?.listingId ?? null) : picked\n const pickedRow = value ? (found.rows.find((row) => row.listingId === value) ?? null) : null\n const ownedOverride = (row: SiteStarterSummary): boolean | undefined =>\n ownedHere.has(row.listingId)\n ? true\n : unpaidHere.has(row.listingId)\n ? false\n : selected?.listingId === row.listingId\n ? selected.owned\n : undefined\n\n const refused = found.error !== null && REFUSED.has(found.error.status)\n // `null` until the first answer, which is the only one that may decide\n // there is nothing to show; after that only a refusal takes it back.\n const verdict: boolean | null = !found.settled\n ? null\n : refused\n ? false\n : shown\n ? true\n : found.error\n ? false\n : found.rows.length > 0 || search.trim() !== '' || narrowed(filter ?? {})\n\n useEffect(() => {\n if (verdict === true) setShown(true)\n if (verdict === false) setShown(false)\n }, [verdict])\n\n // Each distinct verdict once, whatever identity the dialog's callback has.\n const readyRef = useRef(ready)\n readyRef.current = ready\n const reported = useRef<boolean | null>(null)\n useEffect(() => {\n if (verdict === null || reported.current === verdict) return\n reported.current = verdict\n readyRef.current(verdict)\n }, [verdict])\n\n // A pick the dialog restored is handed back once the shelf has read it,\n // so the dialog holds the starter as the search describes it.\n const onPickRef = useRef(onPick)\n onPickRef.current = onPick\n const handedBack = useRef(false)\n useEffect(() => {\n if (!restored || handedBack.current || !pickedRow || pickedRow.listingId !== restored) return\n handedBack.current = true\n const override = ownedOverride(pickedRow)\n onPickRef.current(override === undefined ? pickedRow : { ...pickedRow, owned: override })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [restored, pickedRow])\n\n // The picked AI starter's answers (AGL-4049), read once it is one the\n // workspace may use; keyed on the listing and whether it is owned, so a\n // purchase that makes it usable reads it, and nothing reads it twice.\n const pickedOwned = pickedRow ? ownedOf(pickedRow, ownedOverride(pickedRow)) : false\n const answersKey =\n pickedRow && pickedRow.facets.mode === 'ai' && (pickedRow.priceUsd <= 0 || pickedOwned)\n ? `${pickedRow.listingId}:${pickedOwned}`\n : null\n const [reading, setReading] = useState<{ key: string; failed: string | null } | null>(null)\n const [readAttempt, setReadAttempt] = useState(0)\n const askedAnswers = useRef<string | null>(null)\n useEffect(() => {\n if (!answersKey || !pickedRow || askedAnswers.current === answersKey) return\n askedAnswers.current = answersKey\n const row = pickedRow\n const override = ownedOverride(row)\n const handed = override === undefined ? row : { ...row, owned: override }\n setReading({ key: answersKey, failed: null })\n void starterAnswers.read(row.listingId).then((outcome) => {\n if (askedAnswers.current !== answersKey) return\n if (outcome.status === 'failed') return setReading({ key: answersKey, failed: outcome.message })\n setReading(null)\n if (outcome.status === 'ready') return onPickRef.current(handed, { answers: outcome.answers })\n setUnpaidHere((current) => new Set(current).add(row.listingId))\n onPickRef.current({ ...row, owned: false })\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [answersKey, readAttempt])\n const readingPicked = reading !== null && reading.key === answersKey ? reading : null\n const readAgain = () => {\n askedAnswers.current = null\n setReadAttempt((count) => count + 1)\n }\n\n const options = useMemo(() => found.rows.map(cardOf), [found.rows])\n\n const buy = async (row: SiteStarterSummary) => {\n const outcome = await purchase.buy(row.listingId)\n if (outcome !== 'owned') return\n setOwnedHere((current) => new Set(current).add(row.listingId))\n setUnpaidHere((current) => {\n const next = new Set(current)\n next.delete(row.listingId)\n return next\n })\n onPick({ ...row, owned: true })\n }\n\n if (verdict === false) return null\n if (verdict === null) {\n return (\n <Box\n aria-busy\n aria-label=\"Loading starters\"\n sx={{ display: 'grid', gap: 2, gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, minmax(0, 1fr))', md: 'repeat(3, minmax(0, 1fr))' } }}\n >\n {Array.from({ length: PLACEHOLDER_CARDS }, (_, index) => (\n <Skeleton key={index} variant=\"rounded\" sx={{ height: 'auto', aspectRatio: '1 / 1', borderRadius: 2 }} />\n ))}\n </Box>\n )\n }\n\n return (\n <Stack spacing={2} data-starter-shelf={mode}>\n {found.loading ? <LinearProgress aria-label=\"Searching starters\" /> : null}\n {found.error ? (\n <Alert\n severity=\"error\"\n action={\n <Button color=\"inherit\" size=\"small\" onClick={found.refresh}>\n {'Try again'}\n </Button>\n }\n >\n {found.error.message}\n </Alert>\n ) : options.length === 0 ? (\n <Alert\n severity=\"info\"\n action={\n <Button\n color=\"inherit\"\n size=\"small\"\n component={Link}\n href={STARTERS_HELP.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {'How starters work'}\n </Button>\n }\n >\n {emptyMessage(props)}\n </Alert>\n ) : (\n <OptionCardGrid\n label={mode === 'ai' ? 'AI starters' : 'Starters'}\n options={options}\n value={value}\n columns={{ xs: 1, sm: 2, md: 3 }}\n onChange={(listingId) => {\n const row = found.rows.find((candidate) => candidate.listingId === listingId)\n if (!row) return\n setPicked(listingId)\n const override = ownedOverride(row)\n onPick(override === undefined ? row : { ...row, owned: override })\n }}\n />\n )}\n {pickedRow && pickedRow.priceUsd > 0 ? (\n ownedOf(pickedRow, ownedOverride(pickedRow)) ? (\n <Alert severity=\"success\" data-starter-owned={pickedRow.listingId} sx={PICK_NOTE_SX}>\n {'You own this starter. Create your site from it whenever you are ready.'}\n </Alert>\n ) : (\n <Alert\n severity=\"info\"\n data-starter-buy={pickedRow.listingId}\n sx={PICK_NOTE_SX}\n action={\n <Button\n color=\"inherit\"\n size=\"small\"\n disabled={purchase.busy || !orgId}\n onClick={() => void buy(pickedRow)}\n >\n {purchase.busy ? 'Opening…' : 'Buy and continue'}\n </Button>\n }\n >\n {`${pickedRow.displayName || 'This starter'} costs ${starterPriceLabel(pickedRow.priceUsd)}. ` +\n 'Buy it first; once paid, you come back here to create your site from it.'}\n </Alert>\n )\n ) : null}\n {purchase.error ? <Alert severity=\"error\">{purchase.error}</Alert> : null}\n {readingPicked?.failed ? (\n <Alert\n severity=\"error\"\n data-starter-answers-failed={pickedRow?.listingId}\n action={\n <Button color=\"inherit\" size=\"small\" onClick={readAgain}>\n {'Try again'}\n </Button>\n }\n >\n {readingPicked.failed}\n </Alert>\n ) : readingPicked ? (\n <LinearProgress aria-label=\"Reading this starter’s answers\" />\n ) : null}\n </Stack>\n )\n}\n\nexport default StarterPickerShelf\n"],"names":["pluginDocsHelp","resolveMediaSrc","mdiWeb","OptionCardGrid","Alert","Box","Button","Link","LinearProgress","Skeleton","Stack","useEffect","useMemo","useRef","useState","useStarterAnswers","useStarterPurchase","useStarterSearch","starterCardFacts","starterPriceLabel","PICK_NOTE_SX","position","bottom","theme","spacing","zIndex","boxShadow","STARTERS_HELP","PREVIEW_SIZE","width","height","PLACEHOLDER_CARDS","REFUSED","Set","chipOf","fact","row","key","priceUsd","label","color","variant","previewOf","src","previewImageUrl","alt","displayName","cardOf","image","id","listingId","title","authorName","description","icon","path","chips","map","narrowed","filter","some","ownedOf","override","owned","emptyMessage","props","search","trim","mode","StarterPickerShelf","found","orgId","onPick","ready","selected","restored","word","include","picked","setPicked","shown","setShown","ownedHere","setOwnedHere","unpaidHere","setUnpaidHere","purchase","starterAnswers","value","undefined","pickedRow","rows","find","ownedOverride","has","refused","error","status","verdict","settled","length","readyRef","current","reported","onPickRef","handedBack","pickedOwned","answersKey","facets","reading","setReading","readAttempt","setReadAttempt","askedAnswers","handed","failed","read","then","outcome","message","answers","add","readingPicked","readAgain","count","options","buy","next","delete","aria-busy","aria-label","sx","display","gap","gridTemplateColumns","xs","sm","md","Array","from","_","index","aspectRatio","borderRadius","data-starter-shelf","loading","severity","action","size","onClick","refresh","component","href","target","rel","columns","onChange","candidate","data-starter-owned","data-starter-buy","disabled","busy","data-starter-answers-failed"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SAASA,cAAc,QAAQ,mCAAkC;AACjE,SAASC,eAAe,QAAQ,mCAAkC;AAIlE,SAASC,MAAM,QAAQ,yBAAwB;AAC/C,SACEC,cAAc,QAIT,6DAA4D;AACnE,SACEC,KAAK,EACLC,GAAG,EACHC,MAAM,EACNC,IAAI,EACJC,cAAc,EACdC,QAAQ,EACRC,KAAK,QAGA,gBAAe;AACtB,SAASC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAC5D,SAASC,iBAAiB,QAAQ,kCAA8B;AAChE,SAASC,kBAAkB,QAAQ,mCAA+B;AAClE,SAASC,gBAAgB,QAAQ,iCAA6B;AAC9D,SAASC,gBAAgB,EAAEC,iBAAiB,QAA8B,4BAAwB;AAElG;;;;CAIC,GACD,MAAMC,eAA+B;IACnCC,UAAU;IACVC,QAAQ,CAACC,QAAUA,MAAMC,OAAO,CAAC;IACjCC,QAAQ;IACRC,WAAW;AACb;AAEA,6FAA6F,GAC7F,MAAMC,gBAAgB3B,eAAe;AAErC;;;;CAIC,GACD,MAAM4B,eAAe;IAAEC,OAAO;IAAKC,QAAQ;AAAI;AAE/C,8DAA8D,GAC9D,MAAMC,oBAAoB;AAE1B,2GAA2G,GAC3G,MAAMC,UAAU,IAAIC,IAAI;IAAC;IAAK;IAAK;CAAI;AAEvC,wGAAwG,GACxG,SAASC,OAAOC,IAAqB,EAAEC,GAAuB;IAC5D,OAAQD,KAAKE,GAAG;QACd,KAAK;YACH,OAAOD,IAAIE,QAAQ,GAAG,IAClB;gBAAEC,OAAOJ,KAAKI,KAAK;gBAAEC,OAAO;gBAAWC,SAAS;YAAS,IACzD;gBAAEF,OAAOJ,KAAKI,KAAK;gBAAEC,OAAO;gBAAWC,SAAS;YAAW;QACjE,KAAK;YACH,OAAO;gBAAEF,OAAOJ,KAAKI,KAAK;gBAAEC,OAAO;gBAAWC,SAAS;YAAW;QACpE,KAAK;YACH,OAAO;gBAAEF,OAAOJ,KAAKI,KAAK;gBAAEC,OAAO;gBAAaC,SAAS;YAAW;QACtE;YACE,OAAO;gBAAEF,OAAOJ,KAAKI,KAAK;YAAC;IAC/B;AACF;AAEA,mFAAmF,GACnF,SAASG,UAAUN,GAAuB;IACxC,MAAMO,MAAM1C,gBAAgBmC,IAAIQ,eAAe;IAC/C,IAAI,CAACD,KAAK,OAAO;IACjB,OAAO;QAAEA;QAAKE,KAAK,CAAC,aAAa,EAAET,IAAIU,WAAW,IAAI,gBAAgB;OAAKlB;AAC7E;AAEA,qDAAqD,GACrD,SAASmB,OAAOX,GAAuB;IACrC,MAAMY,QAAQN,UAAUN;IACxB,OAAO;QACLa,IAAIb,IAAIc,SAAS;QACjBC,OAAOf,IAAIU,WAAW,IAAI;OACtBV,IAAIgB,UAAU,GAAG;QAAEC,aAAa,CAAC,GAAG,EAAEjB,IAAIgB,UAAU,EAAE;IAAC,IAAI,CAAC,GAC5DJ,QAAQ;QAAEA;IAAM,IAAI;QAAEM,MAAMpD,OAAOqD,IAAI;IAAC;QAC5CC,OAAOtC,iBAAiBkB,KAAKqB,GAAG,CAAC,CAACtB,OAASD,OAAOC,MAAMC;;AAE5D;AAEA,MAAMsB,WAAW,CAACC,SAChB,AAAC;QAAC;QAAQ;QAAQ;KAAQ,CAAWC,IAAI,CAAC,CAACvB,MAAQ,OAAOsB,MAAM,CAACtB,IAAI,KAAK,YAAYsB,MAAM,CAACtB,IAAI,KAAK;AAExG,kGAAkG,GAClG,MAAMwB,UAAU,CAACzB,KAAyB0B,WACxCA,mBAAAA,WAAY1B,IAAI2B,KAAK,KAAK;AAE5B,0EAA0E,GAC1E,SAASC,aAAaC,KAAwC;QAE/CA;IADb,IAAIA,MAAMC,MAAM,CAACC,IAAI,IAAI,OAAO;IAChC,IAAIT,UAASO,gBAAAA,MAAMN,MAAM,YAAZM,gBAAgB,CAAC,IAAI,OAAO;IACzC,OAAOA,MAAMG,IAAI,KAAK,OAAO,uBAAuB;AACtD;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCC,GACD,OAAO,SAASC,mBAAmBJ,KAAwC;cAgB9CK;IAf3B,MAAM,EAAEC,KAAK,EAAEH,IAAI,EAAEF,MAAM,EAAEP,MAAM,EAAEa,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAE,GAAGT;IACjE,yEAAyE;IACzE,qEAAqE;IACrE,uEAAuE;IACvE,MAAM,CAACU,SAAS,GAAG7D,SAAS;;uBAAM4D,4BAAAA,SAAUxB,SAAS,mBAAI;;IACzD,MAAMoB,QAAQrD,iBAAiB;QAAEsD;QAAOH;QAAMQ,MAAMV;QAAQP;QAAQkB,SAASF;IAAS;IACtF,MAAM,CAACG,QAAQC,UAAU,GAAGjE,SAAwB;IACpD,MAAM,CAACkE,OAAOC,SAAS,GAAGnE,SAAS;IACnC,iEAAiE;IACjE,MAAM,CAACoE,WAAWC,aAAa,GAAGrE,SAA8B,IAAM,IAAImB;IAC1E,yEAAyE;IACzE,MAAM,CAACmD,YAAYC,cAAc,GAAGvE,SAA8B,IAAM,IAAImB;IAC5E,MAAMqD,WAAWtE,mBAAmBuD;IACpC,MAAMgB,iBAAiBxE,kBAAkBwD;IACzC,MAAMiB,QAAQd,aAAae,oBAAaf,4BAAAA,SAAUxB,SAAS,mBAAI,OAAQ4B;IACvE,MAAMY,YAAYF,SAASlB,mBAAAA,MAAMqB,IAAI,CAACC,IAAI,CAAC,CAACxD,MAAQA,IAAIc,SAAS,KAAKsC,kBAA3ClB,mBAAqD,OAAQ;IACxF,MAAMuB,gBAAgB,CAACzD,MACrB8C,UAAUY,GAAG,CAAC1D,IAAIc,SAAS,IACvB,OACAkC,WAAWU,GAAG,CAAC1D,IAAIc,SAAS,IAC1B,QACAwB,CAAAA,4BAAAA,SAAUxB,SAAS,MAAKd,IAAIc,SAAS,GACnCwB,SAASX,KAAK,GACd0B;IAEV,MAAMM,UAAUzB,MAAM0B,KAAK,KAAK,QAAQhE,QAAQ8D,GAAG,CAACxB,MAAM0B,KAAK,CAACC,MAAM;IACtE,uEAAuE;IACvE,qEAAqE;IACrE,MAAMC,UAA0B,CAAC5B,MAAM6B,OAAO,GAC1C,OACAJ,UACE,QACAf,QACE,OACAV,MAAM0B,KAAK,GACT,QACA1B,MAAMqB,IAAI,CAACS,MAAM,GAAG,KAAKlC,OAAOC,IAAI,OAAO,MAAMT,SAASC,iBAAAA,SAAU,CAAC;IAE/EhD,UAAU;QACR,IAAIuF,YAAY,MAAMjB,SAAS;QAC/B,IAAIiB,YAAY,OAAOjB,SAAS;IAClC,GAAG;QAACiB;KAAQ;IAEZ,2EAA2E;IAC3E,MAAMG,WAAWxF,OAAO4D;IACxB4B,SAASC,OAAO,GAAG7B;IACnB,MAAM8B,WAAW1F,OAAuB;IACxCF,UAAU;QACR,IAAIuF,YAAY,QAAQK,SAASD,OAAO,KAAKJ,SAAS;QACtDK,SAASD,OAAO,GAAGJ;QACnBG,SAASC,OAAO,CAACJ;IACnB,GAAG;QAACA;KAAQ;IAEZ,wEAAwE;IACxE,8DAA8D;IAC9D,MAAMM,YAAY3F,OAAO2D;IACzBgC,UAAUF,OAAO,GAAG9B;IACpB,MAAMiC,aAAa5F,OAAO;IAC1BF,UAAU;QACR,IAAI,CAACgE,YAAY8B,WAAWH,OAAO,IAAI,CAACZ,aAAaA,UAAUxC,SAAS,KAAKyB,UAAU;QACvF8B,WAAWH,OAAO,GAAG;QACrB,MAAMxC,WAAW+B,cAAcH;QAC/Bc,UAAUF,OAAO,CAACxC,aAAa2B,YAAYC,YAAY,aAAKA;YAAW3B,OAAOD;;IAC9E,uDAAuD;IACzD,GAAG;QAACa;QAAUe;KAAU;IAExB,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,MAAMgB,cAAchB,YAAY7B,QAAQ6B,WAAWG,cAAcH,cAAc;IAC/E,MAAMiB,aACJjB,aAAaA,UAAUkB,MAAM,CAACxC,IAAI,KAAK,QAASsB,CAAAA,UAAUpD,QAAQ,IAAI,KAAKoE,WAAU,IACjF,GAAGhB,UAAUxC,SAAS,CAAC,CAAC,EAAEwD,aAAa,GACvC;IACN,MAAM,CAACG,SAASC,WAAW,GAAGhG,SAAwD;IACtF,MAAM,CAACiG,aAAaC,eAAe,GAAGlG,SAAS;IAC/C,MAAMmG,eAAepG,OAAsB;IAC3CF,UAAU;QACR,IAAI,CAACgG,cAAc,CAACjB,aAAauB,aAAaX,OAAO,KAAKK,YAAY;QACtEM,aAAaX,OAAO,GAAGK;QACvB,MAAMvE,MAAMsD;QACZ,MAAM5B,WAAW+B,cAAczD;QAC/B,MAAM8E,SAASpD,aAAa2B,YAAYrD,MAAM,aAAKA;YAAK2B,OAAOD;;QAC/DgD,WAAW;YAAEzE,KAAKsE;YAAYQ,QAAQ;QAAK;QAC3C,KAAK5B,eAAe6B,IAAI,CAAChF,IAAIc,SAAS,EAAEmE,IAAI,CAAC,CAACC;YAC5C,IAAIL,aAAaX,OAAO,KAAKK,YAAY;YACzC,IAAIW,QAAQrB,MAAM,KAAK,UAAU,OAAOa,WAAW;gBAAEzE,KAAKsE;gBAAYQ,QAAQG,QAAQC,OAAO;YAAC;YAC9FT,WAAW;YACX,IAAIQ,QAAQrB,MAAM,KAAK,SAAS,OAAOO,UAAUF,OAAO,CAACY,QAAQ;gBAAEM,SAASF,QAAQE,OAAO;YAAC;YAC5FnC,cAAc,CAACiB,UAAY,IAAIrE,IAAIqE,SAASmB,GAAG,CAACrF,IAAIc,SAAS;YAC7DsD,UAAUF,OAAO,CAAC,aAAKlE;gBAAK2B,OAAO;;QACrC;IACA,uDAAuD;IACzD,GAAG;QAAC4C;QAAYI;KAAY;IAC5B,MAAMW,gBAAgBb,YAAY,QAAQA,QAAQxE,GAAG,KAAKsE,aAAaE,UAAU;IACjF,MAAMc,YAAY;QAChBV,aAAaX,OAAO,GAAG;QACvBU,eAAe,CAACY,QAAUA,QAAQ;IACpC;IAEA,MAAMC,UAAUjH,QAAQ,IAAM0D,MAAMqB,IAAI,CAAClC,GAAG,CAACV,SAAS;QAACuB,MAAMqB,IAAI;KAAC;IAElE,MAAMmC,MAAM,OAAO1F;QACjB,MAAMkF,UAAU,MAAMhC,SAASwC,GAAG,CAAC1F,IAAIc,SAAS;QAChD,IAAIoE,YAAY,SAAS;QACzBnC,aAAa,CAACmB,UAAY,IAAIrE,IAAIqE,SAASmB,GAAG,CAACrF,IAAIc,SAAS;QAC5DmC,cAAc,CAACiB;YACb,MAAMyB,OAAO,IAAI9F,IAAIqE;YACrByB,KAAKC,MAAM,CAAC5F,IAAIc,SAAS;YACzB,OAAO6E;QACT;QACAvD,OAAO,aAAKpC;YAAK2B,OAAO;;IAC1B;IAEA,IAAImC,YAAY,OAAO,OAAO;IAC9B,IAAIA,YAAY,MAAM;QACpB,qBACE,KAAC7F;YACC4H,WAAS;YACTC,cAAW;YACXC,IAAI;gBAAEC,SAAS;gBAAQC,KAAK;gBAAGC,qBAAqB;oBAAEC,IAAI;oBAAOC,IAAI;oBAA6BC,IAAI;gBAA4B;YAAE;sBAEnIC,MAAMC,IAAI,CAAC;gBAAEvC,QAAQrE;YAAkB,GAAG,CAAC6G,GAAGC,sBAC7C,KAACpI;oBAAqBgC,SAAQ;oBAAU0F,IAAI;wBAAErG,QAAQ;wBAAQgH,aAAa;wBAASC,cAAc;oBAAE;mBAArFF;;IAIvB;IAEA,qBACE,MAACnI;QAAMc,SAAS;QAAGwH,sBAAoB5E;;YACpCE,MAAM2E,OAAO,iBAAG,KAACzI;gBAAe0H,cAAW;iBAA0B;YACrE5D,MAAM0B,KAAK,iBACV,KAAC5F;gBACC8I,UAAS;gBACTC,sBACE,KAAC7I;oBAAOkC,OAAM;oBAAU4G,MAAK;oBAAQC,SAAS/E,MAAMgF,OAAO;8BACxD;;0BAIJhF,MAAM0B,KAAK,CAACuB,OAAO;iBAEpBM,QAAQzB,MAAM,KAAK,kBACrB,KAAChG;gBACC8I,UAAS;gBACTC,sBACE,KAAC7I;oBACCkC,OAAM;oBACN4G,MAAK;oBACLG,WAAWhJ;oBACXiJ,MAAM7H,cAAc6H,IAAI;oBACxBC,QAAO;oBACPC,KAAI;8BAEH;;0BAIJ1F,aAAaC;+BAGhB,KAAC9D;gBACCoC,OAAO6B,SAAS,OAAO,gBAAgB;gBACvCyD,SAASA;gBACTrC,OAAOA;gBACPmE,SAAS;oBAAEpB,IAAI;oBAAGC,IAAI;oBAAGC,IAAI;gBAAE;gBAC/BmB,UAAU,CAAC1G;oBACT,MAAMd,MAAMkC,MAAMqB,IAAI,CAACC,IAAI,CAAC,CAACiE,YAAcA,UAAU3G,SAAS,KAAKA;oBACnE,IAAI,CAACd,KAAK;oBACV2C,UAAU7B;oBACV,MAAMY,WAAW+B,cAAczD;oBAC/BoC,OAAOV,aAAa2B,YAAYrD,MAAM,aAAKA;wBAAK2B,OAAOD;;gBACzD;;YAGH4B,aAAaA,UAAUpD,QAAQ,GAAG,IACjCuB,QAAQ6B,WAAWG,cAAcH,4BAC/B,KAACtF;gBAAM8I,UAAS;gBAAUY,sBAAoBpE,UAAUxC,SAAS;gBAAEiF,IAAI/G;0BACpE;+BAGH,KAAChB;gBACC8I,UAAS;gBACTa,oBAAkBrE,UAAUxC,SAAS;gBACrCiF,IAAI/G;gBACJ+H,sBACE,KAAC7I;oBACCkC,OAAM;oBACN4G,MAAK;oBACLY,UAAU1E,SAAS2E,IAAI,IAAI,CAAC1F;oBAC5B8E,SAAS,IAAM,KAAKvB,IAAIpC;8BAEvBJ,SAAS2E,IAAI,GAAG,aAAa;;0BAIjC,GAAGvE,UAAU5C,WAAW,IAAI,eAAe,OAAO,EAAE3B,kBAAkBuE,UAAUpD,QAAQ,EAAE,EAAE,CAAC,GAC5F;iBAGJ;YACHgD,SAASU,KAAK,iBAAG,KAAC5F;gBAAM8I,UAAS;0BAAS5D,SAASU,KAAK;iBAAY;YACpE0B,CAAAA,iCAAAA,cAAeP,MAAM,kBACpB,KAAC/G;gBACC8I,UAAS;gBACTgB,6BAA2B,EAAExE,6BAAAA,UAAWxC,SAAS;gBACjDiG,sBACE,KAAC7I;oBAAOkC,OAAM;oBAAU4G,MAAK;oBAAQC,SAAS1B;8BAC3C;;0BAIJD,cAAcP,MAAM;iBAErBO,8BACF,KAAClH;gBAAe0H,cAAW;iBACzB;;;AAGV;AAEA,eAAe7D,mBAAkB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/starter-picker-shelf.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { resolveMediaSrc } from '@aglyn/aglyn/app-utils/media-ref'\nimport type { SiteStarterFacets } from '@aglyn/aglyn/app-utils/site-starter'\nimport type { ConsoleHostStarterPickerZoneProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport type { SiteStarterSummary } from '@aglyn/aglyn/plugin-manager/plugin-site-starters'\nimport { mdiWeb } from '@aglyn/shared-data-mdi'\nimport {\n OptionCardGrid,\n type OptionCard,\n type OptionCardChip,\n type OptionCardImage,\n} from '@aglyn/shared-ui-jsx/components/option-card-grid.component'\nimport {\n Alert,\n Box,\n Button,\n Link,\n LinearProgress,\n Skeleton,\n Stack,\n type SxProps,\n type Theme,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { useStarterAnswers } from '../hooks/use-starter-answers'\nimport { useStarterPurchase } from '../hooks/use-starter-purchase'\nimport { useStarterSearch } from '../hooks/use-starter-search'\nimport { starterCardFacts, starterPriceLabel, type StarterCardFact } from '../model/starter-query'\n\n/**\n * What a picked starter's price or licence says, held at the foot of the\n * visible cards while the shelf is in view, so \"Buy and continue\" is beside\n * whichever card was clicked rather than below all of them.\n */\nconst PICK_NOTE_SX: SxProps<Theme> = {\n position: 'sticky',\n bottom: (theme) => theme.spacing(2),\n zIndex: 1,\n boxShadow: 3,\n}\n\n/**\n * The payment, where the note was, on its own surface. It may fill the window\n * below the app bar, so a form that has grown keeps its button on screen; a\n * smaller window scrolls inside it.\n */\nconst PAY_PANEL_SX: SxProps<Theme> = {\n position: 'sticky',\n bottom: (theme) => theme.spacing(2),\n zIndex: 1,\n boxShadow: 3,\n p: 2,\n borderRadius: 1,\n bgcolor: 'background.paper',\n maxHeight: (theme) => `calc(100dvh - ${theme.spacing(10)})`,\n overflow: 'auto',\n}\n\n/** The guide to site starters, linked from what the shelf says when it has nothing to show. */\nconst STARTERS_HELP = pluginDocsHelp('siteStarters')\n\n/**\n * A starter's preview as the card draws it: the size the platform's own\n * style previews are rendered at (square), so a row of cards stays level and\n * a publisher's picture is cropped to the same shape from its top.\n */\nconst PREVIEW_SIZE = { width: 800, height: 800 } as const\n\n/** How many placeholder cards stand in for the first answer. */\nconst PLACEHOLDER_CARDS = 3\n\n/** Statuses that mean this workspace may not have starters offered here: the dialog's own choice instead. */\nconst REFUSED = new Set([401, 403, 503])\n\n/** How each fact's chip is colored, from the theme: the price as the marketplace's own cards color it. */\nfunction chipOf(fact: StarterCardFact, row: SiteStarterSummary): OptionCardChip {\n switch (fact.key) {\n case 'price':\n return row.priceUsd > 0\n ? { label: fact.label, color: 'primary', variant: 'filled' }\n : { label: fact.label, color: 'success', variant: 'outlined' }\n case 'official':\n return { label: fact.label, color: 'primary', variant: 'outlined' }\n case 'ai':\n return { label: fact.label, color: 'secondary', variant: 'outlined' }\n default:\n return { label: fact.label }\n }\n}\n\n/** The picture a card draws, resolved the way the listing's own pages resolve it. */\nfunction previewOf(row: SiteStarterSummary): OptionCardImage | null {\n const src = resolveMediaSrc(row.previewImageUrl)\n if (!src) return null\n return { src, alt: `A preview of ${row.displayName || 'this starter'}`, ...PREVIEW_SIZE }\n}\n\n/** One starter as a card in the shared option grid. */\nfunction cardOf(row: SiteStarterSummary): OptionCard {\n const image = previewOf(row)\n return {\n id: row.listingId,\n title: row.displayName || 'Untitled starter',\n ...(row.authorName ? { description: `By ${row.authorName}` } : {}),\n ...(image ? { image } : { icon: mdiWeb.path }),\n chips: starterCardFacts(row).map((fact) => chipOf(fact, row)),\n }\n}\n\nconst narrowed = (filter: Partial<SiteStarterFacets>): boolean =>\n (['kind', 'base', 'fonts'] as const).some((key) => typeof filter[key] === 'string' && filter[key] !== '')\n\n/** A row with what the dialog or a purchase knows about owning it laid over the search's answer. */\nconst ownedOf = (row: SiteStarterSummary, override: boolean | undefined): boolean =>\n override ?? row.owned === true\n\n/** What an answer with no starters says, in the words of what was asked. */\nfunction emptyMessage(props: ConsoleHostStarterPickerZoneProps): string {\n if (props.search.trim()) return 'No starters match — try fewer words'\n if (narrowed(props.filter ?? {})) return 'No starters match these choices — try another'\n return props.mode === 'ai' ? 'No AI starters yet' : 'No starters yet'\n}\n\n/**\n * The marketplace's starters in \"Choose your starter\" (AGL-4049): the\n * `hostStarterPicker` zone, drawn before the site exists.\n *\n * Lists what the dialog asks for — its mode, its search words and its\n * filters — as cards of the shared option grid: a picture, the name, who made\n * it, and its price, kind, page count and the \"AI\" and platform marks as\n * chips. NOTHING is selected when it opens; a person picks, and the pick goes\n * to the dialog through `onPick`. It creates nothing: the dialog creates the\n * site from the starter it was handed.\n *\n * `ready` tells the dialog whether a picker is on screen. The first answer\n * decides: starters, or a search that found none (its empty state is a\n * picker with nothing in it yet), answer `true`; a workspace refused the\n * starters (no permission, the release flag off, no source) or a catalog with\n * nothing in it answers `false`, and the dialog offers its own choice\n * instead. Once shown, a later refusal hands back the same way; a later\n * failure is said here with a way to ask again.\n *\n * A picked starter with a price the workspace has not paid says so under\n * the cards, with \"Buy and continue\": the marketplace's payment, in place, on\n * the card on file, with the site still to be created. One it owns says that\n * instead. The dialog's own `selected` wins over the shelf's pick: it is how\n * a pick restored after paying is shown chosen, and how one the create\n * refused is cleared.\n *\n * An AI starter the workspace may use — free, owned, or just bought — has\n * the answers it carries read once it is picked (AGL-4049 phase 4), and\n * handed to the dialog with it (`onPick(pick, { answers })`), which goes on\n * to the AI start's questions with them. A priced one not yet bought is\n * never read: its answers are what it sells, and the door would ask payment\n * for them. A door that still asks is told back to the dialog as unowned.\n */\nexport function StarterPickerShelf(props: ConsoleHostStarterPickerZoneProps) {\n const { orgId, orgSlug, mode, search, filter, onPick, ready, selected } = props\n // The pick the dialog opened on, restored after paying for it: asked for\n // by every search from then on, so it is listed (first) wherever the\n // search would have put it, and never moves under the person's cursor.\n const [restored] = useState(() => selected?.listingId ?? null)\n const found = useStarterSearch({ orgId, mode, word: search, filter, include: restored })\n const [picked, setPicked] = useState<string | null>(null)\n const [shown, setShown] = useState(false)\n // Starters a purchase attempt found this workspace already owns.\n const [ownedHere, setOwnedHere] = useState<ReadonlySet<string>>(() => new Set())\n // AI starters whose answers the door still asked payment for (AGL-4049).\n const [unpaidHere, setUnpaidHere] = useState<ReadonlySet<string>>(() => new Set())\n const purchase = useStarterPurchase(orgId, orgSlug)\n const starterAnswers = useStarterAnswers(orgId)\n const value = selected !== undefined ? (selected?.listingId ?? null) : picked\n const pickedRow = value ? (found.rows.find((row) => row.listingId === value) ?? null) : null\n const ownedOverride = (row: SiteStarterSummary): boolean | undefined =>\n ownedHere.has(row.listingId)\n ? true\n : unpaidHere.has(row.listingId)\n ? false\n : selected?.listingId === row.listingId\n ? selected.owned\n : undefined\n\n const refused = found.error !== null && REFUSED.has(found.error.status)\n // `null` until the first answer, which is the only one that may decide\n // there is nothing to show; after that only a refusal takes it back.\n const verdict: boolean | null = !found.settled\n ? null\n : refused\n ? false\n : shown\n ? true\n : found.error\n ? false\n : found.rows.length > 0 || search.trim() !== '' || narrowed(filter ?? {})\n\n useEffect(() => {\n if (verdict === true) setShown(true)\n if (verdict === false) setShown(false)\n }, [verdict])\n\n // Each distinct verdict once, whatever identity the dialog's callback has.\n const readyRef = useRef(ready)\n readyRef.current = ready\n const reported = useRef<boolean | null>(null)\n useEffect(() => {\n if (verdict === null || reported.current === verdict) return\n reported.current = verdict\n readyRef.current(verdict)\n }, [verdict])\n\n // A pick the dialog restored is handed back once the shelf has read it,\n // so the dialog holds the starter as the search describes it.\n const onPickRef = useRef(onPick)\n onPickRef.current = onPick\n const handedBack = useRef(false)\n useEffect(() => {\n if (!restored || handedBack.current || !pickedRow || pickedRow.listingId !== restored) return\n handedBack.current = true\n const override = ownedOverride(pickedRow)\n onPickRef.current(override === undefined ? pickedRow : { ...pickedRow, owned: override })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [restored, pickedRow])\n\n // The picked AI starter's answers (AGL-4049), read once it is one the\n // workspace may use; keyed on the listing and whether it is owned, so a\n // purchase that makes it usable reads it, and nothing reads it twice.\n const pickedOwned = pickedRow ? ownedOf(pickedRow, ownedOverride(pickedRow)) : false\n const answersKey =\n pickedRow && pickedRow.facets.mode === 'ai' && (pickedRow.priceUsd <= 0 || pickedOwned)\n ? `${pickedRow.listingId}:${pickedOwned}`\n : null\n const [reading, setReading] = useState<{ key: string; failed: string | null } | null>(null)\n const [readAttempt, setReadAttempt] = useState(0)\n const askedAnswers = useRef<string | null>(null)\n useEffect(() => {\n if (!answersKey || !pickedRow || askedAnswers.current === answersKey) return\n askedAnswers.current = answersKey\n const row = pickedRow\n const override = ownedOverride(row)\n const handed = override === undefined ? row : { ...row, owned: override }\n setReading({ key: answersKey, failed: null })\n void starterAnswers.read(row.listingId).then((outcome) => {\n if (askedAnswers.current !== answersKey) return\n if (outcome.status === 'failed') return setReading({ key: answersKey, failed: outcome.message })\n setReading(null)\n if (outcome.status === 'ready') return onPickRef.current(handed, { answers: outcome.answers })\n setUnpaidHere((current) => new Set(current).add(row.listingId))\n onPickRef.current({ ...row, owned: false })\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [answersKey, readAttempt])\n const readingPicked = reading !== null && reading.key === answersKey ? reading : null\n const readAgain = () => {\n askedAnswers.current = null\n setReadAttempt((count) => count + 1)\n }\n\n const options = useMemo(() => found.rows.map(cardOf), [found.rows])\n\n const buy = async (row: SiteStarterSummary) => {\n const outcome = await purchase.buy({ listingId: row.listingId, displayName: row.displayName })\n if (outcome !== 'owned') return\n setOwnedHere((current) => new Set(current).add(row.listingId))\n setUnpaidHere((current) => {\n const next = new Set(current)\n next.delete(row.listingId)\n return next\n })\n onPick({ ...row, owned: true })\n }\n\n if (verdict === false) return null\n if (verdict === null) {\n return (\n <Box\n aria-busy\n aria-label=\"Loading starters\"\n sx={{ display: 'grid', gap: 2, gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, minmax(0, 1fr))', md: 'repeat(3, minmax(0, 1fr))' } }}\n >\n {Array.from({ length: PLACEHOLDER_CARDS }, (_, index) => (\n <Skeleton key={index} variant=\"rounded\" sx={{ height: 'auto', aspectRatio: '1 / 1', borderRadius: 2 }} />\n ))}\n </Box>\n )\n }\n\n return (\n <Stack spacing={2} data-starter-shelf={mode}>\n {found.loading ? <LinearProgress aria-label=\"Searching starters\" /> : null}\n {found.error ? (\n <Alert\n severity=\"error\"\n action={\n <Button color=\"inherit\" size=\"small\" onClick={found.refresh}>\n {'Try again'}\n </Button>\n }\n >\n {found.error.message}\n </Alert>\n ) : options.length === 0 ? (\n <Alert\n severity=\"info\"\n action={\n <Button\n color=\"inherit\"\n size=\"small\"\n component={Link}\n href={STARTERS_HELP.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {'How starters work'}\n </Button>\n }\n >\n {emptyMessage(props)}\n </Alert>\n ) : (\n <OptionCardGrid\n label={mode === 'ai' ? 'AI starters' : 'Starters'}\n options={options}\n value={value}\n columns={{ xs: 1, sm: 2, md: 3 }}\n onChange={(listingId) => {\n const row = found.rows.find((candidate) => candidate.listingId === listingId)\n if (!row) return\n setPicked(listingId)\n const override = ownedOverride(row)\n onPick(override === undefined ? row : { ...row, owned: override })\n }}\n />\n )}\n {pickedRow && pickedRow.priceUsd > 0 ? (\n ownedOf(pickedRow, ownedOverride(pickedRow)) ? (\n <Alert severity=\"success\" data-starter-owned={pickedRow.listingId} sx={PICK_NOTE_SX}>\n {'You own this starter. Create your site from it whenever you are ready.'}\n </Alert>\n ) : purchase.panel ? (\n <Box data-starter-pay={pickedRow.listingId} sx={PAY_PANEL_SX}>\n {purchase.panel}\n </Box>\n ) : (\n <Alert\n severity=\"info\"\n data-starter-buy={pickedRow.listingId}\n sx={PICK_NOTE_SX}\n action={\n <Button\n color=\"inherit\"\n size=\"small\"\n disabled={purchase.busy || !orgId}\n onClick={() => void buy(pickedRow)}\n >\n {purchase.busy ? 'Opening…' : 'Buy and continue'}\n </Button>\n }\n >\n {`${pickedRow.displayName || 'This starter'} costs ${starterPriceLabel(pickedRow.priceUsd)}. ` +\n 'Buy it here, then create your site from it.'}\n </Alert>\n )\n ) : null}\n {purchase.error ? <Alert severity=\"error\">{purchase.error}</Alert> : null}\n {readingPicked?.failed ? (\n <Alert\n severity=\"error\"\n data-starter-answers-failed={pickedRow?.listingId}\n action={\n <Button color=\"inherit\" size=\"small\" onClick={readAgain}>\n {'Try again'}\n </Button>\n }\n >\n {readingPicked.failed}\n </Alert>\n ) : readingPicked ? (\n <LinearProgress aria-label=\"Reading this starter’s answers\" />\n ) : null}\n </Stack>\n )\n}\n\nexport default StarterPickerShelf\n"],"names":["pluginDocsHelp","resolveMediaSrc","mdiWeb","OptionCardGrid","Alert","Box","Button","Link","LinearProgress","Skeleton","Stack","useEffect","useMemo","useRef","useState","useStarterAnswers","useStarterPurchase","useStarterSearch","starterCardFacts","starterPriceLabel","PICK_NOTE_SX","position","bottom","theme","spacing","zIndex","boxShadow","PAY_PANEL_SX","p","borderRadius","bgcolor","maxHeight","overflow","STARTERS_HELP","PREVIEW_SIZE","width","height","PLACEHOLDER_CARDS","REFUSED","Set","chipOf","fact","row","key","priceUsd","label","color","variant","previewOf","src","previewImageUrl","alt","displayName","cardOf","image","id","listingId","title","authorName","description","icon","path","chips","map","narrowed","filter","some","ownedOf","override","owned","emptyMessage","props","search","trim","mode","StarterPickerShelf","found","orgId","orgSlug","onPick","ready","selected","restored","word","include","picked","setPicked","shown","setShown","ownedHere","setOwnedHere","unpaidHere","setUnpaidHere","purchase","starterAnswers","value","undefined","pickedRow","rows","find","ownedOverride","has","refused","error","status","verdict","settled","length","readyRef","current","reported","onPickRef","handedBack","pickedOwned","answersKey","facets","reading","setReading","readAttempt","setReadAttempt","askedAnswers","handed","failed","read","then","outcome","message","answers","add","readingPicked","readAgain","count","options","buy","next","delete","aria-busy","aria-label","sx","display","gap","gridTemplateColumns","xs","sm","md","Array","from","_","index","aspectRatio","data-starter-shelf","loading","severity","action","size","onClick","refresh","component","href","target","rel","columns","onChange","candidate","data-starter-owned","panel","data-starter-pay","data-starter-buy","disabled","busy","data-starter-answers-failed"],"mappings":"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"}
|