@aglyn/plugins-marketplace 1.0.0-beta.199 → 1.0.0-beta.201
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/checkout.js +3 -1
- package/src/lib/server/checkout.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
|
@@ -18,55 +18,45 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
19
|
import { ICON_VARIANT_COMPONENT } from "@aglyn/shared-data-enums";
|
|
20
20
|
import { AppLink, CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
21
|
-
import { useFirestore,
|
|
21
|
+
import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
|
|
22
|
+
import { useListQuery } from "@aglyn/tenant-feature-instance/hooks/use-list-query";
|
|
22
23
|
import { Alert, Chip, Stack } from "@mui/material";
|
|
23
24
|
import ListFilterChips from "@aglyn/shared-ui-jsx/components/list-filter-chips.component";
|
|
25
|
+
import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
|
|
26
|
+
import ListQueryNotices, { listQueryRefusals } from "@aglyn/shared-ui-jsx/components/list-query-notices.component";
|
|
24
27
|
import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { collection
|
|
28
|
+
import { listFilterGridColumns } from "@aglyn/shared-ui-jsx/const/list-grid-filter";
|
|
29
|
+
import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filter";
|
|
30
|
+
import { collection } from "firebase/firestore";
|
|
28
31
|
import { useMemo } from "react";
|
|
29
32
|
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
30
33
|
import { TABLE_ROW_HEIGHT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
31
34
|
import { listingPath } from "../model/marketplace-paths.js";
|
|
35
|
+
import { EVERY_WORKSPACE, HELD_LICENCE_QUERY, licenceBase, MINE_LICENCE_QUERY, mineLicenceClauses } from "../model/listing-query.js";
|
|
36
|
+
import { useListingNameLookup, useListingNames } from "../hooks/use-listing-name-lookup.js";
|
|
32
37
|
import EmptyState from "@aglyn/shared-ui-jsx/components/read-gated-empty-state.component";
|
|
33
38
|
/*
|
|
34
|
-
* What each license grid's Filters panel offers
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
* What each license grid's Filters panel offers, and how its search reads —
|
|
40
|
+
* every clause and the search on the QUERY (AGL-3321). They were matched over
|
|
41
|
+
* the rows each list had loaded (the retired window filter), which answered "no
|
|
42
|
+
* match" for a license past what was read.
|
|
43
|
+
*
|
|
44
|
+
* - "This workspace" filters by who bought it — You, by uid equality. "A
|
|
45
|
+
* colleague" was an in-memory negation; Firestore's `!=` would lead the
|
|
46
|
+
* order by buyer and needs a composite per clause beside it, so it is not
|
|
47
|
+
* offered.
|
|
48
|
+
* - "Bought by you" filters by the workspace a license landed in, or the
|
|
49
|
+
* every-workspace grant a purchase naming none carries (`buyerOrgId`
|
|
50
|
+
* null).
|
|
51
|
+
* - The search is the listing's NAME: the listings holding the word are
|
|
52
|
+
* asked first, and the license query asks for their ids
|
|
53
|
+
* (`useListingNameLookup`).
|
|
54
|
+
*/ const HELD_FILTER_HEADERS = {
|
|
44
55
|
buyerUid: 'Bought by'
|
|
45
56
|
};
|
|
46
|
-
const HELD_FILTER_OPTIONS = {
|
|
47
|
-
buyerUid: [
|
|
48
|
-
{
|
|
49
|
-
value: 'you',
|
|
50
|
-
label: 'You'
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
value: 'colleague',
|
|
54
|
-
label: 'A colleague'
|
|
55
|
-
}
|
|
56
|
-
]
|
|
57
|
-
};
|
|
58
|
-
const MINE_FILTER_FIELDS = [
|
|
59
|
-
inMemoryListField('listingId', 'text', 'listingName'),
|
|
60
|
-
inMemoryListField('buyerOrgId', 'select', 'licensedTo')
|
|
61
|
-
];
|
|
62
57
|
const MINE_FILTER_HEADERS = {
|
|
63
|
-
listingId: 'Listing',
|
|
64
58
|
buyerOrgId: 'Licensed to'
|
|
65
59
|
};
|
|
66
|
-
/** A purchase that names no organization, which entitles its buyer everywhere. */ const EVERY_WORKSPACE = 'every';
|
|
67
|
-
/** What the quick search reads on a license row. */ const LICENSE_SEARCH_FIELDS = [
|
|
68
|
-
'listingName'
|
|
69
|
-
];
|
|
70
60
|
/**
|
|
71
61
|
* WHICH WORKSPACE HOLDS A LICENCE (AGL-2331).
|
|
72
62
|
*
|
|
@@ -118,57 +108,11 @@ const MINE_FILTER_HEADERS = {
|
|
|
118
108
|
* cache with `status: 'success'`, which is why `serverDenied` is checked
|
|
119
109
|
* alongside it rather than trusting the status alone.
|
|
120
110
|
*/ export function OrgLicencesPanel({ orgId, basePath, viewerOrgs }) {
|
|
111
|
+
var _heldLookup_ids, _mineLookup_ids;
|
|
112
|
+
var _heldLookup_ids1, _mineLookup_ids1, _heldLookup_ids2, _mineLookup_ids2;
|
|
121
113
|
const firestore = useFirestore();
|
|
122
114
|
const { data: user } = useUser();
|
|
123
115
|
const uid = user == null ? void 0 : user.uid;
|
|
124
|
-
/** The licences this workspace holds — whoever in it did the buying. */ const orgLicencesRead = useFirestoreCollection(()=>orgId ? query(collection(firestore, 'marketplacePurchases'), where('buyerOrgId', '==', orgId)) : null, [
|
|
125
|
-
firestore,
|
|
126
|
-
orgId
|
|
127
|
-
], {
|
|
128
|
-
idField: '$id'
|
|
129
|
-
});
|
|
130
|
-
/** Everything this person has ever bought, in any workspace. */ const myPurchasesRead = useFirestoreCollection(()=>uid ? query(collection(firestore, 'marketplacePurchases'), where('buyerUid', '==', uid)) : null, [
|
|
131
|
-
firestore,
|
|
132
|
-
uid
|
|
133
|
-
], {
|
|
134
|
-
idField: '$id'
|
|
135
|
-
});
|
|
136
|
-
/**
|
|
137
|
-
* Listing id → display name.
|
|
138
|
-
*
|
|
139
|
-
* One subscription rather than a read per row: `marketplaceListings` is
|
|
140
|
-
* world-readable (it is the catalogue), the browse grid on the sibling tab
|
|
141
|
-
* already listens to it, and a per-row hook would change the hook count
|
|
142
|
-
* between renders as licences arrive.
|
|
143
|
-
*/ const { data: listings } = useFirestoreCollection(/*
|
|
144
|
-
* ORDERED, so the window is a reachable 200 rather than a sample.
|
|
145
|
-
*
|
|
146
|
-
* A capped read with no `orderBy` is answered in document-id order
|
|
147
|
-
* anyway, so naming it changes no row — what it changes is that the
|
|
148
|
-
* ordering is a decision rather than an accident, and that the obvious
|
|
149
|
-
* next edit is caught: `orderBy('displayName')` would drop every listing
|
|
150
|
-
* saved without one out of the map, and a licence whose listing is
|
|
151
|
-
* missing from the map is a row that names itself by raw id.
|
|
152
|
-
*
|
|
153
|
-
* Ordering on the document NAME, which is what a listing id already is
|
|
154
|
-
* here, keeps the map's keys and the walk in the same space. Past 200
|
|
155
|
-
* listings a licence still renders — the fallback below prints the
|
|
156
|
-
* listing id — so the cap degrades the label and never the row.
|
|
157
|
-
*/ ()=>query(collection(firestore, 'marketplaceListings'), orderBy(documentId()), limit(200)), [
|
|
158
|
-
firestore
|
|
159
|
-
], {
|
|
160
|
-
idField: '$id'
|
|
161
|
-
});
|
|
162
|
-
const listingNames = useMemo(()=>{
|
|
163
|
-
const names = {};
|
|
164
|
-
for (const listing of listings != null ? listings : []){
|
|
165
|
-
var _listing_displayName;
|
|
166
|
-
names[listing.$id] = String((_listing_displayName = listing.displayName) != null ? _listing_displayName : listing.$id);
|
|
167
|
-
}
|
|
168
|
-
return names;
|
|
169
|
-
}, [
|
|
170
|
-
listings
|
|
171
|
-
]);
|
|
172
116
|
/** Workspace id → the name the member already sees in the org switcher. */ const orgNames = useMemo(()=>{
|
|
173
117
|
const names = {};
|
|
174
118
|
for (const workspace of viewerOrgs != null ? viewerOrgs : [])names[workspace.id] = workspace.name;
|
|
@@ -176,33 +120,16 @@ const MINE_FILTER_HEADERS = {
|
|
|
176
120
|
}, [
|
|
177
121
|
viewerOrgs
|
|
178
122
|
]);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
var _listingNames_String, _row_listingId, _row_listingId1;
|
|
187
|
-
return _extends({}, row, {
|
|
188
|
-
listingName: (_listingNames_String = listingNames[String((_row_listingId = row.listingId) != null ? _row_listingId : '')]) != null ? _listingNames_String : String((_row_listingId1 = row.listingId) != null ? _row_listingId1 : ''),
|
|
189
|
-
boughtBy: row.buyerUid === uid ? 'you' : 'colleague'
|
|
190
|
-
});
|
|
123
|
+
const heldOptions = useMemo(()=>({
|
|
124
|
+
buyerUid: uid ? [
|
|
125
|
+
{
|
|
126
|
+
value: uid,
|
|
127
|
+
label: 'You'
|
|
128
|
+
}
|
|
129
|
+
] : []
|
|
191
130
|
}), [
|
|
192
|
-
orgLicencesRead.data,
|
|
193
|
-
listingNames,
|
|
194
131
|
uid
|
|
195
132
|
]);
|
|
196
|
-
const mine = useMemo(()=>live(myPurchasesRead.data).map((row)=>{
|
|
197
|
-
var _listingNames_String, _row_listingId, _row_listingId1, _row_buyerOrgId;
|
|
198
|
-
return _extends({}, row, {
|
|
199
|
-
listingName: (_listingNames_String = listingNames[String((_row_listingId = row.listingId) != null ? _row_listingId : '')]) != null ? _listingNames_String : String((_row_listingId1 = row.listingId) != null ? _row_listingId1 : ''),
|
|
200
|
-
licensedTo: String((_row_buyerOrgId = row.buyerOrgId) != null ? _row_buyerOrgId : '') || EVERY_WORKSPACE
|
|
201
|
-
});
|
|
202
|
-
}), [
|
|
203
|
-
myPurchasesRead.data,
|
|
204
|
-
listingNames
|
|
205
|
-
]);
|
|
206
133
|
const mineOptions = useMemo(()=>({
|
|
207
134
|
buyerOrgId: [
|
|
208
135
|
...orgId ? [
|
|
@@ -224,35 +151,99 @@ const MINE_FILTER_HEADERS = {
|
|
|
224
151
|
orgId,
|
|
225
152
|
viewerOrgs
|
|
226
153
|
]);
|
|
227
|
-
const heldFilter =
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
headers: HELD_FILTER_HEADERS,
|
|
232
|
-
search: LICENSE_SEARCH_FIELDS
|
|
154
|
+
const heldFilter = useListGridFilter({
|
|
155
|
+
selectFields: [
|
|
156
|
+
'buyerUid'
|
|
157
|
+
]
|
|
233
158
|
});
|
|
234
|
-
const mineFilter =
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
headers: MINE_FILTER_HEADERS,
|
|
239
|
-
search: LICENSE_SEARCH_FIELDS
|
|
159
|
+
const mineFilter = useListGridFilter({
|
|
160
|
+
selectFields: [
|
|
161
|
+
'buyerOrgId'
|
|
162
|
+
]
|
|
240
163
|
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
164
|
+
const heldLookup = useListingNameLookup(heldFilter.searchWords);
|
|
165
|
+
const mineLookup = useListingNameLookup(mineFilter.searchWords);
|
|
166
|
+
/*
|
|
167
|
+
* Both lists read `marketplacePurchases`, which is buyer/org/seller-gated —
|
|
168
|
+
* the org clause landed with AGL-2331 for exactly this list. Held at null
|
|
169
|
+
* until the org and uid resolve: a rules-shaped LIST is evaluated against
|
|
170
|
+
* the QUERY, so a sentinel value is a guaranteed denial retried on the
|
|
171
|
+
* refusal cadence (AGL-1440), not an empty list. A search whose listing
|
|
172
|
+
* lookup is still out holds too, and one that named no listing is answered
|
|
173
|
+
* with no rows rather than an `in []` Firestore refuses.
|
|
249
174
|
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
|
|
255
|
-
|
|
175
|
+
* A refunded purchase is not a licence (AGL-1546) and must not be listed as
|
|
176
|
+
* one — the whole point of this panel is deciding whether to buy, and a row
|
|
177
|
+
* that says "you own this" when the install route answers 402 is worse than
|
|
178
|
+
* no row. `refundedAt == null` is on both queries (`licenceBase`).
|
|
179
|
+
*/ const purchases = useMemo(()=>collection(firestore, 'marketplacePurchases'), [
|
|
180
|
+
firestore
|
|
181
|
+
]);
|
|
182
|
+
const heldRead = useListQuery({
|
|
183
|
+
collection: orgId && !heldLookup.pending && ((_heldLookup_ids1 = heldLookup.ids) == null ? void 0 : _heldLookup_ids1.length) !== 0 ? purchases : null,
|
|
184
|
+
declaration: HELD_LICENCE_QUERY,
|
|
185
|
+
request: {
|
|
186
|
+
clauses: heldFilter.clauses,
|
|
187
|
+
base: orgId ? licenceBase({
|
|
188
|
+
path: 'buyerOrgId',
|
|
189
|
+
value: orgId
|
|
190
|
+
}, heldLookup.ids) : []
|
|
191
|
+
},
|
|
192
|
+
deps: [
|
|
193
|
+
firestore,
|
|
194
|
+
orgId,
|
|
195
|
+
heldLookup.pending,
|
|
196
|
+
((_heldLookup_ids = heldLookup.ids) != null ? _heldLookup_ids : []).join(',')
|
|
197
|
+
],
|
|
198
|
+
idField: '$id'
|
|
199
|
+
});
|
|
200
|
+
const mineRead = useListQuery({
|
|
201
|
+
collection: uid && !mineLookup.pending && ((_mineLookup_ids1 = mineLookup.ids) == null ? void 0 : _mineLookup_ids1.length) !== 0 ? purchases : null,
|
|
202
|
+
declaration: MINE_LICENCE_QUERY,
|
|
203
|
+
request: {
|
|
204
|
+
clauses: mineLicenceClauses(mineFilter.clauses),
|
|
205
|
+
base: uid ? licenceBase({
|
|
206
|
+
path: 'buyerUid',
|
|
207
|
+
value: uid
|
|
208
|
+
}, mineLookup.ids) : []
|
|
209
|
+
},
|
|
210
|
+
deps: [
|
|
211
|
+
firestore,
|
|
212
|
+
uid,
|
|
213
|
+
mineLookup.pending,
|
|
214
|
+
((_mineLookup_ids = mineLookup.ids) != null ? _mineLookup_ids : []).join(',')
|
|
215
|
+
],
|
|
216
|
+
idField: '$id'
|
|
217
|
+
});
|
|
218
|
+
const heldNamedNone = ((_heldLookup_ids2 = heldLookup.ids) == null ? void 0 : _heldLookup_ids2.length) === 0;
|
|
219
|
+
const mineNamedNone = ((_mineLookup_ids2 = mineLookup.ids) == null ? void 0 : _mineLookup_ids2.length) === 0;
|
|
220
|
+
const heldRows = useMemo(()=>heldNamedNone ? [] : heldRead.rows, [
|
|
221
|
+
heldNamedNone,
|
|
222
|
+
heldRead.rows
|
|
223
|
+
]);
|
|
224
|
+
const mineRows = useMemo(()=>mineNamedNone ? [] : mineRead.rows, [
|
|
225
|
+
mineNamedNone,
|
|
226
|
+
mineRead.rows
|
|
227
|
+
]);
|
|
228
|
+
/**
|
|
229
|
+
* Listing id → display name, read by id for the rows on screen. It was a
|
|
230
|
+
* window of the first two hundred listings, past which a license printed
|
|
231
|
+
* its listing's raw id.
|
|
232
|
+
*/ const listingNames = useListingNames([
|
|
233
|
+
...heldRows,
|
|
234
|
+
...mineRows
|
|
235
|
+
].map((row)=>{
|
|
236
|
+
var _row_listingId;
|
|
237
|
+
return String((_row_listingId = row.listingId) != null ? _row_listingId : '');
|
|
238
|
+
}));
|
|
239
|
+
const held = useMemo(()=>heldRows.map((row)=>_extends({}, row, {
|
|
240
|
+
boughtBy: row.buyerUid === uid ? 'You' : 'A colleague'
|
|
241
|
+
})), [
|
|
242
|
+
heldRows,
|
|
243
|
+
uid
|
|
244
|
+
]);
|
|
245
|
+
const mine = mineRows;
|
|
246
|
+
const heldColumns = useMemo(()=>listFilterGridColumns([
|
|
256
247
|
{
|
|
257
248
|
field: 'listingId',
|
|
258
249
|
headerName: 'Listing',
|
|
@@ -274,8 +265,8 @@ const MINE_FILTER_HEADERS = {
|
|
|
274
265
|
field: 'buyerUid',
|
|
275
266
|
headerName: 'Bought by',
|
|
276
267
|
width: 150,
|
|
277
|
-
//
|
|
278
|
-
|
|
268
|
+
// Drawn as who it was, whatever the select's one choice is.
|
|
269
|
+
renderCell: ({ row })=>row.boughtBy
|
|
279
270
|
},
|
|
280
271
|
{
|
|
281
272
|
field: 'amountCents',
|
|
@@ -291,11 +282,12 @@ const MINE_FILTER_HEADERS = {
|
|
|
291
282
|
},
|
|
292
283
|
valueFormatter: (value)=>`$${value.toFixed(2)}`
|
|
293
284
|
}
|
|
294
|
-
], [
|
|
285
|
+
], HELD_LICENCE_QUERY.fields, heldOptions, HELD_FILTER_HEADERS), [
|
|
295
286
|
listingNames,
|
|
296
|
-
basePath
|
|
287
|
+
basePath,
|
|
288
|
+
heldOptions
|
|
297
289
|
]);
|
|
298
|
-
const mineColumns = useMemo(()=>[
|
|
290
|
+
const mineColumns = useMemo(()=>listFilterGridColumns([
|
|
299
291
|
{
|
|
300
292
|
field: 'listingId',
|
|
301
293
|
headerName: 'Listing',
|
|
@@ -340,10 +332,11 @@ const MINE_FILTER_HEADERS = {
|
|
|
340
332
|
});
|
|
341
333
|
}
|
|
342
334
|
}
|
|
343
|
-
], [
|
|
335
|
+
], MINE_LICENCE_QUERY.fields, mineOptions, MINE_FILTER_HEADERS), [
|
|
344
336
|
listingNames,
|
|
345
337
|
orgId,
|
|
346
|
-
orgNames
|
|
338
|
+
orgNames,
|
|
339
|
+
mineOptions
|
|
347
340
|
]);
|
|
348
341
|
/**
|
|
349
342
|
* "You own nothing here" is a claim about this workspace's purchases, and a
|
|
@@ -354,6 +347,7 @@ const MINE_FILTER_HEADERS = {
|
|
|
354
347
|
* has refused past its retry budget keeps painting whatever the local cache
|
|
355
348
|
* holds, and `status` alone still reads `success` for it.
|
|
356
349
|
*/ const outcome = (read)=>read.status === 'error' || read.serverDenied === true ? 'unavailable' : read.status === 'success' ? 'loaded' : 'loading';
|
|
350
|
+
const filtering = (filter)=>filter.clauses.length > 0 || filter.searchWords.length > 0;
|
|
357
351
|
return /*#__PURE__*/ _jsxs(Stack, {
|
|
358
352
|
spacing: 3,
|
|
359
353
|
children: [
|
|
@@ -361,8 +355,8 @@ const MINE_FILTER_HEADERS = {
|
|
|
361
355
|
severity: "info",
|
|
362
356
|
children: 'A marketplace purchase licenses one organization. Any member with ' + 'install permission can install what this workspace owns — and a ' + 'second workspace needs its own license.'
|
|
363
357
|
}),
|
|
364
|
-
held.length === 0 ? /*#__PURE__*/ _jsx(EmptyState, {
|
|
365
|
-
read: outcome(
|
|
358
|
+
held.length === 0 && heldRead.page === 0 && !filtering(heldFilter) ? /*#__PURE__*/ _jsx(EmptyState, {
|
|
359
|
+
read: outcome(heldRead),
|
|
366
360
|
subject: "this workspace’s licenses",
|
|
367
361
|
iconPath: ICON_VARIANT_COMPONENT.path,
|
|
368
362
|
title: 'This workspace holds no licenses',
|
|
@@ -376,23 +370,20 @@ const MINE_FILTER_HEADERS = {
|
|
|
376
370
|
subheader: 'Installable by any member with install permission, whoever ' + 'on the team bought it',
|
|
377
371
|
contentGutterX: true,
|
|
378
372
|
contentGutterY: true,
|
|
379
|
-
children: /*#__PURE__*/
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
noRowsLabel: "No licenses match these filters"
|
|
390
|
-
}))
|
|
391
|
-
]
|
|
373
|
+
children: /*#__PURE__*/ _jsx(LicenceTable, {
|
|
374
|
+
label: "Licenses this workspace holds",
|
|
375
|
+
declaration: HELD_LICENCE_QUERY,
|
|
376
|
+
headers: HELD_FILTER_HEADERS,
|
|
377
|
+
options: heldOptions,
|
|
378
|
+
gridFilter: heldFilter,
|
|
379
|
+
lookup: heldLookup,
|
|
380
|
+
read: heldRead,
|
|
381
|
+
rows: held,
|
|
382
|
+
columns: heldColumns
|
|
392
383
|
})
|
|
393
384
|
}),
|
|
394
|
-
mine.length === 0 ? /*#__PURE__*/ _jsx(EmptyState, {
|
|
395
|
-
read: outcome(
|
|
385
|
+
mine.length === 0 && mineRead.page === 0 && !filtering(mineFilter) ? /*#__PURE__*/ _jsx(EmptyState, {
|
|
386
|
+
read: outcome(mineRead),
|
|
396
387
|
subject: "your purchases",
|
|
397
388
|
iconPath: ICON_VARIANT_COMPONENT.path,
|
|
398
389
|
title: 'You have not bought anything yet',
|
|
@@ -406,24 +397,75 @@ const MINE_FILTER_HEADERS = {
|
|
|
406
397
|
subheader: 'Your own receipts, and which workspace each license landed in',
|
|
407
398
|
contentGutterX: true,
|
|
408
399
|
contentGutterY: true,
|
|
409
|
-
children: /*#__PURE__*/
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
noRowsLabel: "No licenses match these filters"
|
|
420
|
-
}))
|
|
421
|
-
]
|
|
400
|
+
children: /*#__PURE__*/ _jsx(LicenceTable, {
|
|
401
|
+
label: "Licenses you bought",
|
|
402
|
+
declaration: MINE_LICENCE_QUERY,
|
|
403
|
+
headers: MINE_FILTER_HEADERS,
|
|
404
|
+
options: mineOptions,
|
|
405
|
+
gridFilter: mineFilter,
|
|
406
|
+
lookup: mineLookup,
|
|
407
|
+
read: mineRead,
|
|
408
|
+
rows: mine,
|
|
409
|
+
columns: mineColumns
|
|
422
410
|
})
|
|
423
411
|
})
|
|
424
412
|
]
|
|
425
413
|
});
|
|
426
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* One license grid: its clause chips, what the query refused or said, the
|
|
417
|
+
* grid (filtered and searched by the query, not by itself) and its pager.
|
|
418
|
+
*/ function LicenceTable(props) {
|
|
419
|
+
const { label, declaration, headers, options, gridFilter, lookup, read, rows, columns } = props;
|
|
420
|
+
const refused = listQueryRefusals([
|
|
421
|
+
...lookup.refused,
|
|
422
|
+
...read.plan.refused
|
|
423
|
+
], {
|
|
424
|
+
fields: declaration.fields,
|
|
425
|
+
headers,
|
|
426
|
+
options
|
|
427
|
+
});
|
|
428
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
429
|
+
spacing: 1,
|
|
430
|
+
children: [
|
|
431
|
+
/*#__PURE__*/ _jsx(ListFilterChips, {
|
|
432
|
+
fields: declaration.fields,
|
|
433
|
+
headers: headers,
|
|
434
|
+
clauses: gridFilter.clauses,
|
|
435
|
+
onChange: gridFilter.setClauses,
|
|
436
|
+
options: options
|
|
437
|
+
}),
|
|
438
|
+
/*#__PURE__*/ _jsx(ListQueryNotices, {
|
|
439
|
+
refused: refused,
|
|
440
|
+
notices: lookup.notices
|
|
441
|
+
}),
|
|
442
|
+
/*#__PURE__*/ _jsx(ListTable, {
|
|
443
|
+
"aria-label": label,
|
|
444
|
+
rows: rows,
|
|
445
|
+
columns: columns,
|
|
446
|
+
filterMode: "server",
|
|
447
|
+
filterModel: gridFilter.filterModel,
|
|
448
|
+
onFilterModelChange: gridFilter.onFilterModelChange,
|
|
449
|
+
quickFilter: true,
|
|
450
|
+
// The query's order is the purchases' own; the grid does not re-sort a page.
|
|
451
|
+
sortingMode: "server",
|
|
452
|
+
disableColumnSorting: true,
|
|
453
|
+
// `ListPagination` below pages the query.
|
|
454
|
+
hideFooter: true,
|
|
455
|
+
rowHeight: TABLE_ROW_HEIGHT,
|
|
456
|
+
noRowsLabel: "No licenses match these filters"
|
|
457
|
+
}),
|
|
458
|
+
/*#__PURE__*/ _jsx(ListPagination, {
|
|
459
|
+
page: read.page,
|
|
460
|
+
pageSize: read.pageSize,
|
|
461
|
+
rowCount: rows.length,
|
|
462
|
+
hasMore: read.hasMore,
|
|
463
|
+
onPageChange: read.setPage,
|
|
464
|
+
onPageSizeChange: read.setPageSize
|
|
465
|
+
})
|
|
466
|
+
]
|
|
467
|
+
});
|
|
468
|
+
}
|
|
427
469
|
export default OrgLicencesPanel;
|
|
428
470
|
|
|
429
471
|
//# sourceMappingURL=org-licences-panel.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/org-licences-panel.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 { ICON_VARIANT_COMPONENT } from '@aglyn/shared-data-enums'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n useFirestore,\n useFirestoreCollection,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { Alert, Chip, Stack } from '@mui/material'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { inMemoryListField } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListRowsFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-rows-filter'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n documentId,\n limit,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { listingPath } from '../model/marketplace-paths'\nimport EmptyState from '@aglyn/shared-ui-jsx/components/read-gated-empty-state.component'\nimport type { ReadOutcome } from '@aglyn/shared-ui-jsx/utils/read-outcome'\n\n/*\n * What each license grid's Filters panel offers. Both lists hold every row\n * they describe, so the panel and the search answer over all of them. The\n * Listing column filters by the listing's name; who bought a license and\n * which workspace one landed in are picked.\n */\nconst HELD_FILTER_FIELDS = [\n inMemoryListField('listingId', 'text', 'listingName'),\n inMemoryListField('buyerUid', 'select', 'boughtBy'),\n]\nconst HELD_FILTER_HEADERS: Readonly<Record<string, string>> = {\n listingId: 'Listing',\n buyerUid: 'Bought by',\n}\nconst HELD_FILTER_OPTIONS = {\n buyerUid: [\n { value: 'you', label: 'You' },\n { value: 'colleague', label: 'A colleague' },\n ],\n}\nconst MINE_FILTER_FIELDS = [\n inMemoryListField('listingId', 'text', 'listingName'),\n inMemoryListField('buyerOrgId', 'select', 'licensedTo'),\n]\nconst MINE_FILTER_HEADERS: Readonly<Record<string, string>> = {\n listingId: 'Listing',\n buyerOrgId: 'Licensed to',\n}\n/** A purchase that names no organization, which entitles its buyer everywhere. */\nconst EVERY_WORKSPACE = 'every'\n/** What the quick search reads on a license row. */\nconst LICENSE_SEARCH_FIELDS = ['listingName'] as const\n\n/**\n * WHICH WORKSPACE HOLDS A LICENCE (AGL-2331).\n *\n * A marketplace purchase licenses the installing ORGANIZATION, not the person\n * who paid. That is the model the Publisher Agreement already publishes and\n * the model the install routes now enforce — and it is unusable without a\n * surface, because the two questions it creates have no answer anywhere else\n * in the console:\n *\n * \"Does THIS workspace own that component, or was it the other client's?\"\n * \"I bought this once — which workspace did the licence land in?\"\n *\n * An agency is the population this matters to most, and an agency is exactly\n * the population that cannot answer either question from memory. Before this\n * panel the only signal was a Buy button, and a Buy button is the same\n * whether you own nothing or own three of them in other workspaces.\n *\n * TWO LISTS, deliberately, because they are two different facts:\n *\n * `This workspace` is the org's inventory — what any member with install\n * rights may install here, including licences a colleague bought and\n * licences bought by someone who has since left. It is the list that\n * decides whether a Buy is needed.\n *\n * `Bought by you` is the buyer's own receipt trail across every workspace\n * they belong to. It is what turns \"I already bought this\" into \"…for\n * Northwind, and this is Contoso\".\n *\n * Both read `marketplacePurchases`, which is buyer/org/seller-gated — the org\n * clause landed with AGL-2331 for exactly this list. Held at null until the\n * org and uid resolve: a rules-shaped LIST is evaluated against the QUERY, so\n * a sentinel value is a guaranteed denial retried on the refusal cadence\n * (AGL-1440), not an empty list.\n *\n * ## Presentation (AGL-2486)\n *\n * Each list is a card, like every sibling Marketplace tab — it shipped as two\n * bare `subtitle1` headings with a loose sentence under each, which on a page\n * built from cards reads as an unfinished tab rather than an empty one.\n *\n * More than cosmetic: the sentences became `EmptyState`s, so they are now\n * GATED on the read having succeeded (AGL-1066). \"You have not bought\n * anything\" is a claim about someone's purchase history, and a refused or\n * unfinished read supports no such claim — on a tab whose whole purpose is\n * answering \"do we already own this?\", a zero-state produced by a dead\n * session is an invitation to buy something twice. Both queries are held at\n * `null` until the scope resolves, which reads as `loading` and can never\n * reach the zero-state; a listen the server has REFUSED keeps painting from\n * cache with `status: 'success'`, which is why `serverDenied` is checked\n * alongside it rather than trusting the status alone.\n */\nexport function OrgLicencesPanel({\n orgId,\n basePath,\n viewerOrgs,\n}: {\n orgId?: string | null\n /**\n * Where the marketplace hub is mounted under this organization, from the\n * shell (AGL-3080) — each row links to its listing beneath it. It replaced\n * an org slug and the console's route table, which a plugin may not\n * import.\n */\n basePath: string\n /**\n * Every workspace the reader belongs to, from the shell, so the \"Licensed\n * to\" column can name the one a purchase landed in instead of printing its\n * id. A purchase licenses an ORGANIZATION (AGL-2331), which is the whole\n * subject of this panel, and the reader's other workspaces are the answer\n * to half of it.\n */\n viewerOrgs?: readonly { id: string; name: string }[]\n}) {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const uid = (user as { uid?: string } | null | undefined)?.uid\n\n /** The licences this workspace holds — whoever in it did the buying. */\n const orgLicencesRead = useFirestoreCollection<any>(\n () =>\n orgId\n ? query(\n collection(firestore, 'marketplacePurchases'),\n where('buyerOrgId', '==', orgId),\n )\n : null,\n [firestore, orgId],\n { idField: '$id' },\n )\n /** Everything this person has ever bought, in any workspace. */\n const myPurchasesRead = useFirestoreCollection<any>(\n () =>\n uid\n ? query(\n collection(firestore, 'marketplacePurchases'),\n where('buyerUid', '==', uid),\n )\n : null,\n [firestore, uid],\n { idField: '$id' },\n )\n\n /**\n * Listing id → display name.\n *\n * One subscription rather than a read per row: `marketplaceListings` is\n * world-readable (it is the catalogue), the browse grid on the sibling tab\n * already listens to it, and a per-row hook would change the hook count\n * between renders as licences arrive.\n */\n const { data: listings } = useFirestoreCollection<any>(\n /*\n * ORDERED, so the window is a reachable 200 rather than a sample.\n *\n * A capped read with no `orderBy` is answered in document-id order\n * anyway, so naming it changes no row — what it changes is that the\n * ordering is a decision rather than an accident, and that the obvious\n * next edit is caught: `orderBy('displayName')` would drop every listing\n * saved without one out of the map, and a licence whose listing is\n * missing from the map is a row that names itself by raw id.\n *\n * Ordering on the document NAME, which is what a listing id already is\n * here, keeps the map's keys and the walk in the same space. Past 200\n * listings a licence still renders — the fallback below prints the\n * listing id — so the cap degrades the label and never the row.\n */\n () =>\n query(\n collection(firestore, 'marketplaceListings'),\n orderBy(documentId()),\n limit(200),\n ),\n [firestore],\n { idField: '$id' },\n )\n const listingNames = useMemo(() => {\n const names: Record<string, string> = {}\n for (const listing of listings ?? []) {\n names[listing.$id] = String(listing.displayName ?? listing.$id)\n }\n return names\n }, [listings])\n\n /** Workspace id → the name the member already sees in the org switcher. */\n const orgNames = useMemo(() => {\n const names: Record<string, string> = {}\n for (const workspace of viewerOrgs ?? []) names[workspace.id] = workspace.name\n return names\n }, [viewerOrgs])\n\n /**\n * A refunded purchase is not a licence (AGL-1546) and must not be listed as\n * one — the whole point of this panel is deciding whether to buy, and a row\n * that says \"you own this\" when the install route answers 402 is worse than\n * no row.\n */\n const live = (rows: any[] | undefined): any[] =>\n (rows ?? []).filter((row) => !row?.refundedAt)\n\n const held = useMemo(\n () =>\n live(orgLicencesRead.data).map((row) => ({\n ...row,\n listingName:\n listingNames[String(row.listingId ?? '')] ?? String(row.listingId ?? ''),\n boughtBy: row.buyerUid === uid ? 'you' : 'colleague',\n })),\n [orgLicencesRead.data, listingNames, uid],\n )\n const mine = useMemo(\n () =>\n live(myPurchasesRead.data).map((row) => ({\n ...row,\n listingName:\n listingNames[String(row.listingId ?? '')] ?? String(row.listingId ?? ''),\n licensedTo: String(row.buyerOrgId ?? '') || EVERY_WORKSPACE,\n })),\n [myPurchasesRead.data, listingNames],\n )\n\n const mineOptions = useMemo(\n () => ({\n buyerOrgId: [\n ...(orgId ? [{ value: orgId, label: 'This workspace' }] : []),\n ...(viewerOrgs ?? [])\n .filter((workspace) => workspace.id !== orgId)\n .map((workspace) => ({ value: workspace.id, label: workspace.name })),\n { value: EVERY_WORKSPACE, label: 'Every workspace you belong to' },\n ],\n }),\n [orgId, viewerOrgs],\n )\n const heldFilter = useListRowsFilter({\n rows: held,\n fields: HELD_FILTER_FIELDS,\n options: HELD_FILTER_OPTIONS,\n headers: HELD_FILTER_HEADERS,\n search: LICENSE_SEARCH_FIELDS,\n })\n const mineFilter = useListRowsFilter({\n rows: mine,\n fields: MINE_FILTER_FIELDS,\n options: mineOptions,\n headers: MINE_FILTER_HEADERS,\n search: LICENSE_SEARCH_FIELDS,\n })\n\n /*==========================================\n * BOTH LISTS ARE WHOLE, so the grid pages them and its counts are TOTALS.\n *\n * Neither query is capped: each reads one workspace's purchases or one\n * person's, whole. So the card holds the entire list it is describing, the\n * refund filter above has already run over all of it, and the grid's\n * footer counts the collection's real size rather than a window's length —\n * the one case where a client page can state a total without qualifying it.\n *\n * Server-paging either one would break that. `refundedAt` is filtered after\n * reading, so a ten-document page arrives holding anywhere from zero to ten\n * licences, and an agency deciding whether to buy a component again is\n * exactly the reader who must not be shown a short page as a complete\n * answer.\n *=========================================*/\n const heldColumns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'listingId',\n headerName: 'Listing',\n flex: 1,\n minWidth: 220,\n valueGetter: (_value, row) =>\n listingNames[String(row.listingId ?? '')] ?? String(row.listingId ?? ''),\n renderCell: ({ row, value }) => (\n <AppLink href={listingPath(basePath, String(row.listingId ?? ''))}>\n {value}\n </AppLink>\n ),\n },\n {\n field: 'buyerUid',\n headerName: 'Bought by',\n width: 150,\n // The select's value; the grid shows its label, You or A colleague.\n valueGetter: (_value, row) => row.boughtBy,\n },\n {\n field: 'amountCents',\n headerName: 'Paid',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 120,\n // What the licence cost before tax, which is what a reader compares.\n valueGetter: (_value, row) =>\n (Number(row.amountCents ?? 0) - Number(row.taxCents ?? 0)) / 100,\n valueFormatter: (value: number) => `$${value.toFixed(2)}`,\n },\n ],\n [listingNames, basePath],\n )\n const mineColumns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'listingId',\n headerName: 'Listing',\n flex: 1,\n minWidth: 220,\n valueGetter: (_value, row) =>\n listingNames[String(row.listingId ?? '')] ?? String(row.listingId ?? ''),\n },\n {\n field: 'buyerOrgId',\n headerName: 'Licensed to',\n flex: 1,\n minWidth: 220,\n valueGetter: (_value, row) => {\n const licensedOrg = String(row.buyerOrgId ?? '')\n if (!licensedOrg) return 'Every workspace you belong to'\n return licensedOrg === orgId\n ? 'This workspace'\n : (orgNames[licensedOrg] ?? licensedOrg)\n },\n renderCell: ({ row, value }) => {\n const licensedOrg = String(row.buyerOrgId ?? '')\n // A purchase made before AGL-2331 named no organization, so it is\n // not reinterpreted as belonging to one — it keeps entitling this\n // buyer everywhere, exactly as it did when they paid for it. Saying\n // \"every workspace\" rather than guessing an org is the whole reason\n // nobody loses access here.\n if (!licensedOrg) return <Chip size=\"small\" label={value} />\n return licensedOrg === orgId ? (\n <Chip size=\"small\" color=\"primary\" label={value} />\n ) : (\n <Chip size=\"small\" variant=\"outlined\" label={value} />\n )\n },\n },\n ],\n [listingNames, orgId, orgNames],\n )\n\n /**\n * \"You own nothing here\" is a claim about this workspace's purchases, and a\n * refused read supports no such claim (AGL-1066). Both queries are ALSO\n * held at `null` until the org and the uid resolve, which the hook reports\n * as `loading` — so an unresolved scope can never reach the zero-state\n * either. `serverDenied` is folded in beside `status`: a listen the server\n * has refused past its retry budget keeps painting whatever the local cache\n * holds, and `status` alone still reads `success` for it.\n */\n const outcome = (read: {\n status: string\n serverDenied: boolean\n }): ReadOutcome =>\n read.status === 'error' || read.serverDenied === true\n ? 'unavailable'\n : read.status === 'success'\n ? 'loaded'\n : 'loading'\n\n return (\n <Stack spacing={3}>\n <Alert severity=\"info\">\n {'A marketplace purchase licenses one organization. Any member with ' +\n 'install permission can install what this workspace owns — and a ' +\n 'second workspace needs its own license.'}\n </Alert>\n\n {held.length === 0 ? (\n <EmptyState\n read={outcome(orgLicencesRead)}\n subject=\"this workspace’s licenses\"\n iconPath={ICON_VARIANT_COMPONENT.path}\n title={'This workspace holds no licenses'}\n description={\n 'Anything bought for this workspace shows up here, whoever on ' +\n 'the team paid for it.'\n }\n />\n ) : (\n <CardDisplay\n header={'This workspace'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#how-installs-work-the-buyer-side',\n excerpt:\n 'What this workspace owns — installable by any member with ' +\n 'install permission, whoever on the team paid for it.',\n })}\n subheader={\n 'Installable by any member with install permission, whoever ' +\n 'on the team bought it'\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <ListFilterChips {...heldFilter.chipsProps} />\n <ListTable\n aria-label=\"Licenses this workspace holds\"\n rows={heldFilter.rows}\n columns={heldFilter.filterColumns(heldColumns)}\n {...heldFilter.gridProps}\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No licenses match these filters\"\n />\n </Stack>\n </CardDisplay>\n )}\n\n {mine.length === 0 ? (\n <EmptyState\n read={outcome(myPurchasesRead)}\n subject=\"your purchases\"\n iconPath={ICON_VARIANT_COMPONENT.path}\n title={'You have not bought anything yet'}\n description={\n 'Your own marketplace receipts appear here, across every ' +\n 'workspace you belong to.'\n }\n />\n ) : (\n <CardDisplay\n header={'Bought by you'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#how-installs-work-the-buyer-side',\n excerpt:\n 'Your own purchases across every workspace you belong to — a ' +\n 'purchase licenses one organization, so this says which.',\n })}\n subheader={\n 'Your own receipts, and which workspace each license landed in'\n }\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <ListFilterChips {...mineFilter.chipsProps} />\n <ListTable\n aria-label=\"Licenses you bought\"\n rows={mineFilter.rows}\n columns={mineFilter.filterColumns(mineColumns)}\n {...mineFilter.gridProps}\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No licenses match these filters\"\n />\n </Stack>\n </CardDisplay>\n )}\n </Stack>\n )\n}\n\nexport default OrgLicencesPanel\n"],"names":["ICON_VARIANT_COMPONENT","AppLink","CardDisplay","useFirestore","useFirestoreCollection","useUser","Alert","Chip","Stack","ListFilterChips","ListTable","inMemoryListField","useListRowsFilter","collection","documentId","limit","orderBy","query","where","useMemo","pluginDocsHelp","TABLE_ROW_HEIGHT","listingPath","EmptyState","HELD_FILTER_FIELDS","HELD_FILTER_HEADERS","listingId","buyerUid","HELD_FILTER_OPTIONS","value","label","MINE_FILTER_FIELDS","MINE_FILTER_HEADERS","buyerOrgId","EVERY_WORKSPACE","LICENSE_SEARCH_FIELDS","OrgLicencesPanel","orgId","basePath","viewerOrgs","firestore","data","user","uid","orgLicencesRead","idField","myPurchasesRead","listings","listingNames","names","listing","$id","String","displayName","orgNames","workspace","id","name","live","rows","filter","row","refundedAt","held","map","listingName","boughtBy","mine","licensedTo","mineOptions","heldFilter","fields","options","headers","search","mineFilter","heldColumns","field","headerName","flex","minWidth","valueGetter","_value","renderCell","href","width","type","align","headerAlign","Number","amountCents","taxCents","valueFormatter","toFixed","mineColumns","licensedOrg","size","color","variant","outcome","read","status","serverDenied","spacing","severity","length","subject","iconPath","path","title","description","header","help","anchor","excerpt","subheader","contentGutterX","contentGutterY","chipsProps","aria-label","columns","filterColumns","gridProps","rowHeight","noRowsLabel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/components/org-licences-panel.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 { ICON_VARIANT_COMPONENT } from '@aglyn/shared-data-enums'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n useFirestore,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport {\n useListQuery,\n type UseListQueryResult,\n} from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { Alert, Chip, Stack } from '@mui/material'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport ListQueryNotices, {\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport {\n type ListFilterOption,\n listFilterGridColumns,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport type { ListQueryDeclaration } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport {\n type ListGridFilter,\n useListGridFilter,\n} from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection } from 'firebase/firestore'\nimport { type ReactNode, useMemo } from 'react'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { listingPath } from '../model/marketplace-paths'\nimport {\n EVERY_WORKSPACE,\n HELD_LICENCE_QUERY,\n licenceBase,\n MINE_LICENCE_QUERY,\n mineLicenceClauses,\n} from '../model/listing-query'\nimport {\n useListingNameLookup,\n useListingNames,\n} from '../hooks/use-listing-name-lookup'\nimport EmptyState from '@aglyn/shared-ui-jsx/components/read-gated-empty-state.component'\nimport type { ReadOutcome } from '@aglyn/shared-ui-jsx/utils/read-outcome'\n\n/*\n * What each license grid's Filters panel offers, and how its search reads —\n * every clause and the search on the QUERY (AGL-3321). They were matched over\n * the rows each list had loaded (the retired window filter), which answered \"no\n * match\" for a license past what was read.\n *\n * - \"This workspace\" filters by who bought it — You, by uid equality. \"A\n * colleague\" was an in-memory negation; Firestore's `!=` would lead the\n * order by buyer and needs a composite per clause beside it, so it is not\n * offered.\n * - \"Bought by you\" filters by the workspace a license landed in, or the\n * every-workspace grant a purchase naming none carries (`buyerOrgId`\n * null).\n * - The search is the listing's NAME: the listings holding the word are\n * asked first, and the license query asks for their ids\n * (`useListingNameLookup`).\n */\nconst HELD_FILTER_HEADERS: Readonly<Record<string, string>> = {\n buyerUid: 'Bought by',\n}\nconst MINE_FILTER_HEADERS: Readonly<Record<string, string>> = {\n buyerOrgId: 'Licensed to',\n}\n\n/**\n * WHICH WORKSPACE HOLDS A LICENCE (AGL-2331).\n *\n * A marketplace purchase licenses the installing ORGANIZATION, not the person\n * who paid. That is the model the Publisher Agreement already publishes and\n * the model the install routes now enforce — and it is unusable without a\n * surface, because the two questions it creates have no answer anywhere else\n * in the console:\n *\n * \"Does THIS workspace own that component, or was it the other client's?\"\n * \"I bought this once — which workspace did the licence land in?\"\n *\n * An agency is the population this matters to most, and an agency is exactly\n * the population that cannot answer either question from memory. Before this\n * panel the only signal was a Buy button, and a Buy button is the same\n * whether you own nothing or own three of them in other workspaces.\n *\n * TWO LISTS, deliberately, because they are two different facts:\n *\n * `This workspace` is the org's inventory — what any member with install\n * rights may install here, including licences a colleague bought and\n * licences bought by someone who has since left. It is the list that\n * decides whether a Buy is needed.\n *\n * `Bought by you` is the buyer's own receipt trail across every workspace\n * they belong to. It is what turns \"I already bought this\" into \"…for\n * Northwind, and this is Contoso\".\n *\n * Both read `marketplacePurchases`, which is buyer/org/seller-gated — the org\n * clause landed with AGL-2331 for exactly this list. Held at null until the\n * org and uid resolve: a rules-shaped LIST is evaluated against the QUERY, so\n * a sentinel value is a guaranteed denial retried on the refusal cadence\n * (AGL-1440), not an empty list.\n *\n * ## Presentation (AGL-2486)\n *\n * Each list is a card, like every sibling Marketplace tab — it shipped as two\n * bare `subtitle1` headings with a loose sentence under each, which on a page\n * built from cards reads as an unfinished tab rather than an empty one.\n *\n * More than cosmetic: the sentences became `EmptyState`s, so they are now\n * GATED on the read having succeeded (AGL-1066). \"You have not bought\n * anything\" is a claim about someone's purchase history, and a refused or\n * unfinished read supports no such claim — on a tab whose whole purpose is\n * answering \"do we already own this?\", a zero-state produced by a dead\n * session is an invitation to buy something twice. Both queries are held at\n * `null` until the scope resolves, which reads as `loading` and can never\n * reach the zero-state; a listen the server has REFUSED keeps painting from\n * cache with `status: 'success'`, which is why `serverDenied` is checked\n * alongside it rather than trusting the status alone.\n */\nexport function OrgLicencesPanel({\n orgId,\n basePath,\n viewerOrgs,\n}: {\n orgId?: string | null\n /**\n * Where the marketplace hub is mounted under this organization, from the\n * shell (AGL-3080) — each row links to its listing beneath it. It replaced\n * an org slug and the console's route table, which a plugin may not\n * import.\n */\n basePath: string\n /**\n * Every workspace the reader belongs to, from the shell, so the \"Licensed\n * to\" column can name the one a purchase landed in instead of printing its\n * id. A purchase licenses an ORGANIZATION (AGL-2331), which is the whole\n * subject of this panel, and the reader's other workspaces are the answer\n * to half of it.\n */\n viewerOrgs?: readonly { id: string; name: string }[]\n}) {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const uid = (user as { uid?: string } | null | undefined)?.uid\n\n /** Workspace id → the name the member already sees in the org switcher. */\n const orgNames = useMemo(() => {\n const names: Record<string, string> = {}\n for (const workspace of viewerOrgs ?? []) names[workspace.id] = workspace.name\n return names\n }, [viewerOrgs])\n\n const heldOptions = useMemo(\n () => ({ buyerUid: uid ? [{ value: uid, label: 'You' }] : [] }),\n [uid],\n )\n const mineOptions = useMemo(\n () => ({\n buyerOrgId: [\n ...(orgId ? [{ value: orgId, label: 'This workspace' }] : []),\n ...(viewerOrgs ?? [])\n .filter((workspace) => workspace.id !== orgId)\n .map((workspace) => ({ value: workspace.id, label: workspace.name })),\n { value: EVERY_WORKSPACE, label: 'Every workspace you belong to' },\n ],\n }),\n [orgId, viewerOrgs],\n )\n const heldFilter = useListGridFilter({ selectFields: ['buyerUid'] })\n const mineFilter = useListGridFilter({ selectFields: ['buyerOrgId'] })\n const heldLookup = useListingNameLookup(heldFilter.searchWords)\n const mineLookup = useListingNameLookup(mineFilter.searchWords)\n\n /*\n * Both lists read `marketplacePurchases`, which is buyer/org/seller-gated —\n * the org clause landed with AGL-2331 for exactly this list. Held at null\n * until the org and uid resolve: a rules-shaped LIST is evaluated against\n * the QUERY, so a sentinel value is a guaranteed denial retried on the\n * refusal cadence (AGL-1440), not an empty list. A search whose listing\n * lookup is still out holds too, and one that named no listing is answered\n * with no rows rather than an `in []` Firestore refuses.\n *\n * A refunded purchase is not a licence (AGL-1546) and must not be listed as\n * one — the whole point of this panel is deciding whether to buy, and a row\n * that says \"you own this\" when the install route answers 402 is worse than\n * no row. `refundedAt == null` is on both queries (`licenceBase`).\n */\n const purchases = useMemo(\n () => collection(firestore, 'marketplacePurchases'),\n [firestore],\n )\n const heldRead = useListQuery<any>({\n collection:\n orgId && !heldLookup.pending && heldLookup.ids?.length !== 0\n ? purchases\n : null,\n declaration: HELD_LICENCE_QUERY,\n request: {\n clauses: heldFilter.clauses,\n base: orgId\n ? licenceBase({ path: 'buyerOrgId', value: orgId }, heldLookup.ids)\n : [],\n },\n deps: [firestore, orgId, heldLookup.pending, (heldLookup.ids ?? []).join(',')],\n idField: '$id',\n })\n const mineRead = useListQuery<any>({\n collection:\n uid && !mineLookup.pending && mineLookup.ids?.length !== 0 ? purchases : null,\n declaration: MINE_LICENCE_QUERY,\n request: {\n clauses: mineLicenceClauses(mineFilter.clauses),\n base: uid ? licenceBase({ path: 'buyerUid', value: uid }, mineLookup.ids) : [],\n },\n deps: [firestore, uid, mineLookup.pending, (mineLookup.ids ?? []).join(',')],\n idField: '$id',\n })\n const heldNamedNone = heldLookup.ids?.length === 0\n const mineNamedNone = mineLookup.ids?.length === 0\n const heldRows = useMemo(\n () => (heldNamedNone ? [] : heldRead.rows),\n [heldNamedNone, heldRead.rows],\n )\n const mineRows = useMemo(\n () => (mineNamedNone ? [] : mineRead.rows),\n [mineNamedNone, mineRead.rows],\n )\n\n /**\n * Listing id → display name, read by id for the rows on screen. It was a\n * window of the first two hundred listings, past which a license printed\n * its listing's raw id.\n */\n const listingNames = useListingNames(\n [...heldRows, ...mineRows].map((row) => String(row.listingId ?? '')),\n )\n\n const held = useMemo(\n () =>\n heldRows.map((row: any) => ({\n ...row,\n boughtBy: row.buyerUid === uid ? 'You' : 'A colleague',\n })),\n [heldRows, uid],\n )\n const mine = mineRows\n\n const heldColumns = useMemo<GridColDef[]>(\n () =>\n listFilterGridColumns(\n [\n {\n field: 'listingId',\n headerName: 'Listing',\n flex: 1,\n minWidth: 220,\n valueGetter: (_value, row) =>\n listingNames[String(row.listingId ?? '')] ?? String(row.listingId ?? ''),\n renderCell: ({ row, value }) => (\n <AppLink href={listingPath(basePath, String(row.listingId ?? ''))}>\n {value}\n </AppLink>\n ),\n },\n {\n field: 'buyerUid',\n headerName: 'Bought by',\n width: 150,\n // Drawn as who it was, whatever the select's one choice is.\n renderCell: ({ row }) => row.boughtBy,\n },\n {\n field: 'amountCents',\n headerName: 'Paid',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 120,\n // What the licence cost before tax, which is what a reader compares.\n valueGetter: (_value, row) =>\n (Number(row.amountCents ?? 0) - Number(row.taxCents ?? 0)) / 100,\n valueFormatter: (value: number) => `$${value.toFixed(2)}`,\n },\n ],\n HELD_LICENCE_QUERY.fields,\n heldOptions,\n HELD_FILTER_HEADERS,\n ),\n [listingNames, basePath, heldOptions],\n )\n const mineColumns = useMemo<GridColDef[]>(\n () =>\n listFilterGridColumns(\n [\n {\n field: 'listingId',\n headerName: 'Listing',\n flex: 1,\n minWidth: 220,\n valueGetter: (_value, row) =>\n listingNames[String(row.listingId ?? '')] ?? String(row.listingId ?? ''),\n },\n {\n field: 'buyerOrgId',\n headerName: 'Licensed to',\n flex: 1,\n minWidth: 220,\n valueGetter: (_value, row) => {\n const licensedOrg = String(row.buyerOrgId ?? '')\n if (!licensedOrg) return 'Every workspace you belong to'\n return licensedOrg === orgId\n ? 'This workspace'\n : (orgNames[licensedOrg] ?? licensedOrg)\n },\n renderCell: ({ row, value }) => {\n const licensedOrg = String(row.buyerOrgId ?? '')\n // A purchase made before AGL-2331 named no organization, so it is\n // not reinterpreted as belonging to one — it keeps entitling this\n // buyer everywhere, exactly as it did when they paid for it. Saying\n // \"every workspace\" rather than guessing an org is the whole reason\n // nobody loses access here.\n if (!licensedOrg) return <Chip size=\"small\" label={value} />\n return licensedOrg === orgId ? (\n <Chip size=\"small\" color=\"primary\" label={value} />\n ) : (\n <Chip size=\"small\" variant=\"outlined\" label={value} />\n )\n },\n },\n ],\n MINE_LICENCE_QUERY.fields,\n mineOptions,\n MINE_FILTER_HEADERS,\n ),\n [listingNames, orgId, orgNames, mineOptions],\n )\n\n /**\n * \"You own nothing here\" is a claim about this workspace's purchases, and a\n * refused read supports no such claim (AGL-1066). Both queries are ALSO\n * held at `null` until the org and the uid resolve, which the hook reports\n * as `loading` — so an unresolved scope can never reach the zero-state\n * either. `serverDenied` is folded in beside `status`: a listen the server\n * has refused past its retry budget keeps painting whatever the local cache\n * holds, and `status` alone still reads `success` for it.\n */\n const outcome = (read: {\n status: string\n serverDenied: boolean\n }): ReadOutcome =>\n read.status === 'error' || read.serverDenied === true\n ? 'unavailable'\n : read.status === 'success'\n ? 'loaded'\n : 'loading'\n\n const filtering = (filter: ListGridFilter) =>\n filter.clauses.length > 0 || filter.searchWords.length > 0\n\n return (\n <Stack spacing={3}>\n <Alert severity=\"info\">\n {'A marketplace purchase licenses one organization. Any member with ' +\n 'install permission can install what this workspace owns — and a ' +\n 'second workspace needs its own license.'}\n </Alert>\n\n {held.length === 0 && heldRead.page === 0 && !filtering(heldFilter) ? (\n <EmptyState\n read={outcome(heldRead)}\n subject=\"this workspace’s licenses\"\n iconPath={ICON_VARIANT_COMPONENT.path}\n title={'This workspace holds no licenses'}\n description={\n 'Anything bought for this workspace shows up here, whoever on ' +\n 'the team paid for it.'\n }\n />\n ) : (\n <CardDisplay\n header={'This workspace'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#how-installs-work-the-buyer-side',\n excerpt:\n 'What this workspace owns — installable by any member with ' +\n 'install permission, whoever on the team paid for it.',\n })}\n subheader={\n 'Installable by any member with install permission, whoever ' +\n 'on the team bought it'\n }\n contentGutterX\n contentGutterY\n >\n <LicenceTable\n label=\"Licenses this workspace holds\"\n declaration={HELD_LICENCE_QUERY}\n headers={HELD_FILTER_HEADERS}\n options={heldOptions}\n gridFilter={heldFilter}\n lookup={heldLookup}\n read={heldRead}\n rows={held}\n columns={heldColumns}\n />\n </CardDisplay>\n )}\n\n {mine.length === 0 && mineRead.page === 0 && !filtering(mineFilter) ? (\n <EmptyState\n read={outcome(mineRead)}\n subject=\"your purchases\"\n iconPath={ICON_VARIANT_COMPONENT.path}\n title={'You have not bought anything yet'}\n description={\n 'Your own marketplace receipts appear here, across every ' +\n 'workspace you belong to.'\n }\n />\n ) : (\n <CardDisplay\n header={'Bought by you'}\n help={pluginDocsHelp('publisherHandbook', {\n anchor: '#how-installs-work-the-buyer-side',\n excerpt:\n 'Your own purchases across every workspace you belong to — a ' +\n 'purchase licenses one organization, so this says which.',\n })}\n subheader={\n 'Your own receipts, and which workspace each license landed in'\n }\n contentGutterX\n contentGutterY\n >\n <LicenceTable\n label=\"Licenses you bought\"\n declaration={MINE_LICENCE_QUERY}\n headers={MINE_FILTER_HEADERS}\n options={mineOptions}\n gridFilter={mineFilter}\n lookup={mineLookup}\n read={mineRead}\n rows={mine}\n columns={mineColumns}\n />\n </CardDisplay>\n )}\n </Stack>\n )\n}\n\n/**\n * One license grid: its clause chips, what the query refused or said, the\n * grid (filtered and searched by the query, not by itself) and its pager.\n */\nfunction LicenceTable(props: {\n label: string\n declaration: ListQueryDeclaration\n headers: Readonly<Record<string, string>>\n options: Readonly<Record<string, readonly ListFilterOption[]>>\n gridFilter: ListGridFilter\n lookup: ReturnType<typeof useListingNameLookup>\n read: UseListQueryResult<any>\n rows: any[]\n columns: GridColDef[]\n}): ReactNode {\n const {\n label,\n declaration,\n headers,\n options,\n gridFilter,\n lookup,\n read,\n rows,\n columns,\n } = props\n const refused = listQueryRefusals([...lookup.refused, ...read.plan.refused], {\n fields: declaration.fields,\n headers,\n options,\n })\n return (\n <Stack spacing={1}>\n <ListFilterChips\n fields={declaration.fields}\n headers={headers}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={options}\n />\n <ListQueryNotices refused={refused} notices={lookup.notices} />\n <ListTable\n aria-label={label}\n rows={rows}\n columns={columns}\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n // The query's order is the purchases' own; the grid does not re-sort a page.\n sortingMode=\"server\"\n disableColumnSorting\n // `ListPagination` below pages the query.\n hideFooter\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No licenses match these filters\"\n />\n <ListPagination\n page={read.page}\n pageSize={read.pageSize}\n rowCount={rows.length}\n hasMore={read.hasMore}\n onPageChange={read.setPage}\n onPageSizeChange={read.setPageSize}\n />\n </Stack>\n )\n}\n\nexport default OrgLicencesPanel\n"],"names":["ICON_VARIANT_COMPONENT","AppLink","CardDisplay","useFirestore","useUser","useListQuery","Alert","Chip","Stack","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","listFilterGridColumns","useListGridFilter","collection","useMemo","pluginDocsHelp","TABLE_ROW_HEIGHT","listingPath","EVERY_WORKSPACE","HELD_LICENCE_QUERY","licenceBase","MINE_LICENCE_QUERY","mineLicenceClauses","useListingNameLookup","useListingNames","EmptyState","HELD_FILTER_HEADERS","buyerUid","MINE_FILTER_HEADERS","buyerOrgId","OrgLicencesPanel","orgId","basePath","viewerOrgs","heldLookup","mineLookup","firestore","data","user","uid","orgNames","names","workspace","id","name","heldOptions","value","label","mineOptions","filter","map","heldFilter","selectFields","mineFilter","searchWords","purchases","heldRead","pending","ids","length","declaration","request","clauses","base","path","deps","join","idField","mineRead","heldNamedNone","mineNamedNone","heldRows","rows","mineRows","listingNames","row","String","listingId","held","boughtBy","mine","heldColumns","field","headerName","flex","minWidth","valueGetter","_value","renderCell","href","width","type","align","headerAlign","Number","amountCents","taxCents","valueFormatter","toFixed","fields","mineColumns","licensedOrg","size","color","variant","outcome","read","status","serverDenied","filtering","spacing","severity","page","subject","iconPath","title","description","header","help","anchor","excerpt","subheader","contentGutterX","contentGutterY","LicenceTable","headers","options","gridFilter","lookup","columns","props","refused","plan","onChange","setClauses","notices","aria-label","filterMode","filterModel","onFilterModelChange","quickFilter","sortingMode","disableColumnSorting","hideFooter","rowHeight","noRowsLabel","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize"],"mappings":"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"}
|