@aglyn/plugins-marketplace 1.0.0-beta.198 → 1.0.0-beta.200
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/marketplace-browse.component.js +104 -169
- package/src/lib/components/marketplace-browse.component.js.map +1 -1
- package/src/lib/components/marketplace-reports-page.component.js +9 -0
- package/src/lib/components/marketplace-reports-page.component.js.map +1 -1
- package/src/lib/components/org-licences-panel.component.js +212 -170
- package/src/lib/components/org-licences-panel.component.js.map +1 -1
- package/src/lib/components/org-seller-panel.component.js +23 -5
- package/src/lib/components/org-seller-panel.component.js.map +1 -1
- package/src/lib/components/plugin-reviews-queue.component.d.ts +9 -0
- package/src/lib/components/plugin-reviews-queue.component.js +190 -114
- package/src/lib/components/plugin-reviews-queue.component.js.map +1 -1
- package/src/lib/hooks/use-listing-name-lookup.d.ts +34 -0
- package/src/lib/hooks/use-listing-name-lookup.js +131 -0
- package/src/lib/hooks/use-listing-name-lookup.js.map +1 -0
- package/src/lib/model/listing-query.d.ts +140 -0
- package/src/lib/model/listing-query.js +393 -0
- package/src/lib/model/listing-query.js.map +1 -0
- package/src/lib/model/marketplace.d.ts +11 -0
- package/src/lib/model/marketplace.js.map +1 -1
- package/src/lib/server/admin-reports.js +30 -12
- package/src/lib/server/admin-reports.js.map +1 -1
- package/src/lib/server/admin-reviews.js +180 -128
- package/src/lib/server/admin-reviews.js.map +1 -1
- package/src/lib/server/admin-trust.js +2 -1
- package/src/lib/server/admin-trust.js.map +1 -1
- package/src/lib/server/billing-webhook.js +13 -1
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/install-email-starter.js +9 -2
- package/src/lib/server/install-email-starter.js.map +1 -1
- package/src/lib/server/install-layout.js +17 -5
- package/src/lib/server/install-layout.js.map +1 -1
- package/src/lib/server/install-template.js +15 -5
- package/src/lib/server/install-template.js.map +1 -1
- package/src/lib/server/install.js +10 -2
- package/src/lib/server/install.js.map +1 -1
- package/src/lib/server/listing-query-fields.d.ts +38 -0
- package/src/lib/server/listing-query-fields.js +50 -0
- package/src/lib/server/listing-query-fields.js.map +1 -0
- package/src/lib/server/publish-dataset-schema.js +6 -2
- package/src/lib/server/publish-dataset-schema.js.map +1 -1
- package/src/lib/server/publish-email-starter.js +6 -2
- package/src/lib/server/publish-email-starter.js.map +1 -1
- package/src/lib/server/publish-email-template.js +6 -2
- package/src/lib/server/publish-email-template.js.map +1 -1
- package/src/lib/server/publish-layout.js +6 -2
- package/src/lib/server/publish-layout.js.map +1 -1
- package/src/lib/server/publish-plugin.js +75 -7
- package/src/lib/server/publish-plugin.js.map +1 -1
- package/src/lib/server/publish-template.js +6 -2
- package/src/lib/server/publish-template.js.map +1 -1
- package/src/lib/server/publish-theme.js +6 -2
- package/src/lib/server/publish-theme.js.map +1 -1
- package/src/lib/server/publish.js +6 -2
- package/src/lib/server/publish.js.map +1 -1
- package/src/lib/server/update-artifact.js +18 -4
- package/src/lib/server/update-artifact.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.200",
|
|
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.0.0-beta.
|
|
29
|
-
"@aglyn/aglyn-markdown-editor": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
40
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.200",
|
|
29
|
+
"@aglyn/aglyn-markdown-editor": "1.0.0-beta.200",
|
|
30
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.200",
|
|
31
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.200",
|
|
32
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.200",
|
|
33
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.200",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.200",
|
|
35
|
+
"@aglyn/shared-util-email": "1.0.0-beta.200",
|
|
36
|
+
"@aglyn/shared-util-http": "1.0.0-beta.200",
|
|
37
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.200",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.200",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.200",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.200",
|
|
41
41
|
"@swc/helpers": "0.5.23"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
@@ -19,19 +19,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
19
19
|
import { mdiCheckDecagram } from "@aglyn/shared-data-mdi";
|
|
20
20
|
import { AppLink, CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
|
|
22
|
-
import {
|
|
22
|
+
import ListQueryNotices, { listQueryRefusals } from "@aglyn/shared-ui-jsx/components/list-query-notices.component";
|
|
23
|
+
import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filter";
|
|
23
24
|
import { Alert, Chip, Tooltip, Grid, MenuItem, Rating, Stack, TextField, Typography } from "@mui/material";
|
|
24
25
|
import { collection, doc, documentId, getDoc, limit, orderBy, query, where } from "firebase/firestore";
|
|
26
|
+
import { useListQuery } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
|
|
25
27
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
26
28
|
import { buildRoute, PLATFORM_BRAND_NAME, pluginDocsHelp, Route } from "@aglyn/aglyn";
|
|
27
|
-
import { useConsoleHostRoute, useFirestore, useFirestoreCollection, useFirestoreDoc, useHostOrgId,
|
|
29
|
+
import { useConsoleHostRoute, useFirestore, useFirestoreCollection, useFirestoreDoc, useHostOrgId, useScopeTokens } from "@aglyn/tenant-feature-instance";
|
|
28
30
|
/*
|
|
29
31
|
* The MODULE, not the barrel, for the two PURE helpers — the specs that render
|
|
30
32
|
* this grid mock `@aglyn/tenant-feature-instance` wholesale to stage their
|
|
31
33
|
* Firestore hooks, and a query builder imported through that barrel disappears
|
|
32
34
|
* under the mock. Neither of these is a hook.
|
|
33
35
|
*/ import { ceilingedWindow, collectionCeiling } from "@aglyn/tenant-feature-instance/hooks/host-collection-queries";
|
|
34
|
-
import {
|
|
36
|
+
import { LISTING_CATEGORIES, listingArtifactType, listingArtifactLabel, resolvePluginInstallState } from "../model/marketplace.js";
|
|
37
|
+
import { BROWSE_SORTS, browseBase, MARKETPLACE_BROWSE_QUERY } from "../model/listing-query.js";
|
|
35
38
|
import { ListingImage } from "./listing-image.component.js";
|
|
36
39
|
// The console route table is shared (AGL-685), so these go through
|
|
37
40
|
// buildRoute rather than being reassembled from a base string — the shape
|
|
@@ -45,6 +48,10 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
45
48
|
* bounds an install-state LOOKUP, not the shelf: the listings query has its
|
|
46
49
|
* own cap, and the grid pages what survives the filters.
|
|
47
50
|
*/ const INSTALL_STATE_CEILING = 100;
|
|
51
|
+
/** How the browse clauses read in a refusal. */ const BROWSE_HEADERS = {
|
|
52
|
+
category: 'Category'
|
|
53
|
+
};
|
|
54
|
+
/** How long the search box waits for typing to stop before it asks again. */ const SEARCH_SETTLE_MS = 300;
|
|
48
55
|
/**
|
|
49
56
|
* Marketplace components browse (AGL-44). A read-only catalogue: each card links
|
|
50
57
|
* to the listing's detail page, which is the only place an install happens
|
|
@@ -52,21 +59,29 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
52
59
|
* site-targeting choice. The card still shows install STATE (installed,
|
|
53
60
|
* org-wide) so the shelf reads honestly, but carries no install action.
|
|
54
61
|
*/ export function MarketplaceBrowse(props) {
|
|
62
|
+
var _ref;
|
|
63
|
+
var _gridFilter_clauses_find;
|
|
55
64
|
const { hostId } = props;
|
|
56
65
|
const firestore = useFirestore();
|
|
57
|
-
const { data: user } = useUser();
|
|
58
66
|
const { orgScoped, orgSlug: orgSlugProp, publisherId } = props;
|
|
59
67
|
const [handles, setHandles] = useState({});
|
|
60
68
|
// Listings are org-owned (AGL-652), so "is this mine" is an org comparison.
|
|
61
69
|
// Resolved from the routing mirror rather than a new prop so the component
|
|
62
|
-
// stays self-contained; hostIndex is signed-in readable.
|
|
63
|
-
|
|
70
|
+
// stays self-contained; hostIndex is signed-in readable. `undefined` while
|
|
71
|
+
// it resolves: the shelf's query names the viewer's org in its audience
|
|
72
|
+
// clause, so it waits for the answer rather than asking twice. A failed
|
|
73
|
+
// lookup settles at `null` — the public shelf, without the viewer's own
|
|
74
|
+
// listings in review.
|
|
75
|
+
const [viewerOrgId, setViewerOrgId] = useState(undefined);
|
|
64
76
|
useEffect(()=>{
|
|
65
77
|
let active = true;
|
|
78
|
+
setViewerOrgId(undefined);
|
|
66
79
|
void getDoc(doc(firestore, 'hostIndex', hostId)).then((snapshot)=>{
|
|
67
80
|
var _snapshot_get;
|
|
68
81
|
if (active) setViewerOrgId((_snapshot_get = snapshot.get('orgId')) != null ? _snapshot_get : null);
|
|
69
|
-
}).catch(()=>
|
|
82
|
+
}).catch(()=>{
|
|
83
|
+
if (active) setViewerOrgId(null);
|
|
84
|
+
});
|
|
70
85
|
return ()=>{
|
|
71
86
|
active = false;
|
|
72
87
|
};
|
|
@@ -109,64 +124,75 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
109
124
|
handle: (_handles_profileId1 = handles[profileId]) != null ? _handles_profileId1 : profileId
|
|
110
125
|
}) : undefined;
|
|
111
126
|
};
|
|
112
|
-
|
|
113
|
-
*
|
|
114
|
-
* in `items` below instead (AGL-1196).
|
|
115
|
-
*
|
|
116
|
-
* `deletedAt` flips every time a publisher unpublishes or republishes. A
|
|
117
|
-
* document that stops matching a live query leaves the query target, and the
|
|
118
|
-
* client can cache a `noDocument` tombstone at its own path — which is then
|
|
119
|
-
* served to every reader of that path, including the detail page, which
|
|
120
|
-
* reads `marketplaceListings/{id}` BY ID. So a browse session open across an
|
|
121
|
-
* unpublish could 404 a listing that exists, and republishing would not
|
|
122
|
-
* clear it: a resumed listen only pulls deltas, so an otherwise-unchanged
|
|
123
|
-
* document is never re-sent (AGL-827, AGL-929).
|
|
124
|
-
*
|
|
125
|
-
* Dropping the predicate removes the mechanism rather than healing it: with
|
|
126
|
-
* no `where`, no document can stop matching. The rule permits it —
|
|
127
|
-
* `marketplaceListings` is `allow read: if true`, with no `resource.data`
|
|
128
|
-
* term, so an unconstrained list is not denied. (That is NOT true of the
|
|
129
|
-
* scoped `datasets` query below, which is why this fix does not generalise.)
|
|
130
|
-
*
|
|
131
|
-
* It also fixes a quieter bug: `== null` matches only an EXPLICIT null, and
|
|
132
|
-
* Firestore cannot express "field is absent". Every publish path stamps
|
|
133
|
-
* `deletedAt: null` today, so this happens to be exact — but any future
|
|
134
|
-
* write path that omits the field would make a live listing silently
|
|
135
|
-
* invisible. An in-memory falsy check treats absent as live, which is what
|
|
136
|
-
* the dataset and email-template filters in this file already do.
|
|
137
|
-
*
|
|
138
|
-
* The cap counts CANDIDATES, not results — private and unreviewed listings
|
|
139
|
-
* already consume slots and get filtered below, so this is the same class of
|
|
140
|
-
* consumption, nudged up to leave room for soft-deleted rows. Measured on
|
|
141
|
-
* production 2026-08-03: 7 listings, 0 soft-deleted, 0 missing the field.
|
|
142
|
-
*
|
|
143
|
-
* ## The cap DOES take an `orderBy`, and it is not the same decision
|
|
127
|
+
/*
|
|
128
|
+
* THE SHELF IS ITS QUERY (AGL-3321).
|
|
144
129
|
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
130
|
+
* Browse read the newest ninety listings and resolved everything else in
|
|
131
|
+
* memory — the search box, the category chips, the three sorts, and the
|
|
132
|
+
* gates that keep deleted, private and unreviewed listings off the shelf.
|
|
133
|
+
* A listing that was not among those ninety could not be found by any of
|
|
134
|
+
* them, and "Most installed" sorted a sample.
|
|
150
135
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* `
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
136
|
+
* Every one of them is on the query now. The gates are one clause, the
|
|
137
|
+
* listing's written `browseAudience` (see `listingBrowseAudience`), asked
|
|
138
|
+
* with `array-contains-any` for everyone (`*`) and the viewer's own org —
|
|
139
|
+
* the owner exemption the in-memory gate carried (AGL-432): a publisher
|
|
140
|
+
* sees their own listing while it waits on review. A publisher page asks
|
|
141
|
+
* the same scopes joined to that publisher. The search folds into the same
|
|
142
|
+
* clause through `browseTokens`, a category is an equality, and each sort
|
|
143
|
+
* is the query's order; `MARKETPLACE_BROWSE_QUERY` declares them and its
|
|
144
|
+
* spec pins the composites that serve them.
|
|
160
145
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
146
|
+
* `browseAudience` is MUTABLE — an unpublish, a takedown or a verdict
|
|
147
|
+
* rewrites it — so a document can stop matching mid-session and be cached
|
|
148
|
+
* as a `noDocument` tombstone at the path the detail page reads by id
|
|
149
|
+
* (AGL-827/929). That was why the `deletedAt` predicate came off this query
|
|
150
|
+
* in AGL-1196; `confirmDisappearances` is the repair that hook documents
|
|
151
|
+
* for a predicate that cannot be dropped, and this one cannot.
|
|
152
|
+
*/ const gridFilter = useListGridFilter({
|
|
153
|
+
selectFields: [
|
|
154
|
+
'category'
|
|
155
|
+
]
|
|
156
|
+
});
|
|
157
|
+
const [search, setSearch] = useState('');
|
|
158
|
+
const [searchWords, setSearchWords] = useState([]);
|
|
159
|
+
useEffect(()=>{
|
|
160
|
+
const timer = setTimeout(()=>setSearchWords(search.split(/\s+/).filter(Boolean)), SEARCH_SETTLE_MS);
|
|
161
|
+
return ()=>clearTimeout(timer);
|
|
162
|
+
}, [
|
|
163
|
+
search
|
|
164
|
+
]);
|
|
165
|
+
const [sort, setSort] = useState('newest');
|
|
166
|
+
const category = (_ref = (_gridFilter_clauses_find = gridFilter.clauses.find((clause)=>clause.field === 'category')) == null ? void 0 : _gridFilter_clauses_find.value) != null ? _ref : null;
|
|
167
|
+
const listingsRef = useMemo(()=>viewerOrgId === undefined ? null : collection(firestore, 'marketplaceListings'), [
|
|
168
|
+
firestore,
|
|
169
|
+
viewerOrgId
|
|
170
|
+
]);
|
|
171
|
+
const { rows: shown, status: shelfStatus, hasMore, page, setPage, pageSize, setPageSize, plan } = useListQuery({
|
|
172
|
+
collection: listingsRef,
|
|
173
|
+
declaration: MARKETPLACE_BROWSE_QUERY,
|
|
174
|
+
request: {
|
|
175
|
+
clauses: gridFilter.clauses,
|
|
176
|
+
search: searchWords,
|
|
177
|
+
sort: BROWSE_SORTS[sort],
|
|
178
|
+
base: browseBase(viewerOrgId, publisherId)
|
|
179
|
+
},
|
|
180
|
+
deps: [
|
|
181
|
+
firestore,
|
|
182
|
+
viewerOrgId,
|
|
183
|
+
publisherId
|
|
184
|
+
],
|
|
185
|
+
idField: '$id',
|
|
186
|
+
confirmDisappearances: true
|
|
169
187
|
});
|
|
188
|
+
const listings = shown;
|
|
189
|
+
const filtering = Boolean(category) || searchWords.length > 0;
|
|
190
|
+
const refusals = useMemo(()=>listQueryRefusals(plan.refused, {
|
|
191
|
+
fields: MARKETPLACE_BROWSE_QUERY.fields,
|
|
192
|
+
headers: BROWSE_HEADERS
|
|
193
|
+
}), [
|
|
194
|
+
plan
|
|
195
|
+
]);
|
|
170
196
|
/*
|
|
171
197
|
* ## The install-state lookups are ORDERED too, and they are not lists
|
|
172
198
|
*
|
|
@@ -378,89 +404,7 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
378
404
|
listings,
|
|
379
405
|
firestore
|
|
380
406
|
]);
|
|
381
|
-
|
|
382
|
-
// fetched page of listings.
|
|
383
|
-
const [search, setSearch] = useState('');
|
|
384
|
-
const [category, setCategory] = useState(null);
|
|
385
|
-
const [sort, setSort] = useState('newest');
|
|
386
|
-
const categories = useMemo(()=>[
|
|
387
|
-
...new Set((listings != null ? listings : []).map((listing)=>listing.category).filter(Boolean))
|
|
388
|
-
].sort(), [
|
|
389
|
-
listings
|
|
390
|
-
]);
|
|
391
|
-
const items = useMemo(()=>{
|
|
392
|
-
const needle = search.trim().toLowerCase();
|
|
393
|
-
const filtered = (listings != null ? listings : []).filter((listing)=>{
|
|
394
|
-
// Private plugins never appear here, not even for the org that owns
|
|
395
|
-
// them (AGL-968/993). The owner exemption below exists so a publisher
|
|
396
|
-
// can watch their own SUBMISSION move through review — but a private
|
|
397
|
-
// listing is not waiting to be listed, it is deliberately not for the
|
|
398
|
-
// marketplace, and showing it in this grid is the one thing "private"
|
|
399
|
-
// promises will not happen. Owners reach theirs from
|
|
400
|
-
// Marketplace › Listings, whose View opens the same detail page
|
|
401
|
-
// installs happen on.
|
|
402
|
-
// Soft-deleted listings used to be excluded by the query itself; they
|
|
403
|
-
// are dropped here now so no mutable field sits in a `where`
|
|
404
|
-
// (AGL-1196). Unconditional — unlike the review gate below, deletion
|
|
405
|
-
// has no owner exemption.
|
|
406
|
-
if (isListingDeleted(listing)) return false;
|
|
407
|
-
if (isPrivateListing(listing)) return false;
|
|
408
|
-
// Review queue gate (AGL-432): unreviewed/rejected plugin listings
|
|
409
|
-
// stay off the public browse; the owner still sees their own (the
|
|
410
|
-
// detail page shows them the status). UX-level only — the docs are
|
|
411
|
-
// public-readable by design.
|
|
412
|
-
if (!isListingBrowsable(listing) && listing.profileId !== viewerOrgId) {
|
|
413
|
-
return false;
|
|
414
|
-
}
|
|
415
|
-
// Publisher page (AGL-869): only this publisher's listings.
|
|
416
|
-
if (publisherId && listing.profileId !== publisherId) return false;
|
|
417
|
-
if (category && listing.category !== category) return false;
|
|
418
|
-
if (!needle) return true;
|
|
419
|
-
return [
|
|
420
|
-
listing.displayName,
|
|
421
|
-
listing.description,
|
|
422
|
-
listing.category
|
|
423
|
-
].filter(Boolean).some((value)=>value.toLowerCase().includes(needle));
|
|
424
|
-
});
|
|
425
|
-
return [
|
|
426
|
-
...filtered
|
|
427
|
-
].sort((a, b)=>{
|
|
428
|
-
var _ref, _ref1;
|
|
429
|
-
var _b_createdAt, _a_createdAt;
|
|
430
|
-
if (sort === 'installed') {
|
|
431
|
-
var _b_installCount, _a_installCount;
|
|
432
|
-
return ((_b_installCount = b.installCount) != null ? _b_installCount : 0) - ((_a_installCount = a.installCount) != null ? _a_installCount : 0);
|
|
433
|
-
}
|
|
434
|
-
if (sort === 'rated') {
|
|
435
|
-
var _b_ratingAverage, _a_ratingAverage, _b_ratingCount, _a_ratingCount;
|
|
436
|
-
// Unrated listings sort last rather than as zero-stars — a new
|
|
437
|
-
// listing has not been judged badly, it has not been judged.
|
|
438
|
-
const byAverage = ((_b_ratingAverage = b.ratingAverage) != null ? _b_ratingAverage : -1) - ((_a_ratingAverage = a.ratingAverage) != null ? _a_ratingAverage : -1);
|
|
439
|
-
return byAverage || ((_b_ratingCount = b.ratingCount) != null ? _b_ratingCount : 0) - ((_a_ratingCount = a.ratingCount) != null ? _a_ratingCount : 0);
|
|
440
|
-
}
|
|
441
|
-
return ((_ref = (_b_createdAt = b.createdAt) == null ? void 0 : _b_createdAt.seconds) != null ? _ref : 0) - ((_ref1 = (_a_createdAt = a.createdAt) == null ? void 0 : _a_createdAt.seconds) != null ? _ref1 : 0);
|
|
442
|
-
});
|
|
443
|
-
}, [
|
|
444
|
-
listings,
|
|
445
|
-
search,
|
|
446
|
-
category,
|
|
447
|
-
sort,
|
|
448
|
-
publisherId,
|
|
449
|
-
user == null ? void 0 : user.uid
|
|
450
|
-
]);
|
|
451
|
-
/*
|
|
452
|
-
* The shelf PAGES (AGL-2501). It used to render every card the window held
|
|
453
|
-
* in one wall, so the only control over how much arrived at once was the
|
|
454
|
-
* cap in the query — a number the reader cannot see and cannot change.
|
|
455
|
-
*
|
|
456
|
-
* Client-side, deliberately, and this is the case `ListPagination`'s
|
|
457
|
-
* `count` prop exists for. The search box, the category chips and the
|
|
458
|
-
* owner/review gates are all resolved in memory, so a server page would be
|
|
459
|
-
* a page of CANDIDATES: a reader filtering to one category would get pages
|
|
460
|
-
* that were mostly empty and a count that meant nothing. Paging what is
|
|
461
|
-
* left after the filter is the only version whose numbers are about what is
|
|
462
|
-
* on screen.
|
|
463
|
-
*/ /**
|
|
407
|
+
/**
|
|
464
408
|
* The install-state lookups did not read everything they were asked about.
|
|
465
409
|
*
|
|
466
410
|
* Said on the SHELF rather than on the card that is wrong, because there is
|
|
@@ -469,25 +413,6 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
469
413
|
* chips are a floor — anything marked installed is installed, and something
|
|
470
414
|
* unmarked may still be.
|
|
471
415
|
*/ const installStateTruncated = installedTruncated || hostPinsTruncated || orgPinsTruncated || datasetsTruncated || emailsTruncated;
|
|
472
|
-
const [page, setPage] = useState(0);
|
|
473
|
-
const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT);
|
|
474
|
-
/*
|
|
475
|
-
* A new query starts at page one. Page four of a category with two pages
|
|
476
|
-
* does not exist, and an out-of-range page renders as an empty grid with
|
|
477
|
-
* nothing saying why — which reads as the filter having broken.
|
|
478
|
-
*/ useEffect(()=>{
|
|
479
|
-
setPage(0);
|
|
480
|
-
}, [
|
|
481
|
-
search,
|
|
482
|
-
category,
|
|
483
|
-
sort,
|
|
484
|
-
pageSize
|
|
485
|
-
]);
|
|
486
|
-
const shown = useMemo(()=>items.slice(page * pageSize, (page + 1) * pageSize), [
|
|
487
|
-
items,
|
|
488
|
-
page,
|
|
489
|
-
pageSize
|
|
490
|
-
]);
|
|
491
416
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
492
417
|
header: 'Marketplace components',
|
|
493
418
|
help: pluginDocsHelp('plugins', {
|
|
@@ -515,11 +440,17 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
515
440
|
minWidth: 200
|
|
516
441
|
}
|
|
517
442
|
}),
|
|
518
|
-
|
|
443
|
+
LISTING_CATEGORIES.map((value)=>/*#__PURE__*/ _jsx(Chip, {
|
|
519
444
|
label: value,
|
|
520
445
|
variant: category === value ? 'filled' : 'outlined',
|
|
521
446
|
color: category === value ? 'primary' : 'default',
|
|
522
|
-
onClick: ()=>
|
|
447
|
+
onClick: ()=>gridFilter.setClauses(category === value ? [] : [
|
|
448
|
+
{
|
|
449
|
+
field: 'category',
|
|
450
|
+
op: 'equals',
|
|
451
|
+
value
|
|
452
|
+
}
|
|
453
|
+
])
|
|
523
454
|
}, value)),
|
|
524
455
|
/*#__PURE__*/ _jsxs(TextField, {
|
|
525
456
|
value: sort,
|
|
@@ -554,11 +485,15 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
554
485
|
},
|
|
555
486
|
children: `Installed state is resolved from the first ${INSTALL_STATE_CEILING} ` + 'install records in this workspace, ordered by id. There are ' + 'more, so a card may not be marked installed even though it is. ' + 'Open a listing to see what this workspace actually runs.'
|
|
556
487
|
}) : null,
|
|
557
|
-
|
|
488
|
+
/*#__PURE__*/ _jsx(ListQueryNotices, {
|
|
489
|
+
refused: refusals,
|
|
490
|
+
notices: plan.notices
|
|
491
|
+
}),
|
|
492
|
+
shown.length === 0 && page === 0 ? shelfStatus === 'success' ? /*#__PURE__*/ _jsx(Typography, {
|
|
558
493
|
variant: "body2",
|
|
559
494
|
color: "text.secondary",
|
|
560
|
-
children: 'No marketplace components published yet — publish one of
|
|
561
|
-
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
495
|
+
children: filtering ? 'No components match this search.' : 'No marketplace components published yet — publish one of ' + 'your reusable components from the Setup page to be the first.'
|
|
496
|
+
}) : null : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
562
497
|
children: [
|
|
563
498
|
/*#__PURE__*/ _jsx(Grid, {
|
|
564
499
|
container: true,
|
|
@@ -787,7 +722,7 @@ import { ListingImage } from "./listing-image.component.js";
|
|
|
787
722
|
page: page,
|
|
788
723
|
pageSize: pageSize,
|
|
789
724
|
rowCount: shown.length,
|
|
790
|
-
|
|
725
|
+
hasMore: hasMore,
|
|
791
726
|
onPageChange: setPage,
|
|
792
727
|
onPageSizeChange: setPageSize
|
|
793
728
|
})
|