@streamoid/settings 0.7.2 → 0.7.4
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/dist/index.d.mts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +76 -21
- package/dist/index.mjs +78 -22
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -182,6 +182,9 @@ interface SettingsWorkspaceListItem {
|
|
|
182
182
|
/** The organization this workspace belongs to, if any. My workspaces groups
|
|
183
183
|
* the list under each organization (named from `organization.list()`). */
|
|
184
184
|
organizationId?: string;
|
|
185
|
+
/** The organization's name, when the host has it (used by `listPage()`
|
|
186
|
+
* rows whose organization isn't in `organization.list()`). */
|
|
187
|
+
organizationName?: string;
|
|
185
188
|
}
|
|
186
189
|
interface SettingsWorkspaceAdapter {
|
|
187
190
|
/** Same authenticated transport used by the sidebar owner lookup. */
|
|
@@ -240,6 +243,20 @@ interface SettingsWorkspaceAdapter {
|
|
|
240
243
|
* When omitted, the plan on each `list()` item is used as before.
|
|
241
244
|
*/
|
|
242
245
|
fetchPlans?: (workspaceIds: string[]) => Promise<Record<string, string>>;
|
|
246
|
+
/**
|
|
247
|
+
* Optional: one page of the workspace list, from the API
|
|
248
|
+
* (`GET /workspace?limit=&offset=`). When given, My workspaces asks for each
|
|
249
|
+
* page as it is shown and renders exactly what comes back — `total` is every
|
|
250
|
+
* workspace the user can see. When omitted (or when a page request fails),
|
|
251
|
+
* My workspaces pages through `list()` in the browser as before.
|
|
252
|
+
*/
|
|
253
|
+
listPage?: (params: {
|
|
254
|
+
limit: number;
|
|
255
|
+
offset: number;
|
|
256
|
+
}) => Promise<{
|
|
257
|
+
items: SettingsWorkspaceListItem[];
|
|
258
|
+
total: number;
|
|
259
|
+
}>;
|
|
243
260
|
/**
|
|
244
261
|
* Optional: host-controlled refresh after the package mutates workspace
|
|
245
262
|
* state. If provided, the package will call this instead of (or in addition
|
|
@@ -961,7 +978,7 @@ interface WorkspaceListGroup {
|
|
|
961
978
|
* last. Groups keep the order the organizations came in.
|
|
962
979
|
*/
|
|
963
980
|
declare function groupWorkspaceList(workspaces: WorkspaceInfo[], organizations?: WorkspaceListOrganization[]): WorkspaceListGroup[];
|
|
964
|
-
declare function WorkspaceListTab({ workspaces, loading, onCreateClick: _onCreateClick, onEditClick, onSwitchClick, onLeaveClick, fetchPlans, organizations, pageSize, }: {
|
|
981
|
+
declare function WorkspaceListTab({ workspaces, loading, onCreateClick: _onCreateClick, onEditClick, onSwitchClick, onLeaveClick, fetchPlans, organizations, pageSize, listPage, reloadKey, }: {
|
|
965
982
|
workspaces: WorkspaceInfo[];
|
|
966
983
|
loading: boolean;
|
|
967
984
|
onCreateClick: () => void;
|
|
@@ -973,6 +990,18 @@ declare function WorkspaceListTab({ workspaces, loading, onCreateClick: _onCreat
|
|
|
973
990
|
/** The viewer's organizations, to group the list under. */
|
|
974
991
|
organizations?: WorkspaceListOrganization[];
|
|
975
992
|
pageSize?: number;
|
|
993
|
+
/** One page from the API (see SettingsWorkspaceAdapter.listPage). When
|
|
994
|
+
* given, each page is fetched as it is shown and rendered as returned. */
|
|
995
|
+
listPage?: (params: {
|
|
996
|
+
limit: number;
|
|
997
|
+
offset: number;
|
|
998
|
+
}) => Promise<{
|
|
999
|
+
items: WorkspaceInfo[];
|
|
1000
|
+
total: number;
|
|
1001
|
+
}>;
|
|
1002
|
+
/** Changes whenever the host's list changes (a leave, a rename…), so the
|
|
1003
|
+
* page on screen is fetched again. */
|
|
1004
|
+
reloadKey?: string;
|
|
976
1005
|
}): react.JSX.Element;
|
|
977
1006
|
/**
|
|
978
1007
|
* @deprecated Renamed to `WorkspaceListTab` in 0.4.0 — the tab is a workspace
|
|
@@ -1027,6 +1056,8 @@ interface WorkspaceData {
|
|
|
1027
1056
|
isCurrent: boolean;
|
|
1028
1057
|
imageUrl?: string;
|
|
1029
1058
|
imageName?: string;
|
|
1059
|
+
/** Kept through the list's local copy, so My workspaces can group by it. */
|
|
1060
|
+
organizationId?: string;
|
|
1030
1061
|
}
|
|
1031
1062
|
interface WorkspaceModalProps {
|
|
1032
1063
|
open: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -182,6 +182,9 @@ interface SettingsWorkspaceListItem {
|
|
|
182
182
|
/** The organization this workspace belongs to, if any. My workspaces groups
|
|
183
183
|
* the list under each organization (named from `organization.list()`). */
|
|
184
184
|
organizationId?: string;
|
|
185
|
+
/** The organization's name, when the host has it (used by `listPage()`
|
|
186
|
+
* rows whose organization isn't in `organization.list()`). */
|
|
187
|
+
organizationName?: string;
|
|
185
188
|
}
|
|
186
189
|
interface SettingsWorkspaceAdapter {
|
|
187
190
|
/** Same authenticated transport used by the sidebar owner lookup. */
|
|
@@ -240,6 +243,20 @@ interface SettingsWorkspaceAdapter {
|
|
|
240
243
|
* When omitted, the plan on each `list()` item is used as before.
|
|
241
244
|
*/
|
|
242
245
|
fetchPlans?: (workspaceIds: string[]) => Promise<Record<string, string>>;
|
|
246
|
+
/**
|
|
247
|
+
* Optional: one page of the workspace list, from the API
|
|
248
|
+
* (`GET /workspace?limit=&offset=`). When given, My workspaces asks for each
|
|
249
|
+
* page as it is shown and renders exactly what comes back — `total` is every
|
|
250
|
+
* workspace the user can see. When omitted (or when a page request fails),
|
|
251
|
+
* My workspaces pages through `list()` in the browser as before.
|
|
252
|
+
*/
|
|
253
|
+
listPage?: (params: {
|
|
254
|
+
limit: number;
|
|
255
|
+
offset: number;
|
|
256
|
+
}) => Promise<{
|
|
257
|
+
items: SettingsWorkspaceListItem[];
|
|
258
|
+
total: number;
|
|
259
|
+
}>;
|
|
243
260
|
/**
|
|
244
261
|
* Optional: host-controlled refresh after the package mutates workspace
|
|
245
262
|
* state. If provided, the package will call this instead of (or in addition
|
|
@@ -961,7 +978,7 @@ interface WorkspaceListGroup {
|
|
|
961
978
|
* last. Groups keep the order the organizations came in.
|
|
962
979
|
*/
|
|
963
980
|
declare function groupWorkspaceList(workspaces: WorkspaceInfo[], organizations?: WorkspaceListOrganization[]): WorkspaceListGroup[];
|
|
964
|
-
declare function WorkspaceListTab({ workspaces, loading, onCreateClick: _onCreateClick, onEditClick, onSwitchClick, onLeaveClick, fetchPlans, organizations, pageSize, }: {
|
|
981
|
+
declare function WorkspaceListTab({ workspaces, loading, onCreateClick: _onCreateClick, onEditClick, onSwitchClick, onLeaveClick, fetchPlans, organizations, pageSize, listPage, reloadKey, }: {
|
|
965
982
|
workspaces: WorkspaceInfo[];
|
|
966
983
|
loading: boolean;
|
|
967
984
|
onCreateClick: () => void;
|
|
@@ -973,6 +990,18 @@ declare function WorkspaceListTab({ workspaces, loading, onCreateClick: _onCreat
|
|
|
973
990
|
/** The viewer's organizations, to group the list under. */
|
|
974
991
|
organizations?: WorkspaceListOrganization[];
|
|
975
992
|
pageSize?: number;
|
|
993
|
+
/** One page from the API (see SettingsWorkspaceAdapter.listPage). When
|
|
994
|
+
* given, each page is fetched as it is shown and rendered as returned. */
|
|
995
|
+
listPage?: (params: {
|
|
996
|
+
limit: number;
|
|
997
|
+
offset: number;
|
|
998
|
+
}) => Promise<{
|
|
999
|
+
items: WorkspaceInfo[];
|
|
1000
|
+
total: number;
|
|
1001
|
+
}>;
|
|
1002
|
+
/** Changes whenever the host's list changes (a leave, a rename…), so the
|
|
1003
|
+
* page on screen is fetched again. */
|
|
1004
|
+
reloadKey?: string;
|
|
976
1005
|
}): react.JSX.Element;
|
|
977
1006
|
/**
|
|
978
1007
|
* @deprecated Renamed to `WorkspaceListTab` in 0.4.0 — the tab is a workspace
|
|
@@ -1027,6 +1056,8 @@ interface WorkspaceData {
|
|
|
1027
1056
|
isCurrent: boolean;
|
|
1028
1057
|
imageUrl?: string;
|
|
1029
1058
|
imageName?: string;
|
|
1059
|
+
/** Kept through the list's local copy, so My workspaces can group by it. */
|
|
1060
|
+
organizationId?: string;
|
|
1030
1061
|
}
|
|
1031
1062
|
interface WorkspaceModalProps {
|
|
1032
1063
|
open: boolean;
|
package/dist/index.js
CHANGED
|
@@ -10938,26 +10938,59 @@ function WorkspaceListTab({
|
|
|
10938
10938
|
onLeaveClick,
|
|
10939
10939
|
fetchPlans,
|
|
10940
10940
|
organizations = [],
|
|
10941
|
-
pageSize = WORKSPACE_LIST_PAGE_SIZE
|
|
10941
|
+
pageSize = WORKSPACE_LIST_PAGE_SIZE,
|
|
10942
|
+
listPage,
|
|
10943
|
+
reloadKey
|
|
10942
10944
|
}) {
|
|
10943
10945
|
const currentWorkspace = workspaces.find((ws) => ws.isCurrent);
|
|
10944
|
-
const
|
|
10946
|
+
const currentOrganization = organizations.find(
|
|
10947
|
+
(org) => org._id && org._id === currentWorkspace?.organizationId
|
|
10948
|
+
);
|
|
10949
|
+
const [page, setPage] = (0, import_react17.useState)(0);
|
|
10950
|
+
const [serverPage, setServerPage] = (0, import_react17.useState)(null);
|
|
10951
|
+
const [serverFailed, setServerFailed] = (0, import_react17.useState)(false);
|
|
10952
|
+
const serverMode = !!listPage && !serverFailed;
|
|
10953
|
+
(0, import_react17.useEffect)(() => {
|
|
10954
|
+
if (!listPage || serverFailed) return;
|
|
10955
|
+
let cancelled = false;
|
|
10956
|
+
listPage({ limit: pageSize, offset: page * pageSize }).then((result) => {
|
|
10957
|
+
if (cancelled) return;
|
|
10958
|
+
const lastPage = Math.max(0, Math.ceil(result.total / pageSize) - 1);
|
|
10959
|
+
if (page > lastPage) {
|
|
10960
|
+
setPage(lastPage);
|
|
10961
|
+
return;
|
|
10962
|
+
}
|
|
10963
|
+
setServerPage({ page, items: result.items, total: result.total });
|
|
10964
|
+
}).catch(() => {
|
|
10965
|
+
if (!cancelled) setServerFailed(true);
|
|
10966
|
+
});
|
|
10967
|
+
return () => {
|
|
10968
|
+
cancelled = true;
|
|
10969
|
+
};
|
|
10970
|
+
}, [listPage, serverFailed, page, pageSize, reloadKey]);
|
|
10971
|
+
const organizationLabel = (org) => org.role === "guest" ? `${org.name} \xB7 Guest` : org.name;
|
|
10972
|
+
const serverGroupOf = (ws) => {
|
|
10973
|
+
const org = organizations.find((o) => o._id === ws.organizationId);
|
|
10974
|
+
if (org) return { key: org._id, label: organizationLabel(org), workspaces: [] };
|
|
10975
|
+
if (ws.organizationId && ws.organizationName) {
|
|
10976
|
+
return { key: ws.organizationId, label: ws.organizationName, workspaces: [] };
|
|
10977
|
+
}
|
|
10978
|
+
return { key: "__no_org__", label: "No organization", workspaces: [] };
|
|
10979
|
+
};
|
|
10980
|
+
const groups = serverMode ? [] : groupWorkspaceList(
|
|
10945
10981
|
workspaces.filter((ws) => !ws.isCurrent),
|
|
10946
10982
|
organizations
|
|
10947
10983
|
);
|
|
10948
10984
|
const rows = groups.flatMap(
|
|
10949
10985
|
(group) => group.workspaces.map((ws) => ({ ws, group }))
|
|
10950
10986
|
);
|
|
10951
|
-
const
|
|
10952
|
-
const
|
|
10953
|
-
|
|
10954
|
-
);
|
|
10955
|
-
const pageCount = Math.max(1, Math.ceil(otherWorkspaces.length / pageSize));
|
|
10956
|
-
const [page, setPage] = (0, import_react17.useState)(0);
|
|
10957
|
-
const safePage = Math.min(page, pageCount - 1);
|
|
10987
|
+
const listTotal = serverMode ? serverPage?.total ?? 0 : rows.length;
|
|
10988
|
+
const pageCount = Math.max(1, Math.ceil(listTotal / pageSize));
|
|
10989
|
+
const safePage = serverMode ? serverPage?.page ?? 0 : Math.min(page, pageCount - 1);
|
|
10958
10990
|
const pageStart = safePage * pageSize;
|
|
10959
|
-
const pageRows = rows.slice(pageStart, pageStart + pageSize);
|
|
10991
|
+
const pageRows = serverMode ? (serverPage?.items ?? []).map((ws) => ({ ws, group: serverGroupOf(ws) })) : rows.slice(pageStart, pageStart + pageSize);
|
|
10960
10992
|
const pageWorkspaces = pageRows.map((row) => row.ws);
|
|
10993
|
+
const listLoading = loading || serverMode && !serverPage;
|
|
10961
10994
|
const [plans, setPlans] = (0, import_react17.useState)({});
|
|
10962
10995
|
const requestedPlans = (0, import_react17.useRef)(/* @__PURE__ */ new Set());
|
|
10963
10996
|
const visibleIds = [
|
|
@@ -10973,7 +11006,7 @@ function WorkspaceListTab({
|
|
|
10973
11006
|
};
|
|
10974
11007
|
}, []);
|
|
10975
11008
|
(0, import_react17.useEffect)(() => {
|
|
10976
|
-
if (!fetchPlans ||
|
|
11009
|
+
if (!fetchPlans || listLoading) return;
|
|
10977
11010
|
const missing = visibleKey.split(",").filter((id) => id && !requestedPlans.current.has(id));
|
|
10978
11011
|
if (!missing.length) return;
|
|
10979
11012
|
missing.forEach((id) => requestedPlans.current.add(id));
|
|
@@ -10981,7 +11014,7 @@ function WorkspaceListTab({
|
|
|
10981
11014
|
if (mounted.current) setPlans((prev) => ({ ...prev, ...found }));
|
|
10982
11015
|
}).catch(() => {
|
|
10983
11016
|
});
|
|
10984
|
-
}, [fetchPlans,
|
|
11017
|
+
}, [fetchPlans, listLoading, visibleKey]);
|
|
10985
11018
|
const planOf = (ws) => fetchPlans ? plans[ws._id] ?? "" : ws.plan ?? "";
|
|
10986
11019
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
10987
11020
|
"div",
|
|
@@ -11002,7 +11035,7 @@ function WorkspaceListTab({
|
|
|
11002
11035
|
children: "Switching changes which workspace the rest of Settings acts on."
|
|
11003
11036
|
}
|
|
11004
11037
|
),
|
|
11005
|
-
|
|
11038
|
+
listLoading && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
11006
11039
|
"p",
|
|
11007
11040
|
{
|
|
11008
11041
|
style: {
|
|
@@ -11013,7 +11046,7 @@ function WorkspaceListTab({
|
|
|
11013
11046
|
children: "Loading workspaces\u2026"
|
|
11014
11047
|
}
|
|
11015
11048
|
),
|
|
11016
|
-
!
|
|
11049
|
+
!listLoading && currentWorkspace && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
11017
11050
|
"div",
|
|
11018
11051
|
{
|
|
11019
11052
|
className: "flex flex-col items-start shrink-0 w-full",
|
|
@@ -11053,7 +11086,7 @@ function WorkspaceListTab({
|
|
|
11053
11086
|
]
|
|
11054
11087
|
}
|
|
11055
11088
|
),
|
|
11056
|
-
!
|
|
11089
|
+
!listLoading && currentWorkspace && listTotal > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
11057
11090
|
"div",
|
|
11058
11091
|
{
|
|
11059
11092
|
className: "shrink-0 w-full",
|
|
@@ -11063,7 +11096,7 @@ function WorkspaceListTab({
|
|
|
11063
11096
|
}
|
|
11064
11097
|
}
|
|
11065
11098
|
),
|
|
11066
|
-
!
|
|
11099
|
+
!listLoading && listTotal > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
11067
11100
|
"div",
|
|
11068
11101
|
{
|
|
11069
11102
|
className: "flex flex-col items-start flex-1 min-h-0 w-full",
|
|
@@ -11099,8 +11132,8 @@ function WorkspaceListTab({
|
|
|
11099
11132
|
role: ws.role,
|
|
11100
11133
|
plan: planOf(ws),
|
|
11101
11134
|
ownerId: ws.ownerEmail,
|
|
11102
|
-
type: "otherWS",
|
|
11103
|
-
onSwitch: () => onSwitchClick(ws),
|
|
11135
|
+
type: ws.isCurrent ? "currentWS" : "otherWS",
|
|
11136
|
+
onSwitch: ws.isCurrent ? void 0 : () => onSwitchClick(ws),
|
|
11104
11137
|
onSettings: () => onEditClick(ws),
|
|
11105
11138
|
onLeave: () => onLeaveClick(ws)
|
|
11106
11139
|
}
|
|
@@ -11129,7 +11162,7 @@ function WorkspaceListTab({
|
|
|
11129
11162
|
pageStart + pageWorkspaces.length,
|
|
11130
11163
|
" of",
|
|
11131
11164
|
" ",
|
|
11132
|
-
|
|
11165
|
+
listTotal
|
|
11133
11166
|
]
|
|
11134
11167
|
}
|
|
11135
11168
|
),
|
|
@@ -11303,6 +11336,24 @@ function SettingsContentInner({
|
|
|
11303
11336
|
(0, import_react17.useEffect)(() => {
|
|
11304
11337
|
void refreshWorkspaceList();
|
|
11305
11338
|
}, [refreshWorkspaceList]);
|
|
11339
|
+
const hostListPage = workspace.listPage;
|
|
11340
|
+
const listWorkspacePage = (0, import_react17.useMemo)(
|
|
11341
|
+
() => hostListPage ? async (params) => {
|
|
11342
|
+
const result = await hostListPage(params);
|
|
11343
|
+
return {
|
|
11344
|
+
total: result.total,
|
|
11345
|
+
items: result.items.map(
|
|
11346
|
+
(it) => ({
|
|
11347
|
+
...it,
|
|
11348
|
+
isCurrent: it.isCurrent ?? it._id === workspaceId,
|
|
11349
|
+
imageUrl: it.imageUrl ?? it.image,
|
|
11350
|
+
initials: deriveInitials(it.name)
|
|
11351
|
+
})
|
|
11352
|
+
)
|
|
11353
|
+
};
|
|
11354
|
+
} : void 0,
|
|
11355
|
+
[hostListPage, workspaceId]
|
|
11356
|
+
);
|
|
11306
11357
|
const [listOrganizations, setListOrganizations] = (0, import_react17.useState)([]);
|
|
11307
11358
|
(0, import_react17.useEffect)(() => {
|
|
11308
11359
|
if (activeTab !== "workspaceList" || typeof organization?.list !== "function") {
|
|
@@ -11374,7 +11425,8 @@ function SettingsContentInner({
|
|
|
11374
11425
|
plan: ws.plan ?? "",
|
|
11375
11426
|
ownerEmail: ws.ownerEmail ?? "",
|
|
11376
11427
|
isCurrent: ws.isCurrent ?? false,
|
|
11377
|
-
imageUrl: ws.imageUrl
|
|
11428
|
+
imageUrl: ws.imageUrl,
|
|
11429
|
+
organizationId: ws.organizationId
|
|
11378
11430
|
});
|
|
11379
11431
|
const toWorkspaceInfo = (ws) => ({
|
|
11380
11432
|
_id: ws.id,
|
|
@@ -11384,7 +11436,8 @@ function SettingsContentInner({
|
|
|
11384
11436
|
plan: ws.plan,
|
|
11385
11437
|
ownerEmail: ws.ownerEmail,
|
|
11386
11438
|
isCurrent: ws.isCurrent,
|
|
11387
|
-
imageUrl: ws.imageUrl
|
|
11439
|
+
imageUrl: ws.imageUrl,
|
|
11440
|
+
organizationId: ws.organizationId
|
|
11388
11441
|
});
|
|
11389
11442
|
const asObject = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11390
11443
|
const getWorkspaceImage = (record) => {
|
|
@@ -11974,6 +12027,8 @@ function SettingsContentInner({
|
|
|
11974
12027
|
loading: workspaceListLoading && workspaceList.length === 0,
|
|
11975
12028
|
fetchPlans: workspace.fetchPlans,
|
|
11976
12029
|
organizations: listOrganizations,
|
|
12030
|
+
listPage: listWorkspacePage,
|
|
12031
|
+
reloadKey: displayWorkspaces.map((ws) => `${ws._id}:${ws.name}:${ws.role}`).join(","),
|
|
11977
12032
|
onCreateClick: handleCreateClick,
|
|
11978
12033
|
onEditClick: handleEditClick,
|
|
11979
12034
|
onSwitchClick: async (ws) => {
|
package/dist/index.mjs
CHANGED
|
@@ -167,7 +167,8 @@ import {
|
|
|
167
167
|
useState as useState15,
|
|
168
168
|
useEffect as useEffect13,
|
|
169
169
|
useRef as useRef7,
|
|
170
|
-
useCallback as useCallback7
|
|
170
|
+
useCallback as useCallback7,
|
|
171
|
+
useMemo as useMemo3
|
|
171
172
|
} from "react";
|
|
172
173
|
import {
|
|
173
174
|
ScSidebarMenu,
|
|
@@ -10941,26 +10942,59 @@ function WorkspaceListTab({
|
|
|
10941
10942
|
onLeaveClick,
|
|
10942
10943
|
fetchPlans,
|
|
10943
10944
|
organizations = [],
|
|
10944
|
-
pageSize = WORKSPACE_LIST_PAGE_SIZE
|
|
10945
|
+
pageSize = WORKSPACE_LIST_PAGE_SIZE,
|
|
10946
|
+
listPage,
|
|
10947
|
+
reloadKey
|
|
10945
10948
|
}) {
|
|
10946
10949
|
const currentWorkspace = workspaces.find((ws) => ws.isCurrent);
|
|
10947
|
-
const
|
|
10950
|
+
const currentOrganization = organizations.find(
|
|
10951
|
+
(org) => org._id && org._id === currentWorkspace?.organizationId
|
|
10952
|
+
);
|
|
10953
|
+
const [page, setPage] = useState15(0);
|
|
10954
|
+
const [serverPage, setServerPage] = useState15(null);
|
|
10955
|
+
const [serverFailed, setServerFailed] = useState15(false);
|
|
10956
|
+
const serverMode = !!listPage && !serverFailed;
|
|
10957
|
+
useEffect13(() => {
|
|
10958
|
+
if (!listPage || serverFailed) return;
|
|
10959
|
+
let cancelled = false;
|
|
10960
|
+
listPage({ limit: pageSize, offset: page * pageSize }).then((result) => {
|
|
10961
|
+
if (cancelled) return;
|
|
10962
|
+
const lastPage = Math.max(0, Math.ceil(result.total / pageSize) - 1);
|
|
10963
|
+
if (page > lastPage) {
|
|
10964
|
+
setPage(lastPage);
|
|
10965
|
+
return;
|
|
10966
|
+
}
|
|
10967
|
+
setServerPage({ page, items: result.items, total: result.total });
|
|
10968
|
+
}).catch(() => {
|
|
10969
|
+
if (!cancelled) setServerFailed(true);
|
|
10970
|
+
});
|
|
10971
|
+
return () => {
|
|
10972
|
+
cancelled = true;
|
|
10973
|
+
};
|
|
10974
|
+
}, [listPage, serverFailed, page, pageSize, reloadKey]);
|
|
10975
|
+
const organizationLabel = (org) => org.role === "guest" ? `${org.name} \xB7 Guest` : org.name;
|
|
10976
|
+
const serverGroupOf = (ws) => {
|
|
10977
|
+
const org = organizations.find((o) => o._id === ws.organizationId);
|
|
10978
|
+
if (org) return { key: org._id, label: organizationLabel(org), workspaces: [] };
|
|
10979
|
+
if (ws.organizationId && ws.organizationName) {
|
|
10980
|
+
return { key: ws.organizationId, label: ws.organizationName, workspaces: [] };
|
|
10981
|
+
}
|
|
10982
|
+
return { key: "__no_org__", label: "No organization", workspaces: [] };
|
|
10983
|
+
};
|
|
10984
|
+
const groups = serverMode ? [] : groupWorkspaceList(
|
|
10948
10985
|
workspaces.filter((ws) => !ws.isCurrent),
|
|
10949
10986
|
organizations
|
|
10950
10987
|
);
|
|
10951
10988
|
const rows = groups.flatMap(
|
|
10952
10989
|
(group) => group.workspaces.map((ws) => ({ ws, group }))
|
|
10953
10990
|
);
|
|
10954
|
-
const
|
|
10955
|
-
const
|
|
10956
|
-
|
|
10957
|
-
);
|
|
10958
|
-
const pageCount = Math.max(1, Math.ceil(otherWorkspaces.length / pageSize));
|
|
10959
|
-
const [page, setPage] = useState15(0);
|
|
10960
|
-
const safePage = Math.min(page, pageCount - 1);
|
|
10991
|
+
const listTotal = serverMode ? serverPage?.total ?? 0 : rows.length;
|
|
10992
|
+
const pageCount = Math.max(1, Math.ceil(listTotal / pageSize));
|
|
10993
|
+
const safePage = serverMode ? serverPage?.page ?? 0 : Math.min(page, pageCount - 1);
|
|
10961
10994
|
const pageStart = safePage * pageSize;
|
|
10962
|
-
const pageRows = rows.slice(pageStart, pageStart + pageSize);
|
|
10995
|
+
const pageRows = serverMode ? (serverPage?.items ?? []).map((ws) => ({ ws, group: serverGroupOf(ws) })) : rows.slice(pageStart, pageStart + pageSize);
|
|
10963
10996
|
const pageWorkspaces = pageRows.map((row) => row.ws);
|
|
10997
|
+
const listLoading = loading || serverMode && !serverPage;
|
|
10964
10998
|
const [plans, setPlans] = useState15({});
|
|
10965
10999
|
const requestedPlans = useRef7(/* @__PURE__ */ new Set());
|
|
10966
11000
|
const visibleIds = [
|
|
@@ -10976,7 +11010,7 @@ function WorkspaceListTab({
|
|
|
10976
11010
|
};
|
|
10977
11011
|
}, []);
|
|
10978
11012
|
useEffect13(() => {
|
|
10979
|
-
if (!fetchPlans ||
|
|
11013
|
+
if (!fetchPlans || listLoading) return;
|
|
10980
11014
|
const missing = visibleKey.split(",").filter((id) => id && !requestedPlans.current.has(id));
|
|
10981
11015
|
if (!missing.length) return;
|
|
10982
11016
|
missing.forEach((id) => requestedPlans.current.add(id));
|
|
@@ -10984,7 +11018,7 @@ function WorkspaceListTab({
|
|
|
10984
11018
|
if (mounted.current) setPlans((prev) => ({ ...prev, ...found }));
|
|
10985
11019
|
}).catch(() => {
|
|
10986
11020
|
});
|
|
10987
|
-
}, [fetchPlans,
|
|
11021
|
+
}, [fetchPlans, listLoading, visibleKey]);
|
|
10988
11022
|
const planOf = (ws) => fetchPlans ? plans[ws._id] ?? "" : ws.plan ?? "";
|
|
10989
11023
|
return /* @__PURE__ */ jsxs19(
|
|
10990
11024
|
"div",
|
|
@@ -11005,7 +11039,7 @@ function WorkspaceListTab({
|
|
|
11005
11039
|
children: "Switching changes which workspace the rest of Settings acts on."
|
|
11006
11040
|
}
|
|
11007
11041
|
),
|
|
11008
|
-
|
|
11042
|
+
listLoading && /* @__PURE__ */ jsx22(
|
|
11009
11043
|
"p",
|
|
11010
11044
|
{
|
|
11011
11045
|
style: {
|
|
@@ -11016,7 +11050,7 @@ function WorkspaceListTab({
|
|
|
11016
11050
|
children: "Loading workspaces\u2026"
|
|
11017
11051
|
}
|
|
11018
11052
|
),
|
|
11019
|
-
!
|
|
11053
|
+
!listLoading && currentWorkspace && /* @__PURE__ */ jsxs19(
|
|
11020
11054
|
"div",
|
|
11021
11055
|
{
|
|
11022
11056
|
className: "flex flex-col items-start shrink-0 w-full",
|
|
@@ -11056,7 +11090,7 @@ function WorkspaceListTab({
|
|
|
11056
11090
|
]
|
|
11057
11091
|
}
|
|
11058
11092
|
),
|
|
11059
|
-
!
|
|
11093
|
+
!listLoading && currentWorkspace && listTotal > 0 && /* @__PURE__ */ jsx22(
|
|
11060
11094
|
"div",
|
|
11061
11095
|
{
|
|
11062
11096
|
className: "shrink-0 w-full",
|
|
@@ -11066,7 +11100,7 @@ function WorkspaceListTab({
|
|
|
11066
11100
|
}
|
|
11067
11101
|
}
|
|
11068
11102
|
),
|
|
11069
|
-
!
|
|
11103
|
+
!listLoading && listTotal > 0 && /* @__PURE__ */ jsxs19(
|
|
11070
11104
|
"div",
|
|
11071
11105
|
{
|
|
11072
11106
|
className: "flex flex-col items-start flex-1 min-h-0 w-full",
|
|
@@ -11102,8 +11136,8 @@ function WorkspaceListTab({
|
|
|
11102
11136
|
role: ws.role,
|
|
11103
11137
|
plan: planOf(ws),
|
|
11104
11138
|
ownerId: ws.ownerEmail,
|
|
11105
|
-
type: "otherWS",
|
|
11106
|
-
onSwitch: () => onSwitchClick(ws),
|
|
11139
|
+
type: ws.isCurrent ? "currentWS" : "otherWS",
|
|
11140
|
+
onSwitch: ws.isCurrent ? void 0 : () => onSwitchClick(ws),
|
|
11107
11141
|
onSettings: () => onEditClick(ws),
|
|
11108
11142
|
onLeave: () => onLeaveClick(ws)
|
|
11109
11143
|
}
|
|
@@ -11132,7 +11166,7 @@ function WorkspaceListTab({
|
|
|
11132
11166
|
pageStart + pageWorkspaces.length,
|
|
11133
11167
|
" of",
|
|
11134
11168
|
" ",
|
|
11135
|
-
|
|
11169
|
+
listTotal
|
|
11136
11170
|
]
|
|
11137
11171
|
}
|
|
11138
11172
|
),
|
|
@@ -11306,6 +11340,24 @@ function SettingsContentInner({
|
|
|
11306
11340
|
useEffect13(() => {
|
|
11307
11341
|
void refreshWorkspaceList();
|
|
11308
11342
|
}, [refreshWorkspaceList]);
|
|
11343
|
+
const hostListPage = workspace.listPage;
|
|
11344
|
+
const listWorkspacePage = useMemo3(
|
|
11345
|
+
() => hostListPage ? async (params) => {
|
|
11346
|
+
const result = await hostListPage(params);
|
|
11347
|
+
return {
|
|
11348
|
+
total: result.total,
|
|
11349
|
+
items: result.items.map(
|
|
11350
|
+
(it) => ({
|
|
11351
|
+
...it,
|
|
11352
|
+
isCurrent: it.isCurrent ?? it._id === workspaceId,
|
|
11353
|
+
imageUrl: it.imageUrl ?? it.image,
|
|
11354
|
+
initials: deriveInitials(it.name)
|
|
11355
|
+
})
|
|
11356
|
+
)
|
|
11357
|
+
};
|
|
11358
|
+
} : void 0,
|
|
11359
|
+
[hostListPage, workspaceId]
|
|
11360
|
+
);
|
|
11309
11361
|
const [listOrganizations, setListOrganizations] = useState15([]);
|
|
11310
11362
|
useEffect13(() => {
|
|
11311
11363
|
if (activeTab !== "workspaceList" || typeof organization?.list !== "function") {
|
|
@@ -11377,7 +11429,8 @@ function SettingsContentInner({
|
|
|
11377
11429
|
plan: ws.plan ?? "",
|
|
11378
11430
|
ownerEmail: ws.ownerEmail ?? "",
|
|
11379
11431
|
isCurrent: ws.isCurrent ?? false,
|
|
11380
|
-
imageUrl: ws.imageUrl
|
|
11432
|
+
imageUrl: ws.imageUrl,
|
|
11433
|
+
organizationId: ws.organizationId
|
|
11381
11434
|
});
|
|
11382
11435
|
const toWorkspaceInfo = (ws) => ({
|
|
11383
11436
|
_id: ws.id,
|
|
@@ -11387,7 +11440,8 @@ function SettingsContentInner({
|
|
|
11387
11440
|
plan: ws.plan,
|
|
11388
11441
|
ownerEmail: ws.ownerEmail,
|
|
11389
11442
|
isCurrent: ws.isCurrent,
|
|
11390
|
-
imageUrl: ws.imageUrl
|
|
11443
|
+
imageUrl: ws.imageUrl,
|
|
11444
|
+
organizationId: ws.organizationId
|
|
11391
11445
|
});
|
|
11392
11446
|
const asObject = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11393
11447
|
const getWorkspaceImage = (record) => {
|
|
@@ -11977,6 +12031,8 @@ function SettingsContentInner({
|
|
|
11977
12031
|
loading: workspaceListLoading && workspaceList.length === 0,
|
|
11978
12032
|
fetchPlans: workspace.fetchPlans,
|
|
11979
12033
|
organizations: listOrganizations,
|
|
12034
|
+
listPage: listWorkspacePage,
|
|
12035
|
+
reloadKey: displayWorkspaces.map((ws) => `${ws._id}:${ws.name}:${ws.role}`).join(","),
|
|
11980
12036
|
onCreateClick: handleCreateClick,
|
|
11981
12037
|
onEditClick: handleEditClick,
|
|
11982
12038
|
onSwitchClick: async (ws) => {
|