@rebasepro/admin 0.12.0 → 0.12.1-canary.g06f263c
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/README.md +1 -1
- package/dist/{CollectionEditorDialog-Dpc75wIZ.js → CollectionEditorDialog--VwQl4pk.js} +75 -62
- package/dist/{CollectionEditorDialog-Dpc75wIZ.js.map → CollectionEditorDialog--VwQl4pk.js.map} +1 -1
- package/dist/{PropertyEditView-DH3XZC2x.js → PropertyEditView-QaEYldcT.js} +12 -9
- package/dist/{PropertyEditView-DH3XZC2x.js.map → PropertyEditView-QaEYldcT.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-BZLq4MGp.js → RouterCollectionsStudioView-DvRSUk9o.js} +5 -5
- package/dist/{RouterCollectionsStudioView-BZLq4MGp.js.map → RouterCollectionsStudioView-DvRSUk9o.js.map} +1 -1
- package/dist/collection_editor/useLocalCollectionsConfigController.d.ts +8 -0
- package/dist/collection_editor_ui.js +5 -5
- package/dist/collection_editor_ui.js.map +1 -1
- package/dist/editor.js +18 -10
- package/dist/editor.js.map +1 -1
- package/dist/{export-BzIpUdSK.js → export-BZZfQkT5.js} +2 -2
- package/dist/{export-BzIpUdSK.js.map → export-BZZfQkT5.js.map} +1 -1
- package/dist/{history-g9688IaK.js → history-Ao1HBAK3.js} +2 -2
- package/dist/{history-g9688IaK.js.map → history-Ao1HBAK3.js.map} +1 -1
- package/dist/{import-BjUG-cIY.js → import-AFJRUo99.js} +2 -2
- package/dist/{import-BjUG-cIY.js.map → import-AFJRUo99.js.map} +1 -1
- package/dist/index.js +105 -34
- package/dist/index.js.map +1 -1
- package/dist/markdown-CklalUUk.js.map +1 -1
- package/dist/{util-B5fJm1FA.js → util-ByPlsapJ.js} +103 -61
- package/dist/util-ByPlsapJ.js.map +1 -0
- package/package.json +32 -34
- package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +65 -22
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +38 -30
- package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +1 -1
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +127 -15
- package/src/components/AdminModeSyncer.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +1 -1
- package/src/components/CollectionViewBinding/SplitListView.tsx +1 -1
- package/src/components/DefaultAppBar.tsx +1 -1
- package/src/components/DefaultDrawer.tsx +1 -1
- package/src/components/DetailViewBinding.tsx +1 -1
- package/src/components/DrawerNavigationItem.tsx +1 -1
- package/src/components/EditViewBinding.tsx +1 -1
- package/src/components/HomePage/FavouritesView.tsx +1 -1
- package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
- package/src/components/HomePage/SmallNavigationCard.tsx +1 -1
- package/src/components/RebaseLayout.tsx +1 -1
- package/src/components/RebaseNavigation.tsx +45 -5
- package/src/components/RebaseRouteDefs.tsx +1 -1
- package/src/components/SidePanelBinding.tsx +1 -1
- package/src/components/UserSelector.tsx +6 -5
- package/src/components/common/default_entity_actions.tsx +4 -3
- package/src/components/history/LastEditedByIndicator.tsx +4 -3
- package/src/hooks/navigation/useBuildUrlController.tsx +1 -1
- package/src/hooks/useBuildSideDialogsController.tsx +2 -2
- package/src/hooks/useBuildSidePanel.tsx +1 -1
- package/src/hooks/useHistory.ts +6 -5
- package/src/hooks/useResolvedUsers.ts +8 -5
- package/src/routes/RebaseRoute.tsx +2 -2
- package/dist/util-B5fJm1FA.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
|
|
4
|
-
import { Link, useNavigate } from "react-router
|
|
4
|
+
import { Link, useNavigate } from "react-router";
|
|
5
5
|
import { RebaseLogo, LanguageToggle } from "@rebasepro/app";
|
|
6
6
|
import { ErrorBoundary, iconSize } from "@rebasepro/ui";
|
|
7
7
|
import {
|
|
@@ -6,7 +6,7 @@ import { useUrlController } from "../hooks/navigation/contexts/UrlContext";
|
|
|
6
6
|
import { useNavigationStateController } from "../hooks/navigation/contexts/NavigationStateContext";
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
import { Link, useNavigate } from "react-router
|
|
9
|
+
import { Link, useNavigate } from "react-router";
|
|
10
10
|
import { AnalyticsEvent } from "@rebasepro/admin-types";
|
|
11
11
|
import {
|
|
12
12
|
Avatar,
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
} from "@rebasepro/app";
|
|
42
42
|
import { useUrlController } from "../hooks/navigation/contexts/UrlContext";
|
|
43
43
|
import { useCollectionRegistryController } from "../hooks/navigation/contexts/CollectionRegistryContext";
|
|
44
|
-
import { useNavigate } from "react-router
|
|
44
|
+
import { useNavigate } from "react-router";
|
|
45
45
|
import { getValueInPath } from "@rebasepro/utils";
|
|
46
46
|
import { getEntityTitlePropertyKeyForEntity, isUserSelectProperty, resolveTitleToString } from "../util/previews";
|
|
47
47
|
import { getUserLabel, useResolvedUser } from "../hooks/useResolvedUsers";
|
|
@@ -48,7 +48,7 @@ const EntityHistoryView = lazy(() => import("../components/history").then(m => (
|
|
|
48
48
|
import { createFormexStub, getEntityFromCache } from "@rebasepro/app";
|
|
49
49
|
import { usePermissions } from "@rebasepro/app";
|
|
50
50
|
import { useUrlController } from "../hooks/navigation/contexts/UrlContext";
|
|
51
|
-
import { useNavigate } from "react-router
|
|
51
|
+
import { useNavigate } from "react-router";
|
|
52
52
|
|
|
53
53
|
import { MAIN_TAB_VALUE, JSON_TAB_VALUE, HISTORY_TAB_VALUE } from "../util/view_constants";
|
|
54
54
|
export { MAIN_TAB_VALUE, JSON_TAB_VALUE, HISTORY_TAB_VALUE };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NavigationEntry } from "@rebasepro/admin-types";
|
|
2
|
-
import { useNavigate } from "react-router
|
|
2
|
+
import { useNavigate } from "react-router";
|
|
3
3
|
;
|
|
4
4
|
import { Chip, Collapse, StarIcon } from "@rebasepro/ui";
|
|
5
5
|
import { useUserConfigurationPersistence } from "@rebasepro/app";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import type { NavigationEntry, AdminCollection } from "@rebasepro/admin-types";
|
|
3
|
-
import { useNavigate } from "react-router
|
|
3
|
+
import { useNavigate } from "react-router";
|
|
4
4
|
;
|
|
5
5
|
import { IconButton, StarIcon, Tooltip } from "@rebasepro/ui";
|
|
6
6
|
import { NavigationCard } from "./NavigationCard";
|
|
@@ -38,6 +38,7 @@ import { SidePanelProvider } from "./SidePanelProvider";
|
|
|
38
38
|
// Collection editor internals — used when collectionEditor is enabled
|
|
39
39
|
import { useLocalCollectionsConfigController } from "../collection_editor/useLocalCollectionsConfigController";
|
|
40
40
|
import { ConfigControllerProvider } from "../collection_editor/ConfigControllerProvider";
|
|
41
|
+
import { useCollectionsConfigController } from "../collection_editor/useCollectionsConfigController";
|
|
41
42
|
|
|
42
43
|
// Lazy-load the schema view — only fetched when studio schema tool is active
|
|
43
44
|
const CollectionsStudioView = lazy(() =>
|
|
@@ -168,12 +169,19 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
168
169
|
}
|
|
169
170
|
}, [dataSources]);
|
|
170
171
|
|
|
172
|
+
// `readOnly` is left undefined unless the developer set it: the controller
|
|
173
|
+
// asks the backend whether its schema editor will accept a write, which is
|
|
174
|
+
// the only process that knows. Deciding it here from `process.env.NODE_ENV`
|
|
175
|
+
// — the *frontend bundle's* build mode — is what made the editor offer
|
|
176
|
+
// itself against production backends, `baas` projects and servers without
|
|
177
|
+
// `ts-morph`, and turned every save into a bare 404.
|
|
171
178
|
const internalConfigController = useLocalCollectionsConfigController(
|
|
172
179
|
rebaseClient,
|
|
173
180
|
resolvedCollections,
|
|
174
181
|
collectionEditorEnabled ? {
|
|
175
|
-
readOnly: collectionEditorOptions?.readOnly
|
|
176
|
-
getAuthToken: collectionEditorOptions?.getAuthToken ?? authController?.getAuthToken
|
|
182
|
+
readOnly: collectionEditorOptions?.readOnly,
|
|
183
|
+
getAuthToken: collectionEditorOptions?.getAuthToken ?? authController?.getAuthToken,
|
|
184
|
+
authKey: authController?.user?.uid ?? null
|
|
177
185
|
} : { readOnly: true }
|
|
178
186
|
);
|
|
179
187
|
|
|
@@ -189,13 +197,22 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
189
197
|
group: "Database",
|
|
190
198
|
icon: "LayoutList",
|
|
191
199
|
nestedRoutes: true,
|
|
200
|
+
// Reads the controller from context at render time rather than
|
|
201
|
+
// capturing it here. A view is a React *element*, and the
|
|
202
|
+
// navigation state controller resolves the view list once and holds
|
|
203
|
+
// on to the elements it resolved — so a controller passed as a prop
|
|
204
|
+
// is frozen at whatever it was on the first render, forever. That
|
|
205
|
+
// hid every later change: the editor stayed writable after the
|
|
206
|
+
// backend said it was read-only, and the collection list never
|
|
207
|
+
// refreshed. Everything else in the editor already reads this from
|
|
208
|
+
// `ConfigControllerProvider`, which wraps this subtree.
|
|
192
209
|
view: (
|
|
193
210
|
<Suspense fallback={<CircularProgressCenter/>}>
|
|
194
|
-
<
|
|
211
|
+
<CollectionsStudioViewFromContext/>
|
|
195
212
|
</Suspense>
|
|
196
213
|
)
|
|
197
214
|
};
|
|
198
|
-
}, [collectionEditorEnabled, registry.studioConfig
|
|
215
|
+
}, [collectionEditorEnabled, registry.studioConfig]);
|
|
199
216
|
|
|
200
217
|
const devViews = useMemo(() => {
|
|
201
218
|
const base = registry.studioConfig?.devViews ?? [];
|
|
@@ -275,6 +292,7 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
275
292
|
collectionRegistryController={collectionRegistryController}
|
|
276
293
|
urlController={urlController}
|
|
277
294
|
navigationStateController={navigationStateController}
|
|
295
|
+
canWriteToCodebase={!internalConfigController.readOnly}
|
|
278
296
|
/>
|
|
279
297
|
{children}
|
|
280
298
|
</SidePanelProvider>
|
|
@@ -301,6 +319,17 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
301
319
|
return navigationContent;
|
|
302
320
|
}
|
|
303
321
|
|
|
322
|
+
/**
|
|
323
|
+
* The injected "Edit collections" view, bound to the live config controller.
|
|
324
|
+
*
|
|
325
|
+
* Exists so the element stored in the navigation state does not close over a
|
|
326
|
+
* controller instance — see the comment at its construction site.
|
|
327
|
+
*/
|
|
328
|
+
function CollectionsStudioViewFromContext() {
|
|
329
|
+
const configController = useCollectionsConfigController();
|
|
330
|
+
return <CollectionsStudioView configController={configController}/>;
|
|
331
|
+
}
|
|
332
|
+
|
|
304
333
|
/**
|
|
305
334
|
* Internal component that auto-registers admin controllers into the
|
|
306
335
|
* self-assembling Studio bridge. Must be rendered inside both the
|
|
@@ -309,14 +338,25 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
309
338
|
function BridgeAutoRegistrar({
|
|
310
339
|
collectionRegistryController,
|
|
311
340
|
urlController,
|
|
312
|
-
navigationStateController
|
|
341
|
+
navigationStateController,
|
|
342
|
+
canWriteToCodebase
|
|
313
343
|
}: {
|
|
314
344
|
collectionRegistryController: CollectionRegistryController;
|
|
315
345
|
urlController: UrlController;
|
|
316
346
|
navigationStateController: NavigationStateController;
|
|
347
|
+
canWriteToCodebase: boolean;
|
|
317
348
|
}) {
|
|
318
349
|
useBridgeRegistration("collectionRegistry", collectionRegistryController);
|
|
319
350
|
useBridgeRegistration("urlController", urlController);
|
|
320
351
|
useBridgeRegistration("navigationState", navigationStateController);
|
|
352
|
+
|
|
353
|
+
// Studio tools that would write into the project's collection source — the
|
|
354
|
+
// RLS editor's policy save for a mapped table, its "Import to codebase" —
|
|
355
|
+
// ask the bridge whether that write can land. The default is `true`, which
|
|
356
|
+
// is what an admin panel sitting next to its own `collectionsDir` assumes;
|
|
357
|
+
// it is wrong for exactly the same backends that make the collection editor
|
|
358
|
+
// read-only, so the two now answer from one source.
|
|
359
|
+
const capabilities = useMemo(() => ({ codebase: canWriteToCodebase }), [canWriteToCodebase]);
|
|
360
|
+
useBridgeRegistration("capabilities", capabilities);
|
|
321
361
|
return null;
|
|
322
362
|
}
|
|
@@ -8,7 +8,7 @@ import { IconButton, Maximize2Icon, XIcon } from "@rebasepro/ui";
|
|
|
8
8
|
import { EditViewBinding } from "./EditViewBinding";
|
|
9
9
|
import { DetailViewBinding } from "./DetailViewBinding";
|
|
10
10
|
import { useSideDialogContext } from "./SideDialogs";
|
|
11
|
-
import { useNavigate } from "react-router
|
|
11
|
+
import { useNavigate } from "react-router";
|
|
12
12
|
import { saveEntityToMemoryCache, useComponentOverride } from "@rebasepro/app";
|
|
13
13
|
import { useCollectionRegistryController } from "../hooks/navigation/contexts/CollectionRegistryContext";
|
|
14
14
|
import { useSidePanel } from "../hooks/useSidePanel";
|
|
@@ -21,7 +21,7 @@ import * as React from "react";
|
|
|
21
21
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
22
22
|
import { Command as CommandPrimitive } from "cmdk";
|
|
23
23
|
import { User } from "@rebasepro/types";
|
|
24
|
-
import { useRebaseClient, useAuthController, UserDisplay } from "@rebasepro/app";
|
|
24
|
+
import { apiBaseOf, useRebaseClient, useAuthController, UserDisplay } from "@rebasepro/app";
|
|
25
25
|
import { EmptyValue } from "../preview";
|
|
26
26
|
|
|
27
27
|
interface UserSelectorItem {
|
|
@@ -37,7 +37,7 @@ const DEFAULT_PAGE_SIZE = 10;
|
|
|
37
37
|
* Local hook to fetch users from the collection REST API with search and pagination.
|
|
38
38
|
*/
|
|
39
39
|
function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE }: { pageSize?: number } = {}) {
|
|
40
|
-
const client = useRebaseClient<{ baseUrl?: string }>();
|
|
40
|
+
const client = useRebaseClient<{ baseUrl?: string, apiPath?: string }>();
|
|
41
41
|
const { getAuthToken } = useAuthController();
|
|
42
42
|
|
|
43
43
|
const [items, setItems] = useState<UserSelectorItem[]>([]);
|
|
@@ -48,7 +48,8 @@ function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE }: { pageSize?: number }
|
|
|
48
48
|
const userCacheRef = useRef<Map<string, User>>(new Map());
|
|
49
49
|
|
|
50
50
|
const fetchUsers = useCallback(async (searchStr: string, offset: number, append: boolean) => {
|
|
51
|
-
|
|
51
|
+
const apiBase = apiBaseOf(client);
|
|
52
|
+
if (!apiBase) return;
|
|
52
53
|
setIsLoading(true);
|
|
53
54
|
try {
|
|
54
55
|
const token = await getAuthToken?.();
|
|
@@ -57,7 +58,7 @@ function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE }: { pageSize?: number }
|
|
|
57
58
|
offset: String(offset)
|
|
58
59
|
});
|
|
59
60
|
if (searchStr) params.set("search", searchStr);
|
|
60
|
-
const response = await fetch(`${
|
|
61
|
+
const response = await fetch(`${apiBase}/users?${params}`, {
|
|
61
62
|
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
62
63
|
});
|
|
63
64
|
if (!response.ok) throw new Error("Failed to fetch users");
|
|
@@ -86,7 +87,7 @@ user };
|
|
|
86
87
|
} finally {
|
|
87
88
|
setIsLoading(false);
|
|
88
89
|
}
|
|
89
|
-
}, [client?.baseUrl, getAuthToken, pageSize]);
|
|
90
|
+
}, [client?.baseUrl, client?.apiPath, getAuthToken, pageSize]);
|
|
90
91
|
|
|
91
92
|
useEffect(() => {
|
|
92
93
|
offsetRef.current = 0;
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import type { User } from "@rebasepro/types";
|
|
22
22
|
import type { EntityAction, UserCreationResult } from "@rebasepro/admin-types";
|
|
23
23
|
import {
|
|
24
|
+
apiBaseOf,
|
|
24
25
|
useAuthController,
|
|
25
26
|
useRebaseClient,
|
|
26
27
|
useSnackbarController,
|
|
@@ -246,7 +247,7 @@ export function ResetPasswordActionDialog({
|
|
|
246
247
|
open: boolean;
|
|
247
248
|
onClose: () => void;
|
|
248
249
|
}) {
|
|
249
|
-
const client = useRebaseClient<{ baseUrl?: string }>();
|
|
250
|
+
const client = useRebaseClient<{ baseUrl?: string, apiPath?: string }>();
|
|
250
251
|
const { getAuthToken } = useAuthController();
|
|
251
252
|
const snackbarController = useSnackbarController();
|
|
252
253
|
const { t } = useTranslation();
|
|
@@ -270,9 +271,9 @@ export function ResetPasswordActionDialog({
|
|
|
270
271
|
setLoading(true);
|
|
271
272
|
setError(null);
|
|
272
273
|
try {
|
|
273
|
-
const
|
|
274
|
+
const apiBase = apiBaseOf(client) ?? "";
|
|
274
275
|
const token = await getAuthToken?.();
|
|
275
|
-
const response = await fetch(`${
|
|
276
|
+
const response = await fetch(`${apiBase}/admin/users/${user.uid}/reset-password`, {
|
|
276
277
|
method: "POST",
|
|
277
278
|
headers: {
|
|
278
279
|
"Content-Type": "application/json",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useEffect, useState, useCallback } from "react";
|
|
3
3
|
;
|
|
4
|
-
import { useApiConfig } from "@rebasepro/app";
|
|
4
|
+
import { useApiBase, useApiConfig } from "@rebasepro/app";
|
|
5
5
|
import { useAuthController } from "@rebasepro/app";
|
|
6
6
|
import { HistoryEntryData } from "../../hooks";
|
|
7
7
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
@@ -35,6 +35,7 @@ export function LastEditedByIndicator({
|
|
|
35
35
|
collection: AdminCollection;
|
|
36
36
|
}) {
|
|
37
37
|
const apiConfig = useApiConfig();
|
|
38
|
+
const apiBase = useApiBase();
|
|
38
39
|
const authController = useAuthController();
|
|
39
40
|
const [latestEntry, setLatestEntry] = useState<HistoryEntryData | undefined>();
|
|
40
41
|
|
|
@@ -46,7 +47,7 @@ export function LastEditedByIndicator({
|
|
|
46
47
|
const headers: Record<string, string> = {};
|
|
47
48
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
48
49
|
|
|
49
|
-
const url = `${
|
|
50
|
+
const url = `${apiBase}/data/${collection.slug}/${entityId}/history?limit=1&offset=0`;
|
|
50
51
|
const response = await fetch(url, { headers });
|
|
51
52
|
|
|
52
53
|
if (response.ok) {
|
|
@@ -59,7 +60,7 @@ export function LastEditedByIndicator({
|
|
|
59
60
|
// Silently fail — this is a non-critical UI indicator
|
|
60
61
|
console.debug("Failed to fetch latest history entry:", error);
|
|
61
62
|
}
|
|
62
|
-
}, [apiConfig, entityId, collection.slug]);
|
|
63
|
+
}, [apiConfig, apiBase, entityId, collection.slug]);
|
|
63
64
|
|
|
64
65
|
useEffect(() => {
|
|
65
66
|
fetchLatest();
|
|
@@ -2,7 +2,7 @@ import type { UrlController, NavigateOptions } from "@rebasepro/admin-types";
|
|
|
2
2
|
import { useCallback, useMemo } from "react";
|
|
3
3
|
import { removeInitialAndTrailingSlashes, resolveCollectionPathIds } from "@rebasepro/app";
|
|
4
4
|
import { CollectionRegistryController } from "@rebasepro/types";
|
|
5
|
-
import { useNavigate } from "react-router
|
|
5
|
+
import { useNavigate } from "react-router";
|
|
6
6
|
|
|
7
7
|
export function useBuildUrlController(props: {
|
|
8
8
|
basePath: string,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { SideDialogPanelProps, SideDialogsController } from "../hooks/useSideDialogsController";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState, useMemo } from "react";
|
|
3
|
-
import { useLocation, useNavigate } from "react-router
|
|
3
|
+
import { useLocation, useNavigate } from "react-router";
|
|
4
4
|
;
|
|
5
5
|
import { deepEqual as equal } from "fast-equals"
|
|
6
|
-
import { Location } from "react-router
|
|
6
|
+
import { Location } from "react-router";
|
|
7
7
|
|
|
8
8
|
interface SideDialogLocationState {
|
|
9
9
|
base_location?: Location;
|
|
@@ -4,7 +4,7 @@ import type { CustomizationController, SidePanelBindingProps, SideDialogPanelPro
|
|
|
4
4
|
import { useCallback, useEffect, useRef, useMemo } from "react";
|
|
5
5
|
import { CollectionRegistryController } from "@rebasepro/types";
|
|
6
6
|
import { AuthController } from "@rebasepro/admin-types";
|
|
7
|
-
import { useLocation } from "react-router
|
|
7
|
+
import { useLocation } from "react-router";
|
|
8
8
|
import { NavigationViewInternal } from "@rebasepro/app";
|
|
9
9
|
import { getNavigationEntriesFromPath, removeInitialAndTrailingSlashes, removeTrailingSlash, resolveDefaultSelectedView } from "@rebasepro/app";
|
|
10
10
|
import { resolvedSelectedEntityView } from "../util/resolutions";
|
package/src/hooks/useHistory.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState, useMemo } from "react";
|
|
2
|
-
import { useApiConfig } from "@rebasepro/app";
|
|
2
|
+
import { useApiBase, useApiConfig } from "@rebasepro/app";
|
|
3
3
|
import type { EntityHistoryEntry } from "@rebasepro/types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -31,6 +31,7 @@ export function useHistory(params: {
|
|
|
31
31
|
}): UseEntityHistoryResult {
|
|
32
32
|
const { slug, entityId, enabled = true, pageSize = 10 } = params;
|
|
33
33
|
const apiConfig = useApiConfig();
|
|
34
|
+
const apiBase = useApiBase();
|
|
34
35
|
|
|
35
36
|
const [entries, setEntries] = useState<HistoryEntryData[]>([]);
|
|
36
37
|
const [total, setTotal] = useState(0);
|
|
@@ -59,7 +60,7 @@ export function useHistory(params: {
|
|
|
59
60
|
};
|
|
60
61
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
61
62
|
|
|
62
|
-
const url = `${
|
|
63
|
+
const url = `${apiBase}/data/${slug}/${entityId}/history?limit=${pageSize}&offset=${currentOffset}`;
|
|
63
64
|
const response = await fetch(url, { headers,
|
|
64
65
|
signal });
|
|
65
66
|
|
|
@@ -90,7 +91,7 @@ signal });
|
|
|
90
91
|
} finally {
|
|
91
92
|
setIsLoading(false);
|
|
92
93
|
}
|
|
93
|
-
}, [apiConfig, slug, entityId, enabled, pageSize]);
|
|
94
|
+
}, [apiConfig, apiBase, slug, entityId, enabled, pageSize]);
|
|
94
95
|
|
|
95
96
|
// Single unified effect: reset state when entity changes, fetch when offset changes.
|
|
96
97
|
// Uses AbortController to cancel stale requests on entity change or unmount.
|
|
@@ -136,7 +137,7 @@ signal });
|
|
|
136
137
|
};
|
|
137
138
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
138
139
|
|
|
139
|
-
const url = `${
|
|
140
|
+
const url = `${apiBase}/data/${slug}/${entityId}/history/${historyId}/revert`;
|
|
140
141
|
const response = await fetch(url, { method: "POST",
|
|
141
142
|
headers });
|
|
142
143
|
|
|
@@ -154,7 +155,7 @@ headers });
|
|
|
154
155
|
|
|
155
156
|
// Return the reverted entity data so callers can update the form
|
|
156
157
|
return result.data as Record<string, unknown>;
|
|
157
|
-
}, [apiConfig, slug, entityId]);
|
|
158
|
+
}, [apiConfig, apiBase, slug, entityId]);
|
|
158
159
|
|
|
159
160
|
return useMemo(() => ({
|
|
160
161
|
entries,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import type { User } from "@rebasepro/types";
|
|
3
|
-
import { useAuthController, useRebaseClient } from "@rebasepro/app";
|
|
3
|
+
import { apiBaseOf, useAuthController, useRebaseClient } from "@rebasepro/app";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Resolves the auth users behind `userSelect` values.
|
|
@@ -31,12 +31,12 @@ queue: new Set() };
|
|
|
31
31
|
return cache;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
async function fetchUsers(
|
|
34
|
+
async function fetchUsers(apiBase: string, ids: string[], token: string | undefined): Promise<Map<string, User | null>> {
|
|
35
35
|
const resolved = new Map<string, User | null>();
|
|
36
36
|
for (const id of ids) resolved.set(id, null);
|
|
37
37
|
|
|
38
38
|
const params = new URLSearchParams({ ids: ids.join(",") });
|
|
39
|
-
const response = await fetch(`${
|
|
39
|
+
const response = await fetch(`${apiBase}/users?${params}`, {
|
|
40
40
|
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
41
41
|
});
|
|
42
42
|
if (!response.ok) {
|
|
@@ -103,9 +103,12 @@ function requestUser(
|
|
|
103
103
|
* when it cannot be resolved.
|
|
104
104
|
*/
|
|
105
105
|
export function useResolvedUser(uid: string | undefined): User | null {
|
|
106
|
-
const client = useRebaseClient<{ baseUrl?: string }>();
|
|
106
|
+
const client = useRebaseClient<{ baseUrl?: string, apiPath?: string }>();
|
|
107
107
|
const { getAuthToken } = useAuthController();
|
|
108
|
-
|
|
108
|
+
/* The API prefix, not the bare origin: it keys the cache and is what a
|
|
109
|
+
request is built from, and a backend with a non-default `basePath`
|
|
110
|
+
serves neither at `/api`. */
|
|
111
|
+
const baseUrl = apiBaseOf(client);
|
|
109
112
|
|
|
110
113
|
const [user, setUser] = useState<User | null>(() =>
|
|
111
114
|
(baseUrl && uid) ? getCache(baseUrl).users.get(uid) ?? null : null);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
import type { ViewMode, AdminCollection } from "@rebasepro/admin-types";
|
|
3
|
-
import { useLocation } from "react-router
|
|
3
|
+
import { useLocation } from "react-router";
|
|
4
4
|
import { EditViewBinding } from "../components/EditViewBinding";
|
|
5
5
|
import { DetailViewBinding } from "../components/DetailViewBinding";
|
|
6
6
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
7
|
-
import { useNavigate } from "react-router
|
|
7
|
+
import { useNavigate } from "react-router";
|
|
8
8
|
import { CollectionViewBinding } from "../components/CollectionViewBinding/CollectionViewBinding";
|
|
9
9
|
import { NotFoundPage, useUserConfigurationPersistence, useComponentOverride, useNavigationBlocker } from "@rebasepro/app";
|
|
10
10
|
import { resolveOpenEntityMode, resolveViewMode } from "../util/view_mode";
|