@aglyn/plugins-marketplace 1.0.0 → 1.2.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/package.json +14 -14
- package/src/lib/components/listing-detail-editor.component.js +26 -26
- package/src/lib/components/listing-detail-editor.component.js.map +1 -1
- package/src/lib/components/plugin-review-detail.component.js +17 -3
- package/src/lib/components/plugin-review-detail.component.js.map +1 -1
- package/src/lib/components/plugin-reviews-queue.component.js +88 -12
- package/src/lib/components/plugin-reviews-queue.component.js.map +1 -1
- package/src/lib/components/site-starter-review-detail.component.d.ts +115 -0
- package/src/lib/components/site-starter-review-detail.component.js +785 -0
- package/src/lib/components/site-starter-review-detail.component.js.map +1 -0
- package/src/lib/components/starter-picker-shelf.component.d.ts +7 -0
- package/src/lib/components/starter-picker-shelf.component.js +75 -1
- package/src/lib/components/starter-picker-shelf.component.js.map +1 -1
- package/src/lib/hooks/use-starter-answers.d.ts +41 -0
- package/src/lib/hooks/use-starter-answers.js +75 -0
- package/src/lib/hooks/use-starter-answers.js.map +1 -0
- package/src/lib/model/listing-query.d.ts +20 -2
- package/src/lib/model/listing-query.js +55 -0
- package/src/lib/model/listing-query.js.map +1 -1
- package/src/lib/model/plugin-rejection-categories.d.ts +17 -2
- package/src/lib/model/plugin-rejection-categories.js +48 -4
- package/src/lib/model/plugin-rejection-categories.js.map +1 -1
- package/src/lib/model/plugin-review-checklist.d.ts +38 -8
- package/src/lib/model/plugin-review-checklist.js +60 -11
- package/src/lib/model/plugin-review-checklist.js.map +1 -1
- package/src/lib/model/plugin-review-status.d.ts +8 -0
- package/src/lib/model/plugin-review-status.js +34 -0
- package/src/lib/model/plugin-review-status.js.map +1 -1
- package/src/lib/model/starter-category.d.ts +58 -0
- package/src/lib/model/starter-category.js +79 -0
- package/src/lib/model/starter-category.js.map +1 -0
- package/src/lib/model/starter-query.d.ts +27 -0
- package/src/lib/model/starter-query.js +42 -0
- package/src/lib/model/starter-query.js.map +1 -1
- package/src/lib/server/admin-reviews.js +294 -45
- package/src/lib/server/admin-reviews.js.map +1 -1
- package/src/lib/server/publish-plugin.js +46 -4
- package/src/lib/server/publish-plugin.js.map +1 -1
- package/src/lib/server/publish-starter.js +4 -0
- package/src/lib/server/publish-starter.js.map +1 -1
- package/src/lib/server/publisher-review-email.d.ts +5 -0
- package/src/lib/server/publisher-review-email.js +2 -1
- package/src/lib/server/publisher-review-email.js.map +1 -1
- package/src/lib/server/starters.d.ts +4 -1
- package/src/lib/server/starters.js +128 -28
- package/src/lib/server/starters.js.map +1 -1
- package/src/lib/server.js +12 -1
- package/src/lib/server.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-marketplace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,19 +25,19 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.
|
|
29
|
-
"@aglyn/aglyn-markdown-editor": "1.
|
|
30
|
-
"@aglyn/shared-data-enums": "1.
|
|
31
|
-
"@aglyn/shared-data-mdi": "1.
|
|
32
|
-
"@aglyn/shared-ui-jsx": "1.
|
|
33
|
-
"@aglyn/shared-ui-next": "1.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.
|
|
35
|
-
"@aglyn/shared-util-email": "1.
|
|
36
|
-
"@aglyn/shared-util-http": "1.
|
|
37
|
-
"@aglyn/shared-util-timestamp": "1.
|
|
38
|
-
"@aglyn/tenant-data-admin": "1.
|
|
39
|
-
"@aglyn/tenant-feature-instance": "1.
|
|
40
|
-
"@aglyn/tenant-runtime": "1.
|
|
28
|
+
"@aglyn/aglyn": "1.2.0",
|
|
29
|
+
"@aglyn/aglyn-markdown-editor": "1.2.0",
|
|
30
|
+
"@aglyn/shared-data-enums": "1.2.0",
|
|
31
|
+
"@aglyn/shared-data-mdi": "1.2.0",
|
|
32
|
+
"@aglyn/shared-ui-jsx": "1.2.0",
|
|
33
|
+
"@aglyn/shared-ui-next": "1.2.0",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.2.0",
|
|
35
|
+
"@aglyn/shared-util-email": "1.2.0",
|
|
36
|
+
"@aglyn/shared-util-http": "1.2.0",
|
|
37
|
+
"@aglyn/shared-util-timestamp": "1.2.0",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.2.0",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.2.0",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.2.0",
|
|
41
41
|
"@swc/helpers": "0.5.23"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
@@ -29,23 +29,12 @@ import { LISTING_LOGO_HINT } from "../constants/listing-media.js";
|
|
|
29
29
|
import { formatMediaRef, pluginDocsHelp, useMediaPicker } from "@aglyn/aglyn";
|
|
30
30
|
import { MarkdownField } from "@aglyn/aglyn-markdown-editor";
|
|
31
31
|
import { PublisherAgreementCheckbox, usePublisherAgreementConsent } from "./publisher-agreement-checkbox.component.js";
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'commerce',
|
|
39
|
-
'communication',
|
|
40
|
-
'content',
|
|
41
|
-
'design',
|
|
42
|
-
'forms',
|
|
43
|
-
'integrations',
|
|
44
|
-
'marketing',
|
|
45
|
-
'productivity',
|
|
46
|
-
'seo',
|
|
47
|
-
'security'
|
|
48
|
-
];
|
|
32
|
+
// The plugin taxonomy the server validates `categories` against, from the one
|
|
33
|
+
// list it is defined in (the editor lives in the plugin it belongs to now).
|
|
34
|
+
import { LISTING_CATEGORIES } from "../model/marketplace-catalog.js";
|
|
35
|
+
import { listingArtifactType } from "../model/listing-visibility.js";
|
|
36
|
+
import { siteStarterCategoryOptions } from "../model/starter-category.js";
|
|
37
|
+
import { SITE_STARTER_ARTIFACT_TYPE } from "@aglyn/aglyn/app-utils/site-starter";
|
|
49
38
|
const MAX_SCREENSHOTS = 6;
|
|
50
39
|
/**
|
|
51
40
|
* Full-page listing editor (AGL-869): the owner edits the whole detail page,
|
|
@@ -76,8 +65,15 @@ const MAX_SCREENSHOTS = 6;
|
|
|
76
65
|
const [readme, setReadme] = useState('');
|
|
77
66
|
const [screenshots, setScreenshots] = useState([]);
|
|
78
67
|
const originalPreview = useRef('');
|
|
68
|
+
/*
|
|
69
|
+
* A site starter is filed under the kind of site it is (AGL-4049), in its
|
|
70
|
+
* own `category`, from the style names; every other listing under the
|
|
71
|
+
* plugin taxonomy, in `categories`. The menu keeps a starter's stored
|
|
72
|
+
* category when it is not a kind, so it never opens on a value it lacks.
|
|
73
|
+
*/ const isStarter = listingArtifactType(listing != null ? listing : {}) === SITE_STARTER_ARTIFACT_TYPE;
|
|
74
|
+
const categoryOptions = isStarter ? siteStarterCategoryOptions(listing == null ? void 0 : listing.category) : LISTING_CATEGORIES;
|
|
79
75
|
useEffect(()=>{
|
|
80
|
-
var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10;
|
|
76
|
+
var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11;
|
|
81
77
|
var _listing_categories;
|
|
82
78
|
setValues({
|
|
83
79
|
displayName: (_ref = listing == null ? void 0 : listing.displayName) != null ? _ref : '',
|
|
@@ -87,11 +83,11 @@ const MAX_SCREENSHOTS = 6;
|
|
|
87
83
|
homepageUrl: (_ref4 = listing == null ? void 0 : listing.homepageUrl) != null ? _ref4 : '',
|
|
88
84
|
repositoryUrl: (_ref5 = listing == null ? void 0 : listing.repositoryUrl) != null ? _ref5 : '',
|
|
89
85
|
license: (_ref6 = listing == null ? void 0 : listing.license) != null ? _ref6 : '',
|
|
90
|
-
category: (_ref7 = (_ref8 = listing == null ? void 0 : (_listing_categories = listing.categories) == null ? void 0 : _listing_categories[0]) != null ?
|
|
86
|
+
category: listingArtifactType(listing != null ? listing : {}) === SITE_STARTER_ARTIFACT_TYPE ? String((_ref7 = listing == null ? void 0 : listing.category) != null ? _ref7 : '').trim() : (_ref8 = (_ref9 = listing == null ? void 0 : (_listing_categories = listing.categories) == null ? void 0 : _listing_categories[0]) != null ? _ref9 : listing == null ? void 0 : listing.category) != null ? _ref8 : ''
|
|
91
87
|
});
|
|
92
|
-
setReadme((
|
|
88
|
+
setReadme((_ref10 = listing == null ? void 0 : listing.readme) != null ? _ref10 : '');
|
|
93
89
|
setScreenshots(Array.isArray(listing == null ? void 0 : listing.screenshots) ? listing.screenshots : []);
|
|
94
|
-
originalPreview.current = (
|
|
90
|
+
originalPreview.current = (_ref11 = listing == null ? void 0 : listing.previewImageUrl) != null ? _ref11 : '';
|
|
95
91
|
}, [
|
|
96
92
|
listing == null ? void 0 : listing.$id
|
|
97
93
|
]);
|
|
@@ -195,7 +191,7 @@ const MAX_SCREENSHOTS = 6;
|
|
|
195
191
|
const response = await authorizedFetch(user, '/api/marketplace/publish-plugin', {
|
|
196
192
|
method: 'POST',
|
|
197
193
|
headers,
|
|
198
|
-
body: JSON.stringify({
|
|
194
|
+
body: JSON.stringify(_extends({
|
|
199
195
|
action: 'update-listing',
|
|
200
196
|
listingId,
|
|
201
197
|
displayName: values.displayName,
|
|
@@ -204,13 +200,17 @@ const MAX_SCREENSHOTS = 6;
|
|
|
204
200
|
logoUrl: values.logoUrl,
|
|
205
201
|
homepageUrl: values.homepageUrl,
|
|
206
202
|
repositoryUrl: values.repositoryUrl,
|
|
207
|
-
license: values.license
|
|
203
|
+
license: values.license
|
|
204
|
+
}, isStarter ? {
|
|
205
|
+
category: values.category
|
|
206
|
+
} : {
|
|
208
207
|
categories: values.category ? [
|
|
209
208
|
values.category
|
|
210
|
-
] : []
|
|
209
|
+
] : []
|
|
210
|
+
}, {
|
|
211
211
|
screenshots,
|
|
212
212
|
agreement: agreement.consent
|
|
213
|
-
})
|
|
213
|
+
}))
|
|
214
214
|
});
|
|
215
215
|
const payload = await response.json().catch(()=>({}));
|
|
216
216
|
if (!response.ok) {
|
|
@@ -438,7 +438,7 @@ const MAX_SCREENSHOTS = 6;
|
|
|
438
438
|
value: "",
|
|
439
439
|
children: 'None'
|
|
440
440
|
}),
|
|
441
|
-
|
|
441
|
+
categoryOptions.map((entry)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
442
442
|
value: entry,
|
|
443
443
|
children: entry
|
|
444
444
|
}, entry))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/listing-detail-editor.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 { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\n// The stored preview may be a `media:` reference (AGL-1424), which is not a\n// URL. Resolved where it is DISPLAYED, never in `values`.\nimport { resolveMediaSrc } from '@aglyn/aglyn/app-utils/media-ref'\nimport { inheritedMediaAlt } from '@aglyn/aglyn/app-utils/media-metadata'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Box,\n Button,\n Divider,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useRef, useState } from 'react'\nimport { LISTING_LOGO_HINT } from '../constants/listing-media'\nimport { formatMediaRef, pluginDocsHelp, useMediaPicker } from '@aglyn/aglyn'\nimport {\n MarkdownField,\n type MarkdownFieldHandle,\n} from '@aglyn/aglyn-markdown-editor'\nimport {\n PublisherAgreementCheckbox,\n usePublisherAgreementConsent,\n} from './publisher-agreement-checkbox.component'\n\n// Mirrors the server's fixed taxonomy (marketplace plugin model), which the\n// console can't import across the aglyn:addons boundary. The server\n// re-validates, so drift fails the save rather than corrupting a listing.\nconst LISTING_CATEGORIES = [\n 'analytics',\n 'automation',\n 'commerce',\n 'communication',\n 'content',\n 'design',\n 'forms',\n 'integrations',\n 'marketing',\n 'productivity',\n 'seo',\n 'security',\n] as const\nconst MAX_SCREENSHOTS = 6\n\n/** Which image field / target the DAM picker is currently filling. */\ntype PickTarget = 'preview' | 'logo' | 'screenshot' | 'body' | null\n\nexport interface ListingDetailEditorProps {\n orgId: string\n listingId: string\n listing: Record<string, any>\n /** Firebase user, for the ID token on the update requests. */\n user: unknown\n /** Leave edit mode (Cancel or after a successful save). */\n onDone: () => void\n}\n\n/**\n * Full-page listing editor (AGL-869): the owner edits the whole detail page,\n * not a cramped sidebar card. The body uses the same WYSIWYG markdown editor\n * as blog content (`MarkdownVisualEditor`), and every image — preview, logo,\n * screenshots, and inline body images — picks from the shared DAM.\n *\n * Content saves through the marketplace plugin's HTTP API (`update-listing`);\n * the preview image goes through its dedicated endpoint since it isn't a\n * content field. Both are merges, so an unchanged field is left untouched.\n */\nexport function ListingDetailEditor(props: ListingDetailEditorProps) {\n const { orgId, listingId, listing, user, onDone } = props\n const { enqueueSnackbar } = useSnackbar()\n const editorRef = useRef<MarkdownFieldHandle | null>(null)\n const [busy, setBusy] = useState(false)\n // The publisher agreement, agreed to on this save (AGL-4105).\n const agreement = usePublisherAgreementConsent()\n\n const [values, setValues] = useState({\n displayName: '',\n description: '',\n previewImageUrl: '',\n logoUrl: '',\n homepageUrl: '',\n repositoryUrl: '',\n license: '',\n category: '',\n })\n const [readme, setReadme] = useState('')\n const [screenshots, setScreenshots] = useState<string[]>([])\n const originalPreview = useRef<string>('')\n\n useEffect(() => {\n setValues({\n displayName: listing?.displayName ?? '',\n description: listing?.description ?? '',\n previewImageUrl: listing?.previewImageUrl ?? '',\n logoUrl: listing?.logoUrl ?? '',\n homepageUrl: listing?.homepageUrl ?? '',\n repositoryUrl: listing?.repositoryUrl ?? '',\n license: listing?.license ?? '',\n category: listing?.categories?.[0] ?? listing?.category ?? '',\n })\n setReadme(listing?.readme ?? '')\n setScreenshots(\n Array.isArray(listing?.screenshots) ? listing.screenshots : [],\n )\n originalPreview.current = listing?.previewImageUrl ?? ''\n }, [listing?.$id])\n\n const set = (key: keyof typeof values) => (event: any) =>\n setValues((current) => ({ ...current, [key]: event.target.value }))\n\n /*\n * The console's media library, opened through the picker context rather\n * than by mounting its dialog (AGL-3080). The library is coupled to the\n * org and the session, which a plugin has no way to reach, and the console\n * provides `pickMedia` around every plugin page for exactly this — so the\n * `pickTarget` state that only existed to keep one dialog open for four\n * buttons goes with it: the target is now the argument.\n *\n * The URL comes back already resolved (CDN path over the raw download URL,\n * AGL-1215), which is what `mediaSrc` was doing here.\n */\n const { pickMedia } = useMediaPicker()\n const onPickMedia = async (target: Exclude<PickTarget, null>) => {\n const media = await pickMedia?.({ kind: 'image' })\n if (!media) return\n const url = media.url\n if (!url) return\n if (target === 'preview') setValues((c) => ({ ...c, previewImageUrl: url }))\n else if (target === 'logo') setValues((c) => ({ ...c, logoUrl: url }))\n else if (target === 'body') {\n // The README BODY takes a reference; the three image FIELDS above keep\n // `mediaSrc` (AGL-1705). Not an inconsistency — a field is one string\n // with known readers and its own AGL-1701 validator, while the body is\n // markdown that AGL-1686 taught every renderer to resolve.\n //\n // The absolute form was justified by `mediaSrc`'s own docstring — it\n // prefixes the origin because the markdown \"renders elsewhere\". That\n // was checked rather than assumed, and it is not true. The renderer\n // `MarketplaceListingContent` is registered through\n // `registerConsoleExtension`; its `marketplaceListing` slot has exactly\n // one render site, the console's `/[orgSlug]/marketplace/[listingId]`\n // route; and `apps/tenant` never imports the marketplace plugin at all.\n // The body reaches no other origin by another door either — the social\n // card builds `og:image` from `previewImageUrl`/`logoUrl`, never from\n // the README.\n //\n // So a site-relative resolve lands on the console origin, which serves\n // `/api/media/cdn/[...path]` — the very route the absolute URL pointed\n // at. Same bytes over the same route, minus a baked-in origin and a\n // baked-in route shape that cannot be changed without a migration.\n const src = formatMediaRef(media.mediaScope, media.mediaId) ?? url\n // The alt was a hardcoded '' (AGL-1896). This surface offers no alt\n // prompt at all, so the asset's own alt was the only description that\n // could ever have reached the row — and a markdown-lite image row's\n // alt cannot be edited after insertion, so '' was permanent. Still ''\n // for an asset nobody has described; never a fabricated one.\n editorRef.current?.insertImage(\n inheritedMediaAlt({ assetAlt: media.alt }) ?? '',\n src,\n )\n } else if (target === 'screenshot')\n setScreenshots((current) =>\n current.length >= MAX_SCREENSHOTS || current.includes(url)\n ? current\n : [...current, url],\n )\n }\n\n const save = async () => {\n if (!agreement.consent) return\n setBusy(true)\n try {\n const headers = { 'Content-Type': 'application/json' }\n // Preview image isn't a content field, so it has its own endpoint\n // (AGL-863). Only touch it when it actually changed. Both requests carry\n // the one agreement this save was made with (AGL-4105), and a refused\n // preview stops the save rather than saving the rest without it.\n if (values.previewImageUrl !== originalPreview.current) {\n const preview = await authorizedFetch(user, '/api/marketplace/preview-image', {\n method: values.previewImageUrl ? 'POST' : 'DELETE',\n headers,\n body: JSON.stringify({\n listingId,\n ...(values.previewImageUrl ? { url: values.previewImageUrl } : {}),\n agreement: agreement.consent,\n }),\n })\n if (!preview.ok) {\n const refused = await preview.json().catch(() => ({}))\n return void enqueueSnackbar(refused?.error ?? 'Update failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n originalPreview.current = values.previewImageUrl\n }\n const response = await authorizedFetch(\n user,\n '/api/marketplace/publish-plugin',\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n action: 'update-listing',\n listingId,\n displayName: values.displayName,\n description: values.description,\n readme,\n logoUrl: values.logoUrl,\n homepageUrl: values.homepageUrl,\n repositoryUrl: values.repositoryUrl,\n license: values.license,\n categories: values.category ? [values.category] : [],\n screenshots,\n agreement: agreement.consent,\n }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n return void enqueueSnackbar(payload?.error ?? 'Update failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar('Listing updated', { variant: 'success', persist: false })\n // Each save is its own agreement: the next one ticks the box again.\n agreement.reset()\n onDone()\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\n return (\n <CardDisplay\n header={'Edit listing'}\n help={pluginDocsHelp('publisherHandbook', { anchor: '#edit-listing' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Everything here shows on the public listing page. The body is ' +\n 'rich text — use the toolbar, and add images from your media ' +\n 'library.'}\n </Typography>\n\n <TextField\n label=\"Listing name\"\n value={values.displayName}\n onChange={set('displayName')}\n size=\"small\"\n fullWidth\n />\n <TextField\n label=\"Description\"\n helperText=\"One line, shown on every browse card\"\n value={values.description}\n onChange={set('description')}\n size=\"small\"\n multiline\n minRows={2}\n fullWidth\n />\n\n {/* Preview image — the hero shown on cards and at the top of detail. */}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Preview image'}</Typography>\n {/* Resolved for DISPLAY only (AGL-1424). `values.previewImageUrl`\n stays the stored form — it is what the save POSTs, and writing\n the resolved URL back would undo the reference. */}\n {resolveMediaSrc(values.previewImageUrl) ? (\n <Box\n component=\"img\"\n src={resolveMediaSrc(values.previewImageUrl)}\n alt=\"Preview\"\n sx={{\n width: '100%',\n maxHeight: 220,\n objectFit: 'cover',\n borderRadius: 1,\n border: 1,\n borderColor: 'divider',\n }}\n />\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" onClick={() => onPickMedia('preview')}>\n {values.previewImageUrl ? 'Replace' : 'Choose from library'}\n </Button>\n {values.previewImageUrl ? (\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n setValues((c) => ({ ...c, previewImageUrl: '' }))\n }\n >\n {'Remove'}\n </Button>\n ) : null}\n </Stack>\n </Stack>\n\n <Divider />\n\n {/* Body — the WYSIWYG markdown editor, same as blog content, and\n since AGL-1080 the same component the publish form uses. */}\n <MarkdownField\n label=\"About\"\n value={readme}\n onChange={setReadme}\n onPickImageFromMedia={() => onPickMedia('body')}\n editorRef={(handle) => {\n editorRef.current = handle\n }}\n />\n\n <Divider />\n\n {/* Screenshots gallery. */}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">\n {`Screenshots (${screenshots.length}/${MAX_SCREENSHOTS})`}\n </Typography>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap' }}>\n {screenshots.map((url, index) => (\n <Box key={url} sx={{ position: 'relative' }}>\n <Box\n component=\"img\"\n src={url}\n alt={`Screenshot ${index + 1}`}\n sx={{\n height: 72,\n borderRadius: 1,\n border: 1,\n borderColor: 'divider',\n }}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n setScreenshots((current) =>\n current.filter((entry) => entry !== url),\n )\n }\n >\n {'Remove'}\n </Button>\n </Box>\n ))}\n </Stack>\n {screenshots.length < MAX_SCREENSHOTS ? (\n <Box>\n <Button size=\"small\" onClick={() => onPickMedia('screenshot')}>\n {'Add from library'}\n </Button>\n </Box>\n ) : null}\n </Stack>\n\n <Divider />\n\n {/* Logo + metadata. */}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'flex-start' }}>\n <TextField\n label=\"Logo URL (https)\"\n value={values.logoUrl}\n onChange={set('logoUrl')}\n size=\"small\"\n sx={{ flex: 1 }}\n helperText={LISTING_LOGO_HINT}\n />\n <Button\n size=\"small\"\n onClick={() => onPickMedia('logo')}\n sx={{ mt: 0.5 }}\n >\n {'Library'}\n </Button>\n </Stack>\n <TextField\n label=\"Homepage (https)\"\n value={values.homepageUrl}\n onChange={set('homepageUrl')}\n size=\"small\"\n fullWidth\n />\n <TextField\n label=\"Repository (https)\"\n value={values.repositoryUrl}\n onChange={set('repositoryUrl')}\n size=\"small\"\n fullWidth\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"License\"\n value={values.license}\n onChange={set('license')}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n <TextField\n select\n label=\"Category\"\n value={values.category}\n onChange={set('category')}\n size=\"small\"\n sx={{ flex: 1 }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {LISTING_CATEGORIES.map((entry) => (\n <MenuItem key={entry} value={entry}>\n {entry}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n\n {/* Directly above Save listing (AGL-4105), which waits for it. */}\n <PublisherAgreementCheckbox\n checked={agreement.agreed}\n onChange={agreement.setAgreed}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || !values.displayName.trim() || !agreement.agreed}\n onClick={() => void save()}\n >\n {busy ? 'Saving…' : 'Save listing'}\n </Button>\n <Button disabled={busy} onClick={onDone}>\n {'Cancel'}\n </Button>\n </Stack>\n </Stack>\n\n </CardDisplay>\n )\n}\n\nListingDetailEditor.displayName = 'ListingDetailEditor'\n\nexport default ListingDetailEditor\n"],"names":["CardDisplay","useSnackbar","resolveMediaSrc","inheritedMediaAlt","authorizedFetch","Box","Button","Divider","MenuItem","Stack","TextField","Typography","useEffect","useRef","useState","LISTING_LOGO_HINT","formatMediaRef","pluginDocsHelp","useMediaPicker","MarkdownField","PublisherAgreementCheckbox","usePublisherAgreementConsent","LISTING_CATEGORIES","MAX_SCREENSHOTS","ListingDetailEditor","props","orgId","listingId","listing","user","onDone","enqueueSnackbar","editorRef","busy","setBusy","agreement","values","setValues","displayName","description","previewImageUrl","logoUrl","homepageUrl","repositoryUrl","license","category","readme","setReadme","screenshots","setScreenshots","originalPreview","categories","Array","isArray","current","$id","set","key","event","target","value","pickMedia","onPickMedia","media","kind","url","c","src","mediaScope","mediaId","insertImage","assetAlt","alt","length","includes","save","consent","headers","preview","method","body","JSON","stringify","ok","refused","json","catch","error","variant","allowDuplicate","response","action","payload","persist","reset","console","header","help","anchor","contentGutterX","contentGutterY","spacing","color","label","onChange","size","fullWidth","helperText","multiline","minRows","component","sx","width","maxHeight","objectFit","borderRadius","border","borderColor","direction","onClick","onPickImageFromMedia","handle","useFlexGap","flexWrap","map","index","position","height","filter","entry","alignItems","flex","mt","select","checked","agreed","setAgreed","disabled","trim"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/listing-detail-editor.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 { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\n// The stored preview may be a `media:` reference (AGL-1424), which is not a\n// URL. Resolved where it is DISPLAYED, never in `values`.\nimport { resolveMediaSrc } from '@aglyn/aglyn/app-utils/media-ref'\nimport { inheritedMediaAlt } from '@aglyn/aglyn/app-utils/media-metadata'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Box,\n Button,\n Divider,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useRef, useState } from 'react'\nimport { LISTING_LOGO_HINT } from '../constants/listing-media'\nimport { formatMediaRef, pluginDocsHelp, useMediaPicker } from '@aglyn/aglyn'\nimport {\n MarkdownField,\n type MarkdownFieldHandle,\n} from '@aglyn/aglyn-markdown-editor'\nimport {\n PublisherAgreementCheckbox,\n usePublisherAgreementConsent,\n} from './publisher-agreement-checkbox.component'\n\n// The plugin taxonomy the server validates `categories` against, from the one\n// list it is defined in (the editor lives in the plugin it belongs to now).\nimport { LISTING_CATEGORIES } from '../model/marketplace-catalog'\nimport { listingArtifactType } from '../model/listing-visibility'\nimport { siteStarterCategoryOptions } from '../model/starter-category'\nimport { SITE_STARTER_ARTIFACT_TYPE } from '@aglyn/aglyn/app-utils/site-starter'\n\nconst MAX_SCREENSHOTS = 6\n\n/** Which image field / target the DAM picker is currently filling. */\ntype PickTarget = 'preview' | 'logo' | 'screenshot' | 'body' | null\n\nexport interface ListingDetailEditorProps {\n orgId: string\n listingId: string\n listing: Record<string, any>\n /** Firebase user, for the ID token on the update requests. */\n user: unknown\n /** Leave edit mode (Cancel or after a successful save). */\n onDone: () => void\n}\n\n/**\n * Full-page listing editor (AGL-869): the owner edits the whole detail page,\n * not a cramped sidebar card. The body uses the same WYSIWYG markdown editor\n * as blog content (`MarkdownVisualEditor`), and every image — preview, logo,\n * screenshots, and inline body images — picks from the shared DAM.\n *\n * Content saves through the marketplace plugin's HTTP API (`update-listing`);\n * the preview image goes through its dedicated endpoint since it isn't a\n * content field. Both are merges, so an unchanged field is left untouched.\n */\nexport function ListingDetailEditor(props: ListingDetailEditorProps) {\n const { orgId, listingId, listing, user, onDone } = props\n const { enqueueSnackbar } = useSnackbar()\n const editorRef = useRef<MarkdownFieldHandle | null>(null)\n const [busy, setBusy] = useState(false)\n // The publisher agreement, agreed to on this save (AGL-4105).\n const agreement = usePublisherAgreementConsent()\n\n const [values, setValues] = useState({\n displayName: '',\n description: '',\n previewImageUrl: '',\n logoUrl: '',\n homepageUrl: '',\n repositoryUrl: '',\n license: '',\n category: '',\n })\n const [readme, setReadme] = useState('')\n const [screenshots, setScreenshots] = useState<string[]>([])\n const originalPreview = useRef<string>('')\n /*\n * A site starter is filed under the kind of site it is (AGL-4049), in its\n * own `category`, from the style names; every other listing under the\n * plugin taxonomy, in `categories`. The menu keeps a starter's stored\n * category when it is not a kind, so it never opens on a value it lacks.\n */\n const isStarter = listingArtifactType(listing ?? {}) === SITE_STARTER_ARTIFACT_TYPE\n const categoryOptions: readonly string[] = isStarter\n ? siteStarterCategoryOptions(listing?.category)\n : LISTING_CATEGORIES\n\n useEffect(() => {\n setValues({\n displayName: listing?.displayName ?? '',\n description: listing?.description ?? '',\n previewImageUrl: listing?.previewImageUrl ?? '',\n logoUrl: listing?.logoUrl ?? '',\n homepageUrl: listing?.homepageUrl ?? '',\n repositoryUrl: listing?.repositoryUrl ?? '',\n license: listing?.license ?? '',\n category:\n listingArtifactType(listing ?? {}) === SITE_STARTER_ARTIFACT_TYPE\n ? String(listing?.category ?? '').trim()\n : (listing?.categories?.[0] ?? listing?.category ?? ''),\n })\n setReadme(listing?.readme ?? '')\n setScreenshots(\n Array.isArray(listing?.screenshots) ? listing.screenshots : [],\n )\n originalPreview.current = listing?.previewImageUrl ?? ''\n }, [listing?.$id])\n\n const set = (key: keyof typeof values) => (event: any) =>\n setValues((current) => ({ ...current, [key]: event.target.value }))\n\n /*\n * The console's media library, opened through the picker context rather\n * than by mounting its dialog (AGL-3080). The library is coupled to the\n * org and the session, which a plugin has no way to reach, and the console\n * provides `pickMedia` around every plugin page for exactly this — so the\n * `pickTarget` state that only existed to keep one dialog open for four\n * buttons goes with it: the target is now the argument.\n *\n * The URL comes back already resolved (CDN path over the raw download URL,\n * AGL-1215), which is what `mediaSrc` was doing here.\n */\n const { pickMedia } = useMediaPicker()\n const onPickMedia = async (target: Exclude<PickTarget, null>) => {\n const media = await pickMedia?.({ kind: 'image' })\n if (!media) return\n const url = media.url\n if (!url) return\n if (target === 'preview') setValues((c) => ({ ...c, previewImageUrl: url }))\n else if (target === 'logo') setValues((c) => ({ ...c, logoUrl: url }))\n else if (target === 'body') {\n // The README BODY takes a reference; the three image FIELDS above keep\n // `mediaSrc` (AGL-1705). Not an inconsistency — a field is one string\n // with known readers and its own AGL-1701 validator, while the body is\n // markdown that AGL-1686 taught every renderer to resolve.\n //\n // The absolute form was justified by `mediaSrc`'s own docstring — it\n // prefixes the origin because the markdown \"renders elsewhere\". That\n // was checked rather than assumed, and it is not true. The renderer\n // `MarketplaceListingContent` is registered through\n // `registerConsoleExtension`; its `marketplaceListing` slot has exactly\n // one render site, the console's `/[orgSlug]/marketplace/[listingId]`\n // route; and `apps/tenant` never imports the marketplace plugin at all.\n // The body reaches no other origin by another door either — the social\n // card builds `og:image` from `previewImageUrl`/`logoUrl`, never from\n // the README.\n //\n // So a site-relative resolve lands on the console origin, which serves\n // `/api/media/cdn/[...path]` — the very route the absolute URL pointed\n // at. Same bytes over the same route, minus a baked-in origin and a\n // baked-in route shape that cannot be changed without a migration.\n const src = formatMediaRef(media.mediaScope, media.mediaId) ?? url\n // The alt was a hardcoded '' (AGL-1896). This surface offers no alt\n // prompt at all, so the asset's own alt was the only description that\n // could ever have reached the row — and a markdown-lite image row's\n // alt cannot be edited after insertion, so '' was permanent. Still ''\n // for an asset nobody has described; never a fabricated one.\n editorRef.current?.insertImage(\n inheritedMediaAlt({ assetAlt: media.alt }) ?? '',\n src,\n )\n } else if (target === 'screenshot')\n setScreenshots((current) =>\n current.length >= MAX_SCREENSHOTS || current.includes(url)\n ? current\n : [...current, url],\n )\n }\n\n const save = async () => {\n if (!agreement.consent) return\n setBusy(true)\n try {\n const headers = { 'Content-Type': 'application/json' }\n // Preview image isn't a content field, so it has its own endpoint\n // (AGL-863). Only touch it when it actually changed. Both requests carry\n // the one agreement this save was made with (AGL-4105), and a refused\n // preview stops the save rather than saving the rest without it.\n if (values.previewImageUrl !== originalPreview.current) {\n const preview = await authorizedFetch(user, '/api/marketplace/preview-image', {\n method: values.previewImageUrl ? 'POST' : 'DELETE',\n headers,\n body: JSON.stringify({\n listingId,\n ...(values.previewImageUrl ? { url: values.previewImageUrl } : {}),\n agreement: agreement.consent,\n }),\n })\n if (!preview.ok) {\n const refused = await preview.json().catch(() => ({}))\n return void enqueueSnackbar(refused?.error ?? 'Update failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n originalPreview.current = values.previewImageUrl\n }\n const response = await authorizedFetch(\n user,\n '/api/marketplace/publish-plugin',\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n action: 'update-listing',\n listingId,\n displayName: values.displayName,\n description: values.description,\n readme,\n logoUrl: values.logoUrl,\n homepageUrl: values.homepageUrl,\n repositoryUrl: values.repositoryUrl,\n license: values.license,\n ...(isStarter\n ? { category: values.category }\n : { categories: values.category ? [values.category] : [] }),\n screenshots,\n agreement: agreement.consent,\n }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n return void enqueueSnackbar(payload?.error ?? 'Update failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar('Listing updated', { variant: 'success', persist: false })\n // Each save is its own agreement: the next one ticks the box again.\n agreement.reset()\n onDone()\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\n return (\n <CardDisplay\n header={'Edit listing'}\n help={pluginDocsHelp('publisherHandbook', { anchor: '#edit-listing' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Everything here shows on the public listing page. The body is ' +\n 'rich text — use the toolbar, and add images from your media ' +\n 'library.'}\n </Typography>\n\n <TextField\n label=\"Listing name\"\n value={values.displayName}\n onChange={set('displayName')}\n size=\"small\"\n fullWidth\n />\n <TextField\n label=\"Description\"\n helperText=\"One line, shown on every browse card\"\n value={values.description}\n onChange={set('description')}\n size=\"small\"\n multiline\n minRows={2}\n fullWidth\n />\n\n {/* Preview image — the hero shown on cards and at the top of detail. */}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{'Preview image'}</Typography>\n {/* Resolved for DISPLAY only (AGL-1424). `values.previewImageUrl`\n stays the stored form — it is what the save POSTs, and writing\n the resolved URL back would undo the reference. */}\n {resolveMediaSrc(values.previewImageUrl) ? (\n <Box\n component=\"img\"\n src={resolveMediaSrc(values.previewImageUrl)}\n alt=\"Preview\"\n sx={{\n width: '100%',\n maxHeight: 220,\n objectFit: 'cover',\n borderRadius: 1,\n border: 1,\n borderColor: 'divider',\n }}\n />\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" onClick={() => onPickMedia('preview')}>\n {values.previewImageUrl ? 'Replace' : 'Choose from library'}\n </Button>\n {values.previewImageUrl ? (\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n setValues((c) => ({ ...c, previewImageUrl: '' }))\n }\n >\n {'Remove'}\n </Button>\n ) : null}\n </Stack>\n </Stack>\n\n <Divider />\n\n {/* Body — the WYSIWYG markdown editor, same as blog content, and\n since AGL-1080 the same component the publish form uses. */}\n <MarkdownField\n label=\"About\"\n value={readme}\n onChange={setReadme}\n onPickImageFromMedia={() => onPickMedia('body')}\n editorRef={(handle) => {\n editorRef.current = handle\n }}\n />\n\n <Divider />\n\n {/* Screenshots gallery. */}\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">\n {`Screenshots (${screenshots.length}/${MAX_SCREENSHOTS})`}\n </Typography>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap' }}>\n {screenshots.map((url, index) => (\n <Box key={url} sx={{ position: 'relative' }}>\n <Box\n component=\"img\"\n src={url}\n alt={`Screenshot ${index + 1}`}\n sx={{\n height: 72,\n borderRadius: 1,\n border: 1,\n borderColor: 'divider',\n }}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n setScreenshots((current) =>\n current.filter((entry) => entry !== url),\n )\n }\n >\n {'Remove'}\n </Button>\n </Box>\n ))}\n </Stack>\n {screenshots.length < MAX_SCREENSHOTS ? (\n <Box>\n <Button size=\"small\" onClick={() => onPickMedia('screenshot')}>\n {'Add from library'}\n </Button>\n </Box>\n ) : null}\n </Stack>\n\n <Divider />\n\n {/* Logo + metadata. */}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'flex-start' }}>\n <TextField\n label=\"Logo URL (https)\"\n value={values.logoUrl}\n onChange={set('logoUrl')}\n size=\"small\"\n sx={{ flex: 1 }}\n helperText={LISTING_LOGO_HINT}\n />\n <Button\n size=\"small\"\n onClick={() => onPickMedia('logo')}\n sx={{ mt: 0.5 }}\n >\n {'Library'}\n </Button>\n </Stack>\n <TextField\n label=\"Homepage (https)\"\n value={values.homepageUrl}\n onChange={set('homepageUrl')}\n size=\"small\"\n fullWidth\n />\n <TextField\n label=\"Repository (https)\"\n value={values.repositoryUrl}\n onChange={set('repositoryUrl')}\n size=\"small\"\n fullWidth\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"License\"\n value={values.license}\n onChange={set('license')}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n <TextField\n select\n label=\"Category\"\n value={values.category}\n onChange={set('category')}\n size=\"small\"\n sx={{ flex: 1 }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {categoryOptions.map((entry) => (\n <MenuItem key={entry} value={entry}>\n {entry}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n\n {/* Directly above Save listing (AGL-4105), which waits for it. */}\n <PublisherAgreementCheckbox\n checked={agreement.agreed}\n onChange={agreement.setAgreed}\n disabled={busy}\n />\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={busy || !values.displayName.trim() || !agreement.agreed}\n onClick={() => void save()}\n >\n {busy ? 'Saving…' : 'Save listing'}\n </Button>\n <Button disabled={busy} onClick={onDone}>\n {'Cancel'}\n </Button>\n </Stack>\n </Stack>\n\n </CardDisplay>\n )\n}\n\nListingDetailEditor.displayName = 'ListingDetailEditor'\n\nexport default ListingDetailEditor\n"],"names":["CardDisplay","useSnackbar","resolveMediaSrc","inheritedMediaAlt","authorizedFetch","Box","Button","Divider","MenuItem","Stack","TextField","Typography","useEffect","useRef","useState","LISTING_LOGO_HINT","formatMediaRef","pluginDocsHelp","useMediaPicker","MarkdownField","PublisherAgreementCheckbox","usePublisherAgreementConsent","LISTING_CATEGORIES","listingArtifactType","siteStarterCategoryOptions","SITE_STARTER_ARTIFACT_TYPE","MAX_SCREENSHOTS","ListingDetailEditor","props","orgId","listingId","listing","user","onDone","enqueueSnackbar","editorRef","busy","setBusy","agreement","values","setValues","displayName","description","previewImageUrl","logoUrl","homepageUrl","repositoryUrl","license","category","readme","setReadme","screenshots","setScreenshots","originalPreview","isStarter","categoryOptions","String","trim","categories","Array","isArray","current","$id","set","key","event","target","value","pickMedia","onPickMedia","media","kind","url","c","src","mediaScope","mediaId","insertImage","assetAlt","alt","length","includes","save","consent","headers","preview","method","body","JSON","stringify","ok","refused","json","catch","error","variant","allowDuplicate","response","action","payload","persist","reset","console","header","help","anchor","contentGutterX","contentGutterY","spacing","color","label","onChange","size","fullWidth","helperText","multiline","minRows","component","sx","width","maxHeight","objectFit","borderRadius","border","borderColor","direction","onClick","onPickImageFromMedia","handle","useFlexGap","flexWrap","map","index","position","height","filter","entry","alignItems","flex","mt","select","checked","agreed","setAgreed","disabled"],"mappings":"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"}
|
|
@@ -31,6 +31,7 @@ import { PLUGIN_REJECTION_CATEGORIES, pluginRejectionCategory, rejectionHeadline
|
|
|
31
31
|
import { PUBLISHER_ATTESTATION } from "../model/publisher-attestation.js";
|
|
32
32
|
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
33
33
|
import { reviewStatusMeaning } from "../model/plugin-review-status.js";
|
|
34
|
+
import SiteStarterReviewBody from "./site-starter-review-detail.component.js";
|
|
34
35
|
/** Verifier findings read as a wall of text otherwise; group by severity. */ const SEVERITY_ORDER = [
|
|
35
36
|
'error',
|
|
36
37
|
'warn',
|
|
@@ -66,7 +67,11 @@ import { reviewStatusMeaning } from "../model/plugin-review-status.js";
|
|
|
66
67
|
const listingId = String((_ref1 = params == null ? void 0 : params.listingId) != null ? _ref1 : '');
|
|
67
68
|
const { data: user } = useUser();
|
|
68
69
|
const { enqueueSnackbar } = useSnackbar();
|
|
69
|
-
const [
|
|
70
|
+
const [loadedDetail, setDetail] = useState(null);
|
|
71
|
+
// A site starter is read by its own page (AGL-4049); everything below the
|
|
72
|
+
// branch in the render is about a plugin's code.
|
|
73
|
+
const starter = (loadedDetail == null ? void 0 : loadedDetail.artifactType) === 'siteStarter' ? loadedDetail : null;
|
|
74
|
+
const detail = starter ? null : loadedDetail;
|
|
70
75
|
// Rendered by default (AGL-989) — the reviewer's first question is what a
|
|
71
76
|
// buyer sees; Source is for auditing exactly what was submitted.
|
|
72
77
|
const [readmeView, setReadmeView] = useState('rendered');
|
|
@@ -373,9 +378,12 @@ import { reviewStatusMeaning } from "../model/plugin-review-status.js";
|
|
|
373
378
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
374
379
|
children: [
|
|
375
380
|
/*#__PURE__*/ _jsx(PageHeaderRecord, {
|
|
376
|
-
title: (_ref7 =
|
|
381
|
+
title: (_ref7 = loadedDetail == null ? void 0 : loadedDetail.displayName) != null ? _ref7 : ''
|
|
377
382
|
}),
|
|
378
|
-
/*#__PURE__*/ _jsx(PageHeaderHelp, {
|
|
383
|
+
starter ? /*#__PURE__*/ _jsx(PageHeaderHelp, {
|
|
384
|
+
topic: "staffConsole",
|
|
385
|
+
anchor: "#plugin-reviews-site-starters"
|
|
386
|
+
}) : /*#__PURE__*/ _jsx(PageHeaderHelp, {
|
|
379
387
|
topic: "publisherHandbook",
|
|
380
388
|
anchor: "#review-what-happens-after-you-publish"
|
|
381
389
|
}),
|
|
@@ -391,6 +399,12 @@ import { reviewStatusMeaning } from "../model/plugin-review-status.js";
|
|
|
391
399
|
height: 220
|
|
392
400
|
})
|
|
393
401
|
]
|
|
402
|
+
}) : starter ? /*#__PURE__*/ _jsx(SiteStarterReviewBody, {
|
|
403
|
+
detail: starter,
|
|
404
|
+
busy: busy,
|
|
405
|
+
post: post,
|
|
406
|
+
onSelectVersion: setSelectedVersion,
|
|
407
|
+
staffPaths: staffPaths
|
|
394
408
|
}) : !detail ? /*#__PURE__*/ _jsx(Alert, {
|
|
395
409
|
severity: "warning",
|
|
396
410
|
children: 'That listing no longer exists.'
|