@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.
- package/package.json +15 -11
- package/src/lib/components/edit-listing-dialog.component.d.ts +20 -0
- package/src/lib/components/edit-listing-dialog.component.js +51 -0
- package/src/lib/components/edit-listing-dialog.component.js.map +1 -0
- package/src/lib/components/listing-detail-editor.component.d.ts +24 -0
- package/src/lib/components/listing-detail-editor.component.js +457 -0
- package/src/lib/components/listing-detail-editor.component.js.map +1 -0
- package/src/lib/components/listing-detail-page.component.d.ts +28 -0
- package/src/lib/components/listing-detail-page.component.js +151 -0
- package/src/lib/components/listing-detail-page.component.js.map +1 -0
- package/src/lib/components/listing-review-status.component.d.ts +67 -0
- package/src/lib/components/listing-review-status.component.js +301 -0
- package/src/lib/components/listing-review-status.component.js.map +1 -0
- package/src/lib/components/listing-verification-request.component.d.ts +40 -0
- package/src/lib/components/listing-verification-request.component.js +170 -0
- package/src/lib/components/listing-verification-request.component.js.map +1 -0
- package/src/lib/components/marketplace-hub.component.d.ts +43 -0
- package/src/lib/components/marketplace-hub.component.js +244 -0
- package/src/lib/components/marketplace-hub.component.js.map +1 -0
- package/src/lib/components/marketplace-reports-page.component.d.ts +23 -0
- package/src/lib/components/marketplace-reports-page.component.js +324 -0
- package/src/lib/components/marketplace-reports-page.component.js.map +1 -0
- package/src/lib/components/org-licences-panel.component.d.ts +73 -0
- package/src/lib/components/org-licences-panel.component.js +318 -0
- package/src/lib/components/org-licences-panel.component.js.map +1 -0
- package/src/lib/components/org-publish-panel.component.d.ts +45 -0
- package/src/lib/components/org-publish-panel.component.js +402 -0
- package/src/lib/components/org-publish-panel.component.js.map +1 -0
- package/src/lib/components/org-seller-panel.component.d.ts +28 -0
- package/src/lib/components/org-seller-panel.component.js +956 -0
- package/src/lib/components/org-seller-panel.component.js.map +1 -0
- package/src/lib/components/publish-artifact-dialog.component.d.ts +21 -0
- package/src/lib/components/publish-artifact-dialog.component.js +327 -0
- package/src/lib/components/publish-artifact-dialog.component.js.map +1 -0
- package/src/lib/components/publish-plugin-form.component.d.ts +61 -0
- package/src/lib/components/publish-plugin-form.component.js +857 -0
- package/src/lib/components/publish-plugin-form.component.js.map +1 -0
- package/src/lib/components/publish-plugin-page.component.d.ts +29 -0
- package/src/lib/components/publish-plugin-page.component.js +92 -0
- package/src/lib/components/publish-plugin-page.component.js.map +1 -0
- package/src/lib/components/publisher-profile-page.component.d.ts +13 -0
- package/src/lib/components/publisher-profile-page.component.js +261 -0
- package/src/lib/components/publisher-profile-page.component.js.map +1 -0
- package/src/lib/constants/listing-media.d.ts +28 -0
- package/src/lib/constants/listing-media.js +28 -0
- package/src/lib/constants/listing-media.js.map +1 -0
- package/src/lib/model/listing-social-card.d.ts +94 -0
- package/src/lib/model/listing-social-card.js +177 -0
- package/src/lib/model/listing-social-card.js.map +1 -0
- package/src/lib/model/marketplace-paths.d.ts +44 -0
- package/src/lib/model/marketplace-paths.js +48 -0
- package/src/lib/model/marketplace-paths.js.map +1 -0
- package/src/lib/model/payout-readiness.d.ts +81 -0
- package/src/lib/model/payout-readiness.js +52 -0
- package/src/lib/model/payout-readiness.js.map +1 -0
- package/src/lib/model/seller-ledger-totals.d.ts +100 -0
- package/src/lib/model/seller-ledger-totals.js +96 -0
- package/src/lib/model/seller-ledger-totals.js.map +1 -0
- package/src/lib/plugin.js +155 -40
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/admin-reports.d.ts +18 -0
- package/src/lib/server/admin-reports.js +280 -0
- package/src/lib/server/admin-reports.js.map +1 -0
- package/src/lib/server/listing-social-card.server.d.ts +24 -0
- package/src/lib/server/listing-social-card.server.js +65 -0
- package/src/lib/server/listing-social-card.server.js.map +1 -0
- package/src/lib/server.js +41 -0
- 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.
|
|
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.
|
|
29
|
-
"@aglyn/
|
|
30
|
-
"@aglyn/shared-
|
|
31
|
-
"@aglyn/shared-
|
|
32
|
-
"@aglyn/shared-ui-
|
|
33
|
-
"@aglyn/shared-
|
|
34
|
-
"@aglyn/shared-
|
|
35
|
-
"@aglyn/
|
|
36
|
-
"@aglyn/
|
|
37
|
-
"@aglyn/
|
|
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
|