@aglyn/plugins-marketplace 1.0.0-beta.147 → 1.0.0-beta.150

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.
Files changed (68) hide show
  1. package/package.json +15 -11
  2. package/src/lib/components/edit-listing-dialog.component.d.ts +20 -0
  3. package/src/lib/components/edit-listing-dialog.component.js +51 -0
  4. package/src/lib/components/edit-listing-dialog.component.js.map +1 -0
  5. package/src/lib/components/listing-detail-editor.component.d.ts +24 -0
  6. package/src/lib/components/listing-detail-editor.component.js +457 -0
  7. package/src/lib/components/listing-detail-editor.component.js.map +1 -0
  8. package/src/lib/components/listing-detail-page.component.d.ts +28 -0
  9. package/src/lib/components/listing-detail-page.component.js +151 -0
  10. package/src/lib/components/listing-detail-page.component.js.map +1 -0
  11. package/src/lib/components/listing-review-status.component.d.ts +67 -0
  12. package/src/lib/components/listing-review-status.component.js +301 -0
  13. package/src/lib/components/listing-review-status.component.js.map +1 -0
  14. package/src/lib/components/listing-verification-request.component.d.ts +40 -0
  15. package/src/lib/components/listing-verification-request.component.js +170 -0
  16. package/src/lib/components/listing-verification-request.component.js.map +1 -0
  17. package/src/lib/components/marketplace-hub.component.d.ts +43 -0
  18. package/src/lib/components/marketplace-hub.component.js +244 -0
  19. package/src/lib/components/marketplace-hub.component.js.map +1 -0
  20. package/src/lib/components/marketplace-reports-page.component.d.ts +23 -0
  21. package/src/lib/components/marketplace-reports-page.component.js +324 -0
  22. package/src/lib/components/marketplace-reports-page.component.js.map +1 -0
  23. package/src/lib/components/org-licences-panel.component.d.ts +73 -0
  24. package/src/lib/components/org-licences-panel.component.js +318 -0
  25. package/src/lib/components/org-licences-panel.component.js.map +1 -0
  26. package/src/lib/components/org-publish-panel.component.d.ts +45 -0
  27. package/src/lib/components/org-publish-panel.component.js +402 -0
  28. package/src/lib/components/org-publish-panel.component.js.map +1 -0
  29. package/src/lib/components/org-seller-panel.component.d.ts +28 -0
  30. package/src/lib/components/org-seller-panel.component.js +956 -0
  31. package/src/lib/components/org-seller-panel.component.js.map +1 -0
  32. package/src/lib/components/publish-artifact-dialog.component.d.ts +21 -0
  33. package/src/lib/components/publish-artifact-dialog.component.js +327 -0
  34. package/src/lib/components/publish-artifact-dialog.component.js.map +1 -0
  35. package/src/lib/components/publish-plugin-form.component.d.ts +61 -0
  36. package/src/lib/components/publish-plugin-form.component.js +857 -0
  37. package/src/lib/components/publish-plugin-form.component.js.map +1 -0
  38. package/src/lib/components/publish-plugin-page.component.d.ts +29 -0
  39. package/src/lib/components/publish-plugin-page.component.js +92 -0
  40. package/src/lib/components/publish-plugin-page.component.js.map +1 -0
  41. package/src/lib/components/publisher-profile-page.component.d.ts +13 -0
  42. package/src/lib/components/publisher-profile-page.component.js +261 -0
  43. package/src/lib/components/publisher-profile-page.component.js.map +1 -0
  44. package/src/lib/constants/listing-media.d.ts +28 -0
  45. package/src/lib/constants/listing-media.js +28 -0
  46. package/src/lib/constants/listing-media.js.map +1 -0
  47. package/src/lib/model/listing-social-card.d.ts +94 -0
  48. package/src/lib/model/listing-social-card.js +177 -0
  49. package/src/lib/model/listing-social-card.js.map +1 -0
  50. package/src/lib/model/marketplace-paths.d.ts +44 -0
  51. package/src/lib/model/marketplace-paths.js +48 -0
  52. package/src/lib/model/marketplace-paths.js.map +1 -0
  53. package/src/lib/model/payout-readiness.d.ts +81 -0
  54. package/src/lib/model/payout-readiness.js +52 -0
  55. package/src/lib/model/payout-readiness.js.map +1 -0
  56. package/src/lib/model/seller-ledger-totals.d.ts +100 -0
  57. package/src/lib/model/seller-ledger-totals.js +96 -0
  58. package/src/lib/model/seller-ledger-totals.js.map +1 -0
  59. package/src/lib/plugin.js +155 -40
  60. package/src/lib/plugin.js.map +1 -1
  61. package/src/lib/server/admin-reports.d.ts +18 -0
  62. package/src/lib/server/admin-reports.js +280 -0
  63. package/src/lib/server/admin-reports.js.map +1 -0
  64. package/src/lib/server/listing-social-card.server.d.ts +24 -0
  65. package/src/lib/server/listing-social-card.server.js +65 -0
  66. package/src/lib/server/listing-social-card.server.js.map +1 -0
  67. package/src/lib/server.js +41 -0
  68. 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.0.0-beta.147",
3
+ "version": "1.0.0-beta.150",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,20 +25,24 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.147",
29
- "@aglyn/shared-data-mdi": "1.0.0-beta.147",
30
- "@aglyn/shared-ui-jsx": "1.0.0-beta.147",
31
- "@aglyn/shared-ui-next": "1.0.0-beta.147",
32
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.147",
33
- "@aglyn/shared-util-email": "1.0.0-beta.147",
34
- "@aglyn/shared-util-http": "1.0.0-beta.147",
35
- "@aglyn/tenant-data-admin": "1.0.0-beta.147",
36
- "@aglyn/tenant-feature-instance": "1.0.0-beta.147",
37
- "@aglyn/tenant-runtime": "1.0.0-beta.147",
28
+ "@aglyn/aglyn": "1.0.0-beta.150",
29
+ "@aglyn/aglyn-markdown-editor": "1.0.0-beta.150",
30
+ "@aglyn/shared-data-enums": "1.0.0-beta.150",
31
+ "@aglyn/shared-data-mdi": "1.0.0-beta.150",
32
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.150",
33
+ "@aglyn/shared-ui-next": "1.0.0-beta.150",
34
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.150",
35
+ "@aglyn/shared-util-email": "1.0.0-beta.150",
36
+ "@aglyn/shared-util-http": "1.0.0-beta.150",
37
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.150",
38
+ "@aglyn/tenant-data-admin": "1.0.0-beta.150",
39
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.150",
40
+ "@aglyn/tenant-runtime": "1.0.0-beta.150",
38
41
  "@swc/helpers": "0.5.23"
39
42
  },
40
43
  "peerDependencies": {
41
44
  "@mui/material": "^9.3.1",
45
+ "@mui/x-data-grid": "^9.12.0",
42
46
  "firebase": "^12.18.0",
43
47
  "firebase-admin": "^14.3.0",
44
48
  "next": "16.3.3",
@@ -0,0 +1,20 @@
1
+ export interface EditListingDialogProps {
2
+ orgId: string;
3
+ /** The listing being edited (a `marketplaceListings` doc with `$id`). */
4
+ listing: Record<string, any> | null;
5
+ open: boolean;
6
+ onClose: () => void;
7
+ /** Firebase user, for the ID token on the update requests. */
8
+ user: unknown;
9
+ }
10
+ /**
11
+ * Listings-tab entry point to the listing editor (AGL-862/869). A thin dialog
12
+ * wrapper around the same full editor the detail page uses ({@link
13
+ * ListingDetailEditor}) — one implementation, so the WYSIWYG body, DAM image
14
+ * pickers, and field set are identical on both surfaces.
15
+ */
16
+ export declare function EditListingDialog(props: EditListingDialogProps): import("react").JSX.Element;
17
+ export declare namespace EditListingDialog {
18
+ var displayName: string;
19
+ }
20
+ export default EditListingDialog;
@@ -0,0 +1,51 @@
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 } from "react/jsx-runtime";
18
+ import { Box, Dialog } from "@mui/material";
19
+ import ListingDetailEditor from "./listing-detail-editor.component.js";
20
+ /**
21
+ * Listings-tab entry point to the listing editor (AGL-862/869). A thin dialog
22
+ * wrapper around the same full editor the detail page uses ({@link
23
+ * ListingDetailEditor}) — one implementation, so the WYSIWYG body, DAM image
24
+ * pickers, and field set are identical on both surfaces.
25
+ */ export function EditListingDialog(props) {
26
+ const { orgId, listing, open, onClose, user } = props;
27
+ return /*#__PURE__*/ _jsx(Dialog, {
28
+ open: open,
29
+ onClose: onClose,
30
+ maxWidth: "md",
31
+ fullWidth: true,
32
+ children: /*#__PURE__*/ _jsx(Box, {
33
+ sx: {
34
+ flex: 1,
35
+ minHeight: 0,
36
+ overflowY: 'auto'
37
+ },
38
+ children: listing ? /*#__PURE__*/ _jsx(ListingDetailEditor, {
39
+ orgId: orgId,
40
+ listingId: listing.$id,
41
+ listing: listing,
42
+ user: user,
43
+ onDone: onClose
44
+ }) : null
45
+ })
46
+ });
47
+ }
48
+ EditListingDialog.displayName = 'EditListingDialog';
49
+ export default EditListingDialog;
50
+
51
+ //# sourceMappingURL=edit-listing-dialog.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/edit-listing-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 { Box, Dialog } from '@mui/material'\nimport ListingDetailEditor from './listing-detail-editor.component'\n\nexport interface EditListingDialogProps {\n orgId: string\n /** The listing being edited (a `marketplaceListings` doc with `$id`). */\n listing: Record<string, any> | null\n open: boolean\n onClose: () => void\n /** Firebase user, for the ID token on the update requests. */\n user: unknown\n}\n\n/**\n * Listings-tab entry point to the listing editor (AGL-862/869). A thin dialog\n * wrapper around the same full editor the detail page uses ({@link\n * ListingDetailEditor}) — one implementation, so the WYSIWYG body, DAM image\n * pickers, and field set are identical on both surfaces.\n */\nexport function EditListingDialog(props: EditListingDialogProps) {\n const { orgId, listing, open, onClose, user } = props\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"md\" fullWidth>\n {/* The editor is a full form, taller than any viewport (AGL-987). The\n dialog paper caps at the screen, so without a scroller of its own\n everything below the body — including Save — was unreachable. */}\n {/* flex:1 + minHeight:0 inside the paper's column, or the child just\n grows past the capped paper instead of scrolling within it. */}\n <Box sx={{ flex: 1, minHeight: 0, overflowY: 'auto' }}>\n {listing ? (\n <ListingDetailEditor\n orgId={orgId}\n listingId={listing.$id}\n listing={listing}\n user={user}\n onDone={onClose}\n />\n ) : null}\n </Box>\n </Dialog>\n )\n}\n\nEditListingDialog.displayName = 'EditListingDialog'\n\nexport default EditListingDialog\n"],"names":["Box","Dialog","ListingDetailEditor","EditListingDialog","props","orgId","listing","open","onClose","user","maxWidth","fullWidth","sx","flex","minHeight","overflowY","listingId","$id","onDone","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,GAAG,EAAEC,MAAM,QAAQ,gBAAe;AAC3C,OAAOC,yBAAyB,uCAAmC;AAYnE;;;;;CAKC,GACD,OAAO,SAASC,kBAAkBC,KAA6B;IAC7D,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAEC,IAAI,EAAEC,OAAO,EAAEC,IAAI,EAAE,GAAGL;IAChD,qBACE,KAACH;QAAOM,MAAMA;QAAMC,SAASA;QAASE,UAAS;QAAKC,SAAS;kBAM3D,cAAA,KAACX;YAAIY,IAAI;gBAAEC,MAAM;gBAAGC,WAAW;gBAAGC,WAAW;YAAO;sBACjDT,wBACC,KAACJ;gBACCG,OAAOA;gBACPW,WAAWV,QAAQW,GAAG;gBACtBX,SAASA;gBACTG,MAAMA;gBACNS,QAAQV;iBAER;;;AAIZ;AAEAL,kBAAkBgB,WAAW,GAAG;AAEhC,eAAehB,kBAAiB"}
@@ -0,0 +1,24 @@
1
+ export interface ListingDetailEditorProps {
2
+ orgId: string;
3
+ listingId: string;
4
+ listing: Record<string, any>;
5
+ /** Firebase user, for the ID token on the update requests. */
6
+ user: unknown;
7
+ /** Leave edit mode (Cancel or after a successful save). */
8
+ onDone: () => void;
9
+ }
10
+ /**
11
+ * Full-page listing editor (AGL-869): the owner edits the whole detail page,
12
+ * not a cramped sidebar card. The body uses the same WYSIWYG markdown editor
13
+ * as blog content (`MarkdownVisualEditor`), and every image — preview, logo,
14
+ * screenshots, and inline body images — picks from the shared DAM.
15
+ *
16
+ * Content saves through the marketplace plugin's HTTP API (`update-listing`);
17
+ * the preview image goes through its dedicated endpoint since it isn't a
18
+ * content field. Both are merges, so an unchanged field is left untouched.
19
+ */
20
+ export declare function ListingDetailEditor(props: ListingDetailEditorProps): import("react").JSX.Element;
21
+ export declare namespace ListingDetailEditor {
22
+ var displayName: string;
23
+ }
24
+ export default ListingDetailEditor;
@@ -0,0 +1,457 @@
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 { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ import { CardDisplay } from "@aglyn/shared-ui-jsx";
20
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
21
+ // The stored preview may be a `media:` reference (AGL-1424), which is not a
22
+ // URL. Resolved where it is DISPLAYED, never in `values`.
23
+ import { resolveMediaSrc } from "@aglyn/aglyn/app-utils/media-ref";
24
+ import { inheritedMediaAlt } from "@aglyn/aglyn/app-utils/media-metadata";
25
+ import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
26
+ import { Box, Button, Divider, MenuItem, Stack, TextField, Typography } from "@mui/material";
27
+ import { useEffect, useRef, useState } from "react";
28
+ import { LISTING_LOGO_HINT } from "../constants/listing-media.js";
29
+ import { formatMediaRef, pluginDocsHelp, useMediaPicker } from "@aglyn/aglyn";
30
+ import { MarkdownField } from "@aglyn/aglyn-markdown-editor";
31
+ // Mirrors the server's fixed taxonomy (marketplace plugin model), which the
32
+ // console can't import across the aglyn:addons boundary. The server
33
+ // re-validates, so drift fails the save rather than corrupting a listing.
34
+ const LISTING_CATEGORIES = [
35
+ 'analytics',
36
+ 'automation',
37
+ 'commerce',
38
+ 'communication',
39
+ 'content',
40
+ 'design',
41
+ 'forms',
42
+ 'integrations',
43
+ 'marketing',
44
+ 'productivity',
45
+ 'seo',
46
+ 'security'
47
+ ];
48
+ const MAX_SCREENSHOTS = 6;
49
+ /**
50
+ * Full-page listing editor (AGL-869): the owner edits the whole detail page,
51
+ * not a cramped sidebar card. The body uses the same WYSIWYG markdown editor
52
+ * as blog content (`MarkdownVisualEditor`), and every image — preview, logo,
53
+ * screenshots, and inline body images — picks from the shared DAM.
54
+ *
55
+ * Content saves through the marketplace plugin's HTTP API (`update-listing`);
56
+ * the preview image goes through its dedicated endpoint since it isn't a
57
+ * content field. Both are merges, so an unchanged field is left untouched.
58
+ */ export function ListingDetailEditor(props) {
59
+ const { orgId, listingId, listing, user, onDone } = props;
60
+ const { enqueueSnackbar } = useSnackbar();
61
+ const editorRef = useRef(null);
62
+ const [busy, setBusy] = useState(false);
63
+ const [values, setValues] = useState({
64
+ displayName: '',
65
+ description: '',
66
+ previewImageUrl: '',
67
+ logoUrl: '',
68
+ homepageUrl: '',
69
+ repositoryUrl: '',
70
+ license: '',
71
+ category: ''
72
+ });
73
+ const [readme, setReadme] = useState('');
74
+ const [screenshots, setScreenshots] = useState([]);
75
+ const originalPreview = useRef('');
76
+ useEffect(()=>{
77
+ var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10;
78
+ var _listing_categories;
79
+ setValues({
80
+ displayName: (_ref = listing == null ? void 0 : listing.displayName) != null ? _ref : '',
81
+ description: (_ref1 = listing == null ? void 0 : listing.description) != null ? _ref1 : '',
82
+ previewImageUrl: (_ref2 = listing == null ? void 0 : listing.previewImageUrl) != null ? _ref2 : '',
83
+ logoUrl: (_ref3 = listing == null ? void 0 : listing.logoUrl) != null ? _ref3 : '',
84
+ homepageUrl: (_ref4 = listing == null ? void 0 : listing.homepageUrl) != null ? _ref4 : '',
85
+ repositoryUrl: (_ref5 = listing == null ? void 0 : listing.repositoryUrl) != null ? _ref5 : '',
86
+ license: (_ref6 = listing == null ? void 0 : listing.license) != null ? _ref6 : '',
87
+ category: (_ref7 = (_ref8 = listing == null ? void 0 : (_listing_categories = listing.categories) == null ? void 0 : _listing_categories[0]) != null ? _ref8 : listing == null ? void 0 : listing.category) != null ? _ref7 : ''
88
+ });
89
+ setReadme((_ref9 = listing == null ? void 0 : listing.readme) != null ? _ref9 : '');
90
+ setScreenshots(Array.isArray(listing == null ? void 0 : listing.screenshots) ? listing.screenshots : []);
91
+ originalPreview.current = (_ref10 = listing == null ? void 0 : listing.previewImageUrl) != null ? _ref10 : '';
92
+ }, [
93
+ listing == null ? void 0 : listing.$id
94
+ ]);
95
+ const set = (key)=>(event)=>setValues((current)=>_extends({}, current, {
96
+ [key]: event.target.value
97
+ }));
98
+ /*
99
+ * The console's media library, opened through the picker context rather
100
+ * than by mounting its dialog (AGL-3080). The library is coupled to the
101
+ * org and the session, which a plugin has no way to reach, and the console
102
+ * provides `pickMedia` around every plugin page for exactly this — so the
103
+ * `pickTarget` state that only existed to keep one dialog open for four
104
+ * buttons goes with it: the target is now the argument.
105
+ *
106
+ * The URL comes back already resolved (CDN path over the raw download URL,
107
+ * AGL-1215), which is what `mediaSrc` was doing here.
108
+ */ const { pickMedia } = useMediaPicker();
109
+ const onPickMedia = async (target)=>{
110
+ const media = await (pickMedia == null ? void 0 : pickMedia({
111
+ kind: 'image'
112
+ }));
113
+ if (!media) return;
114
+ const url = media.url;
115
+ if (!url) return;
116
+ if (target === 'preview') setValues((c)=>_extends({}, c, {
117
+ previewImageUrl: url
118
+ }));
119
+ else if (target === 'logo') setValues((c)=>_extends({}, c, {
120
+ logoUrl: url
121
+ }));
122
+ else if (target === 'body') {
123
+ var _formatMediaRef, _inheritedMediaAlt;
124
+ var // The alt was a hardcoded '' (AGL-1896). This surface offers no alt
125
+ // prompt at all, so the asset's own alt was the only description that
126
+ // could ever have reached the row — and a markdown-lite image row's
127
+ // alt cannot be edited after insertion, so '' was permanent. Still ''
128
+ // for an asset nobody has described; never a fabricated one.
129
+ _editorRef_current;
130
+ // The README BODY takes a reference; the three image FIELDS above keep
131
+ // `mediaSrc` (AGL-1705). Not an inconsistency — a field is one string
132
+ // with known readers and its own AGL-1701 validator, while the body is
133
+ // markdown that AGL-1686 taught every renderer to resolve.
134
+ //
135
+ // The absolute form was justified by `mediaSrc`'s own docstring — it
136
+ // prefixes the origin because the markdown "renders elsewhere". That
137
+ // was checked rather than assumed, and it is not true. The renderer
138
+ // `MarketplaceListingContent` is registered through
139
+ // `registerConsoleExtension`; its `marketplaceListing` slot has exactly
140
+ // one render site, the console's `/[orgSlug]/marketplace/[listingId]`
141
+ // route; and `apps/tenant` never imports the marketplace plugin at all.
142
+ // The body reaches no other origin by another door either — the social
143
+ // card builds `og:image` from `previewImageUrl`/`logoUrl`, never from
144
+ // the README.
145
+ //
146
+ // So a site-relative resolve lands on the console origin, which serves
147
+ // `/api/media/cdn/[...path]` — the very route the absolute URL pointed
148
+ // at. Same bytes over the same route, minus a baked-in origin and a
149
+ // baked-in route shape that cannot be changed without a migration.
150
+ const src = (_formatMediaRef = formatMediaRef(media.mediaScope, media.mediaId)) != null ? _formatMediaRef : url;
151
+ (_editorRef_current = editorRef.current) == null ? void 0 : _editorRef_current.insertImage((_inheritedMediaAlt = inheritedMediaAlt({
152
+ assetAlt: media.alt
153
+ })) != null ? _inheritedMediaAlt : '', src);
154
+ } else if (target === 'screenshot') setScreenshots((current)=>current.length >= MAX_SCREENSHOTS || current.includes(url) ? current : [
155
+ ...current,
156
+ url
157
+ ]);
158
+ };
159
+ const save = async ()=>{
160
+ setBusy(true);
161
+ try {
162
+ const headers = {
163
+ 'Content-Type': 'application/json'
164
+ };
165
+ // Preview image isn't a content field, so it has its own endpoint
166
+ // (AGL-863). Only touch it when it actually changed.
167
+ if (values.previewImageUrl !== originalPreview.current) {
168
+ await authorizedFetch(user, '/api/marketplace/preview-image', {
169
+ method: values.previewImageUrl ? 'POST' : 'DELETE',
170
+ headers,
171
+ body: JSON.stringify(_extends({
172
+ listingId
173
+ }, values.previewImageUrl ? {
174
+ url: values.previewImageUrl
175
+ } : {}))
176
+ });
177
+ }
178
+ const response = await authorizedFetch(user, '/api/marketplace/publish-plugin', {
179
+ method: 'POST',
180
+ headers,
181
+ body: JSON.stringify({
182
+ action: 'update-listing',
183
+ listingId,
184
+ displayName: values.displayName,
185
+ description: values.description,
186
+ readme,
187
+ logoUrl: values.logoUrl,
188
+ homepageUrl: values.homepageUrl,
189
+ repositoryUrl: values.repositoryUrl,
190
+ license: values.license,
191
+ categories: values.category ? [
192
+ values.category
193
+ ] : [],
194
+ screenshots
195
+ })
196
+ });
197
+ const payload = await response.json().catch(()=>({}));
198
+ if (!response.ok) {
199
+ var _ref;
200
+ return void enqueueSnackbar((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'Update failed', {
201
+ variant: 'error',
202
+ allowDuplicate: true
203
+ });
204
+ }
205
+ enqueueSnackbar('Listing updated', {
206
+ variant: 'success',
207
+ persist: false
208
+ });
209
+ onDone();
210
+ } catch (error) {
211
+ console.error(error);
212
+ enqueueSnackbar('An error has occurred', {
213
+ variant: 'error',
214
+ allowDuplicate: true
215
+ });
216
+ } finally{
217
+ setBusy(false);
218
+ }
219
+ };
220
+ return /*#__PURE__*/ _jsx(CardDisplay, {
221
+ header: 'Edit listing',
222
+ help: pluginDocsHelp('publisherHandbook', {
223
+ anchor: '#authoring-your-listing',
224
+ excerpt: 'What buyers see on the public listing page — name, summary, ' + 'rich-text body, and media.'
225
+ }),
226
+ contentGutterX: true,
227
+ contentGutterY: true,
228
+ children: /*#__PURE__*/ _jsxs(Stack, {
229
+ spacing: 2,
230
+ children: [
231
+ /*#__PURE__*/ _jsx(Typography, {
232
+ variant: "body2",
233
+ color: "text.secondary",
234
+ children: 'Everything here shows on the public listing page. The body is ' + 'rich text — use the toolbar, and add images from your media ' + 'library.'
235
+ }),
236
+ /*#__PURE__*/ _jsx(TextField, {
237
+ label: "Listing name",
238
+ value: values.displayName,
239
+ onChange: set('displayName'),
240
+ size: "small",
241
+ fullWidth: true
242
+ }),
243
+ /*#__PURE__*/ _jsx(TextField, {
244
+ label: "Description",
245
+ helperText: "One line, shown on every browse card",
246
+ value: values.description,
247
+ onChange: set('description'),
248
+ size: "small",
249
+ multiline: true,
250
+ minRows: 2,
251
+ fullWidth: true
252
+ }),
253
+ /*#__PURE__*/ _jsxs(Stack, {
254
+ spacing: 1,
255
+ children: [
256
+ /*#__PURE__*/ _jsx(Typography, {
257
+ variant: "subtitle2",
258
+ children: 'Preview image'
259
+ }),
260
+ resolveMediaSrc(values.previewImageUrl) ? /*#__PURE__*/ _jsx(Box, {
261
+ component: "img",
262
+ src: resolveMediaSrc(values.previewImageUrl),
263
+ alt: "Preview",
264
+ sx: {
265
+ width: '100%',
266
+ maxHeight: 220,
267
+ objectFit: 'cover',
268
+ borderRadius: 1,
269
+ border: 1,
270
+ borderColor: 'divider'
271
+ }
272
+ }) : null,
273
+ /*#__PURE__*/ _jsxs(Stack, {
274
+ direction: "row",
275
+ spacing: 1,
276
+ children: [
277
+ /*#__PURE__*/ _jsx(Button, {
278
+ size: "small",
279
+ onClick: ()=>onPickMedia('preview'),
280
+ children: values.previewImageUrl ? 'Replace' : 'Choose from library'
281
+ }),
282
+ values.previewImageUrl ? /*#__PURE__*/ _jsx(Button, {
283
+ size: "small",
284
+ color: "error",
285
+ onClick: ()=>setValues((c)=>_extends({}, c, {
286
+ previewImageUrl: ''
287
+ })),
288
+ children: 'Remove'
289
+ }) : null
290
+ ]
291
+ })
292
+ ]
293
+ }),
294
+ /*#__PURE__*/ _jsx(Divider, {}),
295
+ /*#__PURE__*/ _jsx(MarkdownField, {
296
+ label: "About",
297
+ value: readme,
298
+ onChange: setReadme,
299
+ onPickImageFromMedia: ()=>onPickMedia('body'),
300
+ editorRef: (handle)=>{
301
+ editorRef.current = handle;
302
+ }
303
+ }),
304
+ /*#__PURE__*/ _jsx(Divider, {}),
305
+ /*#__PURE__*/ _jsxs(Stack, {
306
+ spacing: 1,
307
+ children: [
308
+ /*#__PURE__*/ _jsx(Typography, {
309
+ variant: "subtitle2",
310
+ children: `Screenshots (${screenshots.length}/${MAX_SCREENSHOTS})`
311
+ }),
312
+ /*#__PURE__*/ _jsx(Stack, {
313
+ direction: "row",
314
+ spacing: 1,
315
+ sx: {
316
+ flexWrap: 'wrap'
317
+ },
318
+ children: screenshots.map((url, index)=>/*#__PURE__*/ _jsxs(Box, {
319
+ sx: {
320
+ position: 'relative'
321
+ },
322
+ children: [
323
+ /*#__PURE__*/ _jsx(Box, {
324
+ component: "img",
325
+ src: url,
326
+ alt: `Screenshot ${index + 1}`,
327
+ sx: {
328
+ height: 72,
329
+ borderRadius: 1,
330
+ border: 1,
331
+ borderColor: 'divider'
332
+ }
333
+ }),
334
+ /*#__PURE__*/ _jsx(Button, {
335
+ size: "small",
336
+ color: "error",
337
+ onClick: ()=>setScreenshots((current)=>current.filter((entry)=>entry !== url)),
338
+ children: 'Remove'
339
+ })
340
+ ]
341
+ }, url))
342
+ }),
343
+ screenshots.length < MAX_SCREENSHOTS ? /*#__PURE__*/ _jsx(Box, {
344
+ children: /*#__PURE__*/ _jsx(Button, {
345
+ size: "small",
346
+ onClick: ()=>onPickMedia('screenshot'),
347
+ children: 'Add from library'
348
+ })
349
+ }) : null
350
+ ]
351
+ }),
352
+ /*#__PURE__*/ _jsx(Divider, {}),
353
+ /*#__PURE__*/ _jsxs(Stack, {
354
+ direction: "row",
355
+ spacing: 1,
356
+ sx: {
357
+ alignItems: 'flex-start'
358
+ },
359
+ children: [
360
+ /*#__PURE__*/ _jsx(TextField, {
361
+ label: "Logo URL (https)",
362
+ value: values.logoUrl,
363
+ onChange: set('logoUrl'),
364
+ size: "small",
365
+ sx: {
366
+ flex: 1
367
+ },
368
+ helperText: LISTING_LOGO_HINT
369
+ }),
370
+ /*#__PURE__*/ _jsx(Button, {
371
+ size: "small",
372
+ onClick: ()=>onPickMedia('logo'),
373
+ sx: {
374
+ mt: 0.5
375
+ },
376
+ children: 'Library'
377
+ })
378
+ ]
379
+ }),
380
+ /*#__PURE__*/ _jsx(TextField, {
381
+ label: "Homepage (https)",
382
+ value: values.homepageUrl,
383
+ onChange: set('homepageUrl'),
384
+ size: "small",
385
+ fullWidth: true
386
+ }),
387
+ /*#__PURE__*/ _jsx(TextField, {
388
+ label: "Repository (https)",
389
+ value: values.repositoryUrl,
390
+ onChange: set('repositoryUrl'),
391
+ size: "small",
392
+ fullWidth: true
393
+ }),
394
+ /*#__PURE__*/ _jsxs(Stack, {
395
+ direction: "row",
396
+ spacing: 1,
397
+ children: [
398
+ /*#__PURE__*/ _jsx(TextField, {
399
+ label: "License",
400
+ value: values.license,
401
+ onChange: set('license'),
402
+ size: "small",
403
+ sx: {
404
+ flex: 1
405
+ }
406
+ }),
407
+ /*#__PURE__*/ _jsxs(TextField, {
408
+ select: true,
409
+ label: "Category",
410
+ value: values.category,
411
+ onChange: set('category'),
412
+ size: "small",
413
+ sx: {
414
+ flex: 1
415
+ },
416
+ children: [
417
+ /*#__PURE__*/ _jsx(MenuItem, {
418
+ value: "",
419
+ children: 'None'
420
+ }),
421
+ LISTING_CATEGORIES.map((entry)=>/*#__PURE__*/ _jsx(MenuItem, {
422
+ value: entry,
423
+ children: entry
424
+ }, entry))
425
+ ]
426
+ })
427
+ ]
428
+ }),
429
+ /*#__PURE__*/ _jsxs(Stack, {
430
+ direction: "row",
431
+ spacing: 1,
432
+ sx: {
433
+ pt: 1
434
+ },
435
+ children: [
436
+ /*#__PURE__*/ _jsx(Button, {
437
+ variant: "contained",
438
+ color: "primary",
439
+ disabled: busy || !values.displayName.trim(),
440
+ onClick: ()=>void save(),
441
+ children: busy ? 'Saving…' : 'Save listing'
442
+ }),
443
+ /*#__PURE__*/ _jsx(Button, {
444
+ disabled: busy,
445
+ onClick: onDone,
446
+ children: 'Cancel'
447
+ })
448
+ ]
449
+ })
450
+ ]
451
+ })
452
+ });
453
+ }
454
+ ListingDetailEditor.displayName = 'ListingDetailEditor';
455
+ export default ListingDetailEditor;
456
+
457
+ //# sourceMappingURL=listing-detail-editor.component.js.map