@rebasepro/app 0.12.1-canary.gf5f1d39 → 0.13.0
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/auth/api.d.ts +3 -8
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/types.d.ts +1 -5
- package/dist/collections/form-layout.d.ts +107 -0
- package/dist/collections/index.d.ts +1 -0
- package/dist/collections/title-property.d.ts +22 -0
- package/dist/components/Debug/collection-views/sample_data.d.ts +3 -2
- package/dist/hooks/ApiConfigContext.d.ts +36 -2
- package/dist/hooks/useBackendStorageSource.d.ts +6 -1
- package/dist/hooks/useNavigationBlocker.d.ts +1 -1
- package/dist/hooks/useSnackbarController.d.ts +2 -1
- package/dist/hooks/useStudioBridge.d.ts +27 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +681 -186
- package/dist/index.es.js.map +1 -1
- package/dist/internal/common.d.ts +14 -2
- package/package.json +29 -25
- package/src/auth/api.ts +8 -9
- package/src/auth/index.ts +2 -2
- package/src/auth/types.ts +5 -6
- package/src/collections/form-layout.ts +386 -0
- package/src/collections/index.ts +1 -0
- package/src/collections/title-property.ts +41 -0
- package/src/components/Debug/UIReferenceView.tsx +9 -1
- package/src/components/Debug/collection-views/sample_data.ts +4 -2
- package/src/components/NotFoundPage.tsx +1 -1
- package/src/components/common/useDataTableController.tsx +1 -1
- package/src/core/Rebase.tsx +5 -1
- package/src/core/RebaseRouter.tsx +2 -1
- package/src/core/RebaseRoutes.tsx +1 -1
- package/src/hooks/ApiConfigContext.tsx +45 -3
- package/src/hooks/useAuthSubscription.ts +29 -3
- package/src/hooks/useBackendStorageSource.ts +8 -1
- package/src/hooks/useNavigationBlocker.tsx +1 -1
- package/src/hooks/useSnackbarController.tsx +34 -6
- package/src/hooks/useStudioBridge.tsx +50 -2
- package/src/index.ts +1 -1
- package/src/internal/common.tsx +15 -3
- package/src/internal/useRestoreScroll.tsx +1 -1
- package/src/locales/de.ts +23 -2
- package/src/locales/en.ts +23 -1
- package/src/locales/es.ts +23 -1
- package/src/locales/fr.ts +23 -1
- package/src/locales/hi.ts +23 -1
- package/src/locales/it.ts +23 -1
- package/src/locales/pt.ts +23 -1
- package/src/util/enums.ts +4 -2
- package/src/util/useStorageUploadController.tsx +5 -1
package/dist/index.es.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React, { createContext, lazy, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Alert, AlertCircleIcon, AlertTriangleIcon, AppWindow, ArrowDownIcon, ArrowLeftIcon, ArrowRightLeftIcon, ArrowUpDownIcon, ArrowUpIcon, Avatar, BooleanSwitch, Button,
|
|
2
|
+
import { Alert, AlertCircleIcon, AlertTriangleIcon, AppWindow, ArrowDownIcon, ArrowLeftIcon, ArrowRightLeftIcon, ArrowUpDownIcon, ArrowUpIcon, Avatar, BooleanSwitch, Button, CalendarIcon, Card, CenteredView, CheckCircle2Icon, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, Chip, CircleDotIcon, CircleUserIcon, CircularProgress, ColumnsIcon, Container, Dialog, DialogActions, DialogContent, DialogTitle, DollarSignIcon, ErrorBoundary, FileIcon, FileTextIcon, FilterChip, FilterIcon, FolderIcon, FolderKanbanIcon, IconButton, KanbanIcon, KanbanView, LanguagesIcon, LayoutGridIcon, ListIcon, ListPlusIcon, ListTodoIcon, LoadingButton, LogOutIcon, MailIcon, Menu, MenuItem, MessageCircleIcon, MoonIcon, MultiSelect, MultiSelectItem, PanelLeftIcon, Paper, PenLineIcon, PencilIcon, PhoneIcon, PinIcon, PlusIcon, Popover, RefreshCwIcon, SearchBar, Select, SelectItem, Separator, SettingsIcon, Skeleton, StickyNoteIcon, SunIcon, SunMoonIcon, Tab, Table, TableBody, TableCell, TableHeader, TableRow, Tabs, TagIcon, TextField, ToggleButtonGroup, Tooltip, Trash2Icon, TrendingUpIcon, TypeIcon, Typography, UserIcon, UserPlus, UsersIcon, VideoIcon, Wand2Icon, WrenchIcon, XIcon, cls, colorClassesMapping, coolIconKeys, defaultBorderMixin, getColorSchemeForKey, getColorSchemeForSeed, iconKeys, iconSize, lucideIcons } from "@rebasepro/ui";
|
|
3
3
|
import { ALL_WHERE_FILTER_OPS, DEFAULT_DATA_SOURCE_KEY, DEFAULT_FILTERABLE_RELATION_KINDS, DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, RebaseApiError, Vector, getDataSourceCapabilities, isLazyComponentRef, isRelationalCollectionConfig } from "@rebasepro/types";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack";
|
|
6
6
|
import { buildRebaseData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, evaluateCondition, getEntityChildViews, getLabelOrConfigFrom, getPrimaryKeys, getSubcollections, isPropertyBuilder, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, stripCollectionPath, wrapAsEntityData, wrapAsSdkData } from "@rebasepro/common";
|
|
7
7
|
import { getTitlePropertyKey as getTitlePropertyKey$1, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1 } from "@rebasepro/app";
|
|
8
|
-
import { Link,
|
|
8
|
+
import { Link, Routes, createBrowserRouter, useBlocker, useLocation } from "react-router";
|
|
9
9
|
import { generateForeignKeyName, hashString, isObject, isPlainObject, mergeDeep, randomString, slugify } from "@rebasepro/utils";
|
|
10
10
|
import { I18nextProvider, initReactI18next, useTranslation as useTranslation$1 } from "react-i18next";
|
|
11
11
|
import { deepEqual } from "fast-equals";
|
|
12
12
|
import i18next from "i18next";
|
|
13
|
+
import { RouterProvider } from "react-router/dom";
|
|
13
14
|
import Fuse from "fuse.js";
|
|
14
15
|
import Compressor from "compressorjs";
|
|
15
16
|
import { resolveAdminCollection } from "@rebasepro/admin-types";
|
|
@@ -162,12 +163,15 @@ var DialogsProvider = ({ children }) => {
|
|
|
162
163
|
};
|
|
163
164
|
const close = useCallback(() => {
|
|
164
165
|
if (dialogEntries.length === 0) return;
|
|
165
|
-
|
|
166
|
+
const updatedPanels = [...dialogEntriesRef.current.slice(0, -1)];
|
|
167
|
+
updateDialogEntries(updatedPanels);
|
|
166
168
|
}, [dialogEntries]);
|
|
167
169
|
const open = useCallback((dialogEntry) => {
|
|
168
|
-
|
|
170
|
+
const updatedPanels = [...dialogEntriesRef.current, dialogEntry];
|
|
171
|
+
updateDialogEntries(updatedPanels);
|
|
169
172
|
return { closeDialog: () => {
|
|
170
|
-
|
|
173
|
+
const updatedPanels = dialogEntriesRef.current.filter((e) => e.key !== dialogEntry.key);
|
|
174
|
+
updateDialogEntries(updatedPanels);
|
|
171
175
|
} };
|
|
172
176
|
}, [dialogEntries]);
|
|
173
177
|
const dialogsController = useMemo(() => ({
|
|
@@ -624,24 +628,37 @@ var useStorageSource = () => {
|
|
|
624
628
|
var useSnackbarController = () => {
|
|
625
629
|
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
|
626
630
|
const open = useCallback((props) => {
|
|
627
|
-
const { type, message, autoHideDuration } = props;
|
|
631
|
+
const { type, message, autoHideDuration, action } = props;
|
|
628
632
|
enqueueSnackbar({
|
|
629
633
|
message: props.title ? /* @__PURE__ */ jsxs("div", {
|
|
630
634
|
className: "flex flex-col",
|
|
631
635
|
children: [/* @__PURE__ */ jsx("strong", { children: props.title }), message]
|
|
632
636
|
}) : message,
|
|
633
637
|
variant: type,
|
|
634
|
-
autoHideDuration
|
|
638
|
+
autoHideDuration,
|
|
639
|
+
...action ? { key: nextActionKey() } : {},
|
|
640
|
+
action: action ? (key) => /* @__PURE__ */ jsx(Button, {
|
|
641
|
+
variant: "text",
|
|
642
|
+
size: "small",
|
|
643
|
+
className: "text-inherit",
|
|
644
|
+
onClick: () => {
|
|
645
|
+
action.onClick();
|
|
646
|
+
closeSnackbar(key);
|
|
647
|
+
},
|
|
648
|
+
children: action.label
|
|
649
|
+
}) : void 0
|
|
635
650
|
});
|
|
636
|
-
}, []);
|
|
651
|
+
}, [enqueueSnackbar, closeSnackbar]);
|
|
637
652
|
const close = useCallback(() => {
|
|
638
653
|
closeSnackbar();
|
|
639
|
-
}, []);
|
|
654
|
+
}, [closeSnackbar]);
|
|
640
655
|
return useMemo(() => ({
|
|
641
656
|
open,
|
|
642
657
|
close
|
|
643
658
|
}), [open, close]);
|
|
644
659
|
};
|
|
660
|
+
var actionKeySeq = 0;
|
|
661
|
+
var nextActionKey = () => `rebase-snackbar-action-${actionKeySeq++}`;
|
|
645
662
|
//#endregion
|
|
646
663
|
//#region src/hooks/useUserConfigurationPersistence.tsx
|
|
647
664
|
/**
|
|
@@ -908,7 +925,8 @@ function useRelationSelector({ path, collection, fixedFilter, pageSize = DEFAULT
|
|
|
908
925
|
setLoading(true);
|
|
909
926
|
const whereParams = fixedFilter && Object.keys(fixedFilter).length > 0 ? fixedFilter : void 0;
|
|
910
927
|
const onEntitiesUpdate = (res) => {
|
|
911
|
-
|
|
928
|
+
const newItems = res.data.map((e) => entityToRelationItem(e));
|
|
929
|
+
setItems(newItems);
|
|
912
930
|
setHasMore(res.meta.hasMore);
|
|
913
931
|
setLoading(false);
|
|
914
932
|
};
|
|
@@ -1201,14 +1219,38 @@ function useUnsavedChangesDialog(when, onOk) {
|
|
|
1201
1219
|
function useAuthSubscription(authClient) {
|
|
1202
1220
|
const currentSession = authClient?.getSession();
|
|
1203
1221
|
const [user, setUser] = useState(currentSession?.user ?? null);
|
|
1204
|
-
const [initialLoading, setInitialLoading] = useState(!currentSession);
|
|
1205
1222
|
const [authLoading, setAuthLoading] = useState(false);
|
|
1206
1223
|
const [authError, setAuthError] = useState();
|
|
1207
1224
|
const [loginSkipped, setLoginSkipped] = useState(false);
|
|
1208
1225
|
const [extra, setExtra] = useState();
|
|
1226
|
+
/**
|
|
1227
|
+
* Whether asking the server "who am I?" could possibly answer.
|
|
1228
|
+
*
|
|
1229
|
+
* A client configured with `persistSession: false` and JSON auth — the
|
|
1230
|
+
* shape used when one client borrows another's credential, as the hosted
|
|
1231
|
+
* Studio does — has no stored session and no auth cookie, so the probe
|
|
1232
|
+
* below is guaranteed to 401. Sending it anyway put three failing
|
|
1233
|
+
* `GET /auth/me` calls on the wire per mount, each one landing in the
|
|
1234
|
+
* *customer's* request log as an authentication failure against their own
|
|
1235
|
+
* backend.
|
|
1236
|
+
*
|
|
1237
|
+
* A client that does not implement the capability is treated as "might
|
|
1238
|
+
* work" — the historical behaviour.
|
|
1239
|
+
*/
|
|
1240
|
+
const mayHaveRestorableSession = authClient?.canRestoreSession?.() ?? true;
|
|
1241
|
+
/**
|
|
1242
|
+
* `true` only while a probe is genuinely outstanding.
|
|
1243
|
+
*
|
|
1244
|
+
* It used to be seeded from `!currentSession` alone, which left a client
|
|
1245
|
+
* that cannot restore a session — and therefore never runs the probe that
|
|
1246
|
+
* clears the flag — reporting "still loading" forever. Anything gated on
|
|
1247
|
+
* `initialLoading` (a spinner, a redirect to the login view) would never
|
|
1248
|
+
* resolve.
|
|
1249
|
+
*/
|
|
1250
|
+
const [initialLoading, setInitialLoading] = useState(!currentSession && mayHaveRestorableSession);
|
|
1209
1251
|
useEffect(() => {
|
|
1210
1252
|
if (!authClient) return;
|
|
1211
|
-
if (!currentSession) {
|
|
1253
|
+
if (!currentSession && mayHaveRestorableSession) {
|
|
1212
1254
|
setInitialLoading(true);
|
|
1213
1255
|
authClient.getUser().then((user) => {
|
|
1214
1256
|
if (user) setUser(user);
|
|
@@ -1216,7 +1258,11 @@ function useAuthSubscription(authClient) {
|
|
|
1216
1258
|
setInitialLoading(false);
|
|
1217
1259
|
});
|
|
1218
1260
|
}
|
|
1219
|
-
}, [
|
|
1261
|
+
}, [
|
|
1262
|
+
authClient,
|
|
1263
|
+
currentSession,
|
|
1264
|
+
mayHaveRestorableSession
|
|
1265
|
+
]);
|
|
1220
1266
|
useEffect(() => {
|
|
1221
1267
|
if (!authClient) return;
|
|
1222
1268
|
return authClient.onAuthStateChange((event, session) => {
|
|
@@ -1751,6 +1797,60 @@ function useRebaseRegistryDispatch() {
|
|
|
1751
1797
|
return dispatch;
|
|
1752
1798
|
}
|
|
1753
1799
|
//#endregion
|
|
1800
|
+
//#region src/hooks/ApiConfigContext.tsx
|
|
1801
|
+
/** What the server uses when `basePath` is not configured. */
|
|
1802
|
+
var DEFAULT_API_PATH = "/api";
|
|
1803
|
+
var ApiConfigContext = React.createContext(void 0);
|
|
1804
|
+
/**
|
|
1805
|
+
* Read the API config from context. Returns `undefined` if no provider is present,
|
|
1806
|
+
* allowing hooks to fall back to their own props.
|
|
1807
|
+
*/
|
|
1808
|
+
function useApiConfig() {
|
|
1809
|
+
return useContext(ApiConfigContext);
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* `apiUrl` and `apiPath` joined, with no trailing slash — the prefix to build a
|
|
1813
|
+
* request URL from.
|
|
1814
|
+
*
|
|
1815
|
+
* Returns `undefined` when there is no provider, so a caller can keep its own
|
|
1816
|
+
* fallback rather than silently requesting a relative path.
|
|
1817
|
+
*/
|
|
1818
|
+
function useApiBase() {
|
|
1819
|
+
const config = useApiConfig();
|
|
1820
|
+
if (!config?.apiUrl) return void 0;
|
|
1821
|
+
return config.apiUrl.replace(/\/+$/, "") + (config.apiPath || "/api");
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* The same prefix, derived from a client rather than from context.
|
|
1825
|
+
*
|
|
1826
|
+
* For the code that holds a `RebaseClient` but sits outside (or above) an
|
|
1827
|
+
* `ApiConfigProvider`. Returns `undefined` when the client has no base URL,
|
|
1828
|
+
* which is the signal every caller already treats as "cannot build a request".
|
|
1829
|
+
*/
|
|
1830
|
+
function apiBaseOf(client) {
|
|
1831
|
+
if (!client?.baseUrl) return void 0;
|
|
1832
|
+
return client.baseUrl.replace(/\/+$/, "") + (client.apiPath || "/api");
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Provide API configuration (apiUrl, apiPath, getAuthToken) to the entire subtree.
|
|
1836
|
+
* Typically rendered inside `<Rebase>` or at the app root.
|
|
1837
|
+
*/
|
|
1838
|
+
function ApiConfigProvider({ apiUrl, apiPath = DEFAULT_API_PATH, getAuthToken, children }) {
|
|
1839
|
+
const value = React.useMemo(() => ({
|
|
1840
|
+
apiUrl,
|
|
1841
|
+
apiPath,
|
|
1842
|
+
getAuthToken
|
|
1843
|
+
}), [
|
|
1844
|
+
apiUrl,
|
|
1845
|
+
apiPath,
|
|
1846
|
+
getAuthToken
|
|
1847
|
+
]);
|
|
1848
|
+
return /* @__PURE__ */ jsx(ApiConfigContext.Provider, {
|
|
1849
|
+
value,
|
|
1850
|
+
children
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
//#endregion
|
|
1754
1854
|
//#region src/hooks/useBackendStorageSource.ts
|
|
1755
1855
|
/**
|
|
1756
1856
|
* React hook for using backend storage API as a StorageSource
|
|
@@ -1770,8 +1870,8 @@ function useRebaseRegistryDispatch() {
|
|
|
1770
1870
|
* <Rebase storageSource={storageSource} ... />
|
|
1771
1871
|
* ```
|
|
1772
1872
|
*/
|
|
1773
|
-
function useBackendStorageSource({ apiUrl, getAuthToken }) {
|
|
1774
|
-
const storageBasePath = `${apiUrl}/
|
|
1873
|
+
function useBackendStorageSource({ apiUrl, apiPath = DEFAULT_API_PATH, getAuthToken }) {
|
|
1874
|
+
const storageBasePath = `${apiUrl.replace(/\/+$/, "")}${apiPath}/storage`;
|
|
1775
1875
|
const urlsCache = useMemo(() => /* @__PURE__ */ new Map(), []);
|
|
1776
1876
|
/**
|
|
1777
1877
|
* Make an authenticated request to the storage API
|
|
@@ -1933,30 +2033,6 @@ function usePermissions() {
|
|
|
1933
2033
|
]);
|
|
1934
2034
|
}
|
|
1935
2035
|
//#endregion
|
|
1936
|
-
//#region src/hooks/ApiConfigContext.tsx
|
|
1937
|
-
var ApiConfigContext = React.createContext(void 0);
|
|
1938
|
-
/**
|
|
1939
|
-
* Read the API config from context. Returns `undefined` if no provider is present,
|
|
1940
|
-
* allowing hooks to fall back to their own props.
|
|
1941
|
-
*/
|
|
1942
|
-
function useApiConfig() {
|
|
1943
|
-
return useContext(ApiConfigContext);
|
|
1944
|
-
}
|
|
1945
|
-
/**
|
|
1946
|
-
* Provide API configuration (apiUrl, getAuthToken) to the entire subtree.
|
|
1947
|
-
* Typically rendered inside `<Rebase>` or at the app root.
|
|
1948
|
-
*/
|
|
1949
|
-
function ApiConfigProvider({ apiUrl, getAuthToken, children }) {
|
|
1950
|
-
const value = React.useMemo(() => ({
|
|
1951
|
-
apiUrl,
|
|
1952
|
-
getAuthToken
|
|
1953
|
-
}), [apiUrl, getAuthToken]);
|
|
1954
|
-
return /* @__PURE__ */ jsx(ApiConfigContext.Provider, {
|
|
1955
|
-
value,
|
|
1956
|
-
children
|
|
1957
|
-
});
|
|
1958
|
-
}
|
|
1959
|
-
//#endregion
|
|
1960
2036
|
//#region src/hooks/useTranslation.ts
|
|
1961
2037
|
var REBASE_NS$1 = "rebase_core";
|
|
1962
2038
|
/**
|
|
@@ -6318,7 +6394,7 @@ function KanbanBoardDemo() {
|
|
|
6318
6394
|
const [columns, setColumns] = useState(STATUS_COLUMNS);
|
|
6319
6395
|
const assignColumn = useCallback((item) => item.data.status, []);
|
|
6320
6396
|
const handleItemsReorder = useCallback((items, moveInfo) => {
|
|
6321
|
-
|
|
6397
|
+
const updatedItems = items.map((item) => {
|
|
6322
6398
|
if (moveInfo && item.id === moveInfo.itemId && moveInfo.sourceColumn !== moveInfo.targetColumn) return {
|
|
6323
6399
|
...item,
|
|
6324
6400
|
data: {
|
|
@@ -6328,7 +6404,8 @@ function KanbanBoardDemo() {
|
|
|
6328
6404
|
}
|
|
6329
6405
|
};
|
|
6330
6406
|
return item;
|
|
6331
|
-
})
|
|
6407
|
+
});
|
|
6408
|
+
setBoardData(updatedItems);
|
|
6332
6409
|
}, []);
|
|
6333
6410
|
const handleColumnReorder = useCallback((reordered) => setColumns(reordered), []);
|
|
6334
6411
|
const columnLoadingState = useMemo(() => {
|
|
@@ -7563,6 +7640,22 @@ function UIReferenceView() {
|
|
|
7563
7640
|
disabled: true,
|
|
7564
7641
|
value: "Read only",
|
|
7565
7642
|
onChange: () => {}
|
|
7643
|
+
}),
|
|
7644
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
7645
|
+
size: "medium",
|
|
7646
|
+
label: "Medium",
|
|
7647
|
+
placeholder: "Placeholder under the label"
|
|
7648
|
+
}),
|
|
7649
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
7650
|
+
size: "small",
|
|
7651
|
+
label: "Small",
|
|
7652
|
+
placeholder: "Placeholder under the label"
|
|
7653
|
+
}),
|
|
7654
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
7655
|
+
size: "smallest",
|
|
7656
|
+
label: "Smallest",
|
|
7657
|
+
value: "Filled value",
|
|
7658
|
+
onChange: () => {}
|
|
7566
7659
|
})
|
|
7567
7660
|
]
|
|
7568
7661
|
})]
|
|
@@ -8012,7 +8105,7 @@ function UIReferenceView() {
|
|
|
8012
8105
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx("div", {
|
|
8013
8106
|
className: "flex flex-wrap gap-2",
|
|
8014
8107
|
children: user.roles.map((role) => /* @__PURE__ */ jsx(Chip, {
|
|
8015
|
-
colorScheme: role.isAdmin ? "
|
|
8108
|
+
colorScheme: role.isAdmin ? "purple" : "blue",
|
|
8016
8109
|
size: "small",
|
|
8017
8110
|
children: role.name
|
|
8018
8111
|
}, role.id))
|
|
@@ -8336,8 +8429,10 @@ function UserSettingsView() {
|
|
|
8336
8429
|
setLoadingSessions(true);
|
|
8337
8430
|
setSessionsError(null);
|
|
8338
8431
|
try {
|
|
8339
|
-
if (authController.fetchSessions)
|
|
8340
|
-
|
|
8432
|
+
if (authController.fetchSessions) {
|
|
8433
|
+
const sortedSessions = (await authController.fetchSessions() || []).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
8434
|
+
setSessions(sortedSessions);
|
|
8435
|
+
} else throw new Error("fetchSessions not implemented in this auth controller.");
|
|
8341
8436
|
} catch (e) {
|
|
8342
8437
|
setSessionsError(e instanceof Error ? e.message : String(e));
|
|
8343
8438
|
} finally {
|
|
@@ -9690,6 +9785,8 @@ var en = {
|
|
|
9690
9785
|
clear_filter: "Clear filter",
|
|
9691
9786
|
clear_filter_sort: "Clear filter/sort",
|
|
9692
9787
|
clear_sort: "Clear sort",
|
|
9788
|
+
sort: "Sort",
|
|
9789
|
+
sort_by: "Sort by",
|
|
9693
9790
|
clear_all: "Clear all",
|
|
9694
9791
|
no_items: "No items",
|
|
9695
9792
|
no_entries_found: "No entries found",
|
|
@@ -9723,6 +9820,7 @@ var en = {
|
|
|
9723
9820
|
log_out: "Log out",
|
|
9724
9821
|
column_cannot_be_edited: "This column can't be edited directly",
|
|
9725
9822
|
close: "Close",
|
|
9823
|
+
hide_list: "Hide list",
|
|
9726
9824
|
unsaved_local_changes: "Unsaved Local changes",
|
|
9727
9825
|
unsaved_local_changes_description: "This document was edited locally and has unsaved changes. These local changes will be lost if you don't apply them.",
|
|
9728
9826
|
preview_changes: "Preview Changes",
|
|
@@ -10527,7 +10625,22 @@ var en = {
|
|
|
10527
10625
|
datetime_automatic_value: "Automatic value",
|
|
10528
10626
|
markdown_paste_behavior: "Paste behavior",
|
|
10529
10627
|
markdown_strip_html: "Strip HTML on paste",
|
|
10530
|
-
markdown_convert_pasted: "Convert pasted text to markdown"
|
|
10628
|
+
markdown_convert_pasted: "Convert pasted text to markdown",
|
|
10629
|
+
entity_saved: "Saved",
|
|
10630
|
+
more_actions: "More actions",
|
|
10631
|
+
record_history: "History",
|
|
10632
|
+
inspect_record: "Inspect record",
|
|
10633
|
+
inspect: "Inspect",
|
|
10634
|
+
close_inspector: "Close inspector",
|
|
10635
|
+
history_revert: "Revert to this version",
|
|
10636
|
+
history_revert_title: "Revert data to this version?",
|
|
10637
|
+
history_reverted: "Reverted to selected version",
|
|
10638
|
+
history_revert_error: "Error reverting entity",
|
|
10639
|
+
history_revert_dirty: "Please save or discard your changes before reverting",
|
|
10640
|
+
history_empty: "No history available",
|
|
10641
|
+
history_empty_description: "When you save a record, a new version is created and stored in the history.",
|
|
10642
|
+
history_new_entity: "History is only available for existing records",
|
|
10643
|
+
history_no_more: "No more history"
|
|
10531
10644
|
};
|
|
10532
10645
|
//#endregion
|
|
10533
10646
|
//#region src/locales/es.ts
|
|
@@ -10571,6 +10684,8 @@ var es = {
|
|
|
10571
10684
|
clear_filter: "Borrar filtro",
|
|
10572
10685
|
clear_filter_sort: "Borrar filtro/orden",
|
|
10573
10686
|
clear_sort: "Borrar orden",
|
|
10687
|
+
sort: "Ordenar",
|
|
10688
|
+
sort_by: "Ordenar por",
|
|
10574
10689
|
clear_all: "Borrar todo",
|
|
10575
10690
|
no_items: "Sin elementos",
|
|
10576
10691
|
no_entries_found: "No se encontraron entradas",
|
|
@@ -10604,6 +10719,7 @@ var es = {
|
|
|
10604
10719
|
log_out: "Cerrar sesión",
|
|
10605
10720
|
column_cannot_be_edited: "Esta columna no se puede editar directamente",
|
|
10606
10721
|
close: "Cerrar",
|
|
10722
|
+
hide_list: "Ocultar lista",
|
|
10607
10723
|
unsaved_local_changes: "Cambios locales sin guardar",
|
|
10608
10724
|
unsaved_local_changes_description: "Este documento ha sido editado localmente y tiene cambios sin guardar. Estos cambios locales se perderán si no los aplicas.",
|
|
10609
10725
|
preview_changes: "Previsualizar cambios",
|
|
@@ -11373,7 +11489,22 @@ var es = {
|
|
|
11373
11489
|
datetime_automatic_value: "Valor automático",
|
|
11374
11490
|
markdown_paste_behavior: "Comportamiento al pegar",
|
|
11375
11491
|
markdown_strip_html: "Eliminar HTML al pegar",
|
|
11376
|
-
markdown_convert_pasted: "Convertir texto pegado a markdown"
|
|
11492
|
+
markdown_convert_pasted: "Convertir texto pegado a markdown",
|
|
11493
|
+
entity_saved: "Guardado",
|
|
11494
|
+
more_actions: "Más acciones",
|
|
11495
|
+
record_history: "Historial",
|
|
11496
|
+
inspect_record: "Inspeccionar registro",
|
|
11497
|
+
inspect: "Inspeccionar",
|
|
11498
|
+
close_inspector: "Cerrar el inspector",
|
|
11499
|
+
history_revert: "Volver a esta versión",
|
|
11500
|
+
history_revert_title: "¿Volver a esta versión de los datos?",
|
|
11501
|
+
history_reverted: "Se volvió a la versión seleccionada",
|
|
11502
|
+
history_revert_error: "Error al revertir el registro",
|
|
11503
|
+
history_revert_dirty: "Guarda o descarta tus cambios antes de revertir",
|
|
11504
|
+
history_empty: "No hay historial disponible",
|
|
11505
|
+
history_empty_description: "Al guardar un registro se crea una nueva versión y se almacena en el historial.",
|
|
11506
|
+
history_new_entity: "El historial solo está disponible para registros existentes",
|
|
11507
|
+
history_no_more: "No hay más historial"
|
|
11377
11508
|
};
|
|
11378
11509
|
//#endregion
|
|
11379
11510
|
//#region src/locales/de.ts
|
|
@@ -11409,6 +11540,8 @@ var de = {
|
|
|
11409
11540
|
clear_filter: "Filter löschen",
|
|
11410
11541
|
clear_filter_sort: "Filter/Sortierung löschen",
|
|
11411
11542
|
clear_sort: "Sortierung löschen",
|
|
11543
|
+
sort: "Sortieren",
|
|
11544
|
+
sort_by: "Sortieren nach",
|
|
11412
11545
|
clear_all: "Alle löschen",
|
|
11413
11546
|
no_items: "Keine Elemente",
|
|
11414
11547
|
no_entries_found: "Keine Einträge gefunden",
|
|
@@ -11442,6 +11575,7 @@ var de = {
|
|
|
11442
11575
|
log_out: "Abmelden",
|
|
11443
11576
|
column_cannot_be_edited: "Diese Spalte kann nicht direkt bearbeitet werden",
|
|
11444
11577
|
close: "Schließen",
|
|
11578
|
+
hide_list: "Liste ausblenden",
|
|
11445
11579
|
unsaved_local_changes: "Ungespeicherte lokale Änderungen",
|
|
11446
11580
|
unsaved_local_changes_description: "Dieses Dokument wurde lokal bearbeitet und weist ungespeicherte Änderungen auf. Diese lokalen Änderungen gehen verloren, wenn Sie sie nicht anwenden.",
|
|
11447
11581
|
preview_changes: "Änderungen in der Vorschau anzeigen",
|
|
@@ -12201,7 +12335,22 @@ var de = {
|
|
|
12201
12335
|
datetime_automatic_value: "Automatischer Wert",
|
|
12202
12336
|
markdown_paste_behavior: "Einfügeverhalten",
|
|
12203
12337
|
markdown_strip_html: "HTML beim Einfügen entfernen",
|
|
12204
|
-
markdown_convert_pasted: "Eingefügten Text in Markdown umwandeln"
|
|
12338
|
+
markdown_convert_pasted: "Eingefügten Text in Markdown umwandeln",
|
|
12339
|
+
entity_saved: "Gespeichert",
|
|
12340
|
+
more_actions: "Weitere Aktionen",
|
|
12341
|
+
record_history: "Verlauf",
|
|
12342
|
+
inspect_record: "Datensatz inspizieren",
|
|
12343
|
+
inspect: "Inspizieren",
|
|
12344
|
+
close_inspector: "Inspektor schließen",
|
|
12345
|
+
history_revert: "Auf diese Version zurücksetzen",
|
|
12346
|
+
history_revert_title: "Daten auf diese Version zurücksetzen?",
|
|
12347
|
+
history_reverted: "Auf die gewählte Version zurückgesetzt",
|
|
12348
|
+
history_revert_error: "Fehler beim Zurücksetzen des Datensatzes",
|
|
12349
|
+
history_revert_dirty: "Bitte speichere oder verwirf deine Änderungen vor dem Zurücksetzen",
|
|
12350
|
+
history_empty: "Kein Verlauf vorhanden",
|
|
12351
|
+
history_empty_description: "Beim Speichern eines Datensatzes wird eine neue Version erstellt und im Verlauf abgelegt.",
|
|
12352
|
+
history_new_entity: "Der Verlauf ist nur für vorhandene Datensätze verfügbar",
|
|
12353
|
+
history_no_more: "Kein weiterer Verlauf"
|
|
12205
12354
|
};
|
|
12206
12355
|
//#endregion
|
|
12207
12356
|
//#region src/locales/fr.ts
|
|
@@ -12237,6 +12386,8 @@ var fr = {
|
|
|
12237
12386
|
clear_filter: "Effacer le filtre",
|
|
12238
12387
|
clear_filter_sort: "Effacer le filtre/tri",
|
|
12239
12388
|
clear_sort: "Effacer le tri",
|
|
12389
|
+
sort: "Trier",
|
|
12390
|
+
sort_by: "Trier par",
|
|
12240
12391
|
clear_all: "Tout effacer",
|
|
12241
12392
|
no_items: "Aucun élément",
|
|
12242
12393
|
no_entries_found: "Aucune entrée trouvée",
|
|
@@ -12270,6 +12421,7 @@ var fr = {
|
|
|
12270
12421
|
log_out: "Se déconnecter",
|
|
12271
12422
|
column_cannot_be_edited: "Cette colonne ne peut pas être modifiée directement",
|
|
12272
12423
|
close: "Fermer",
|
|
12424
|
+
hide_list: "Masquer la liste",
|
|
12273
12425
|
unsaved_local_changes: "Modifications locales non enregistrées",
|
|
12274
12426
|
unsaved_local_changes_description: "Ce document a été modifié localement et contient des modifications non enregistrées. Ces modifications locales seront perdues si vous ne les appliquez pas.",
|
|
12275
12427
|
preview_changes: "Aperçu des modifications",
|
|
@@ -13029,7 +13181,22 @@ var fr = {
|
|
|
13029
13181
|
datetime_automatic_value: "Valeur automatique",
|
|
13030
13182
|
markdown_paste_behavior: "Comportement au collage",
|
|
13031
13183
|
markdown_strip_html: "Supprimer le HTML au collage",
|
|
13032
|
-
markdown_convert_pasted: "Convertir le texte collé en markdown"
|
|
13184
|
+
markdown_convert_pasted: "Convertir le texte collé en markdown",
|
|
13185
|
+
entity_saved: "Enregistré",
|
|
13186
|
+
more_actions: "Plus d'actions",
|
|
13187
|
+
record_history: "Historique",
|
|
13188
|
+
inspect_record: "Inspecter l'enregistrement",
|
|
13189
|
+
inspect: "Inspecter",
|
|
13190
|
+
close_inspector: "Fermer l'inspecteur",
|
|
13191
|
+
history_revert: "Revenir à cette version",
|
|
13192
|
+
history_revert_title: "Revenir à cette version des données ?",
|
|
13193
|
+
history_reverted: "Retour à la version sélectionnée",
|
|
13194
|
+
history_revert_error: "Erreur lors du retour en arrière",
|
|
13195
|
+
history_revert_dirty: "Enregistrez ou annulez vos modifications avant de revenir en arrière",
|
|
13196
|
+
history_empty: "Aucun historique disponible",
|
|
13197
|
+
history_empty_description: "À chaque enregistrement, une nouvelle version est créée et conservée dans l'historique.",
|
|
13198
|
+
history_new_entity: "L'historique n'est disponible que pour les enregistrements existants",
|
|
13199
|
+
history_no_more: "Plus d'historique"
|
|
13033
13200
|
};
|
|
13034
13201
|
//#endregion
|
|
13035
13202
|
//#region src/locales/it.ts
|
|
@@ -13065,6 +13232,8 @@ var it = {
|
|
|
13065
13232
|
clear_filter: "Cancella filtro",
|
|
13066
13233
|
clear_filter_sort: "Cancella filtro/ordinamento",
|
|
13067
13234
|
clear_sort: "Cancella ordinamento",
|
|
13235
|
+
sort: "Ordina",
|
|
13236
|
+
sort_by: "Ordina per",
|
|
13068
13237
|
clear_all: "Cancella tutto",
|
|
13069
13238
|
no_items: "Nessun elemento",
|
|
13070
13239
|
no_entries_found: "Nessuna voce trovata",
|
|
@@ -13098,6 +13267,7 @@ var it = {
|
|
|
13098
13267
|
log_out: "Esci",
|
|
13099
13268
|
column_cannot_be_edited: "Questa colonna non può essere modificata direttamente",
|
|
13100
13269
|
close: "Chiudi",
|
|
13270
|
+
hide_list: "Nascondi elenco",
|
|
13101
13271
|
unsaved_local_changes: "Modifiche locali non salvate",
|
|
13102
13272
|
unsaved_local_changes_description: "Questo documento è stato modificato localmente e ha modifiche non salvate. Queste modifiche locali andranno perse se non le applichi.",
|
|
13103
13273
|
preview_changes: "Anteprima modifiche",
|
|
@@ -13857,7 +14027,22 @@ var it = {
|
|
|
13857
14027
|
datetime_automatic_value: "Valore automatico",
|
|
13858
14028
|
markdown_paste_behavior: "Comportamento incolla",
|
|
13859
14029
|
markdown_strip_html: "Rimuovi HTML all'incolla",
|
|
13860
|
-
markdown_convert_pasted: "Converti testo incollato in markdown"
|
|
14030
|
+
markdown_convert_pasted: "Converti testo incollato in markdown",
|
|
14031
|
+
entity_saved: "Salvato",
|
|
14032
|
+
more_actions: "Altre azioni",
|
|
14033
|
+
record_history: "Cronologia",
|
|
14034
|
+
inspect_record: "Ispeziona record",
|
|
14035
|
+
inspect: "Ispeziona",
|
|
14036
|
+
close_inspector: "Chiudi l'ispettore",
|
|
14037
|
+
history_revert: "Torna a questa versione",
|
|
14038
|
+
history_revert_title: "Ripristinare i dati a questa versione?",
|
|
14039
|
+
history_reverted: "Ripristinata la versione selezionata",
|
|
14040
|
+
history_revert_error: "Errore durante il ripristino del record",
|
|
14041
|
+
history_revert_dirty: "Salva o annulla le modifiche prima di ripristinare",
|
|
14042
|
+
history_empty: "Nessuna cronologia disponibile",
|
|
14043
|
+
history_empty_description: "Quando salvi un record viene creata una nuova versione e conservata nella cronologia.",
|
|
14044
|
+
history_new_entity: "La cronologia è disponibile solo per i record esistenti",
|
|
14045
|
+
history_no_more: "Nessun'altra cronologia"
|
|
13861
14046
|
};
|
|
13862
14047
|
//#endregion
|
|
13863
14048
|
//#region src/locales/hi.ts
|
|
@@ -13893,6 +14078,8 @@ var hi = {
|
|
|
13893
14078
|
clear_filter: "फ़िल्टर साफ़ करें",
|
|
13894
14079
|
clear_filter_sort: "फ़िल्टर/सॉर्ट साफ़ करें",
|
|
13895
14080
|
clear_sort: "सॉर्ट साफ़ करें",
|
|
14081
|
+
sort: "क्रमबद्ध करें",
|
|
14082
|
+
sort_by: "इसके अनुसार क्रमबद्ध करें",
|
|
13896
14083
|
clear_all: "सभी साफ़ करें",
|
|
13897
14084
|
no_items: "कोई आइटम नहीं",
|
|
13898
14085
|
no_entries_found: "कोई प्रविष्टि नहीं मिली",
|
|
@@ -13926,6 +14113,7 @@ var hi = {
|
|
|
13926
14113
|
log_out: "लॉग आउट",
|
|
13927
14114
|
column_cannot_be_edited: "इस कॉलम को सीधे संपादित नहीं किया जा सकता",
|
|
13928
14115
|
close: "बंद करें",
|
|
14116
|
+
hide_list: "सूची छिपाएँ",
|
|
13929
14117
|
unsaved_local_changes: "सहेजे नहीं गए स्थानीय परिवर्तन",
|
|
13930
14118
|
unsaved_local_changes_description: "इस दस्तावेज़ को स्थानीय स्तर पर संपादित किया गया था और इसमें सहेजे नहीं गए परिवर्तन हैं। यदि आप इन्हें लागू नहीं करते हैं तो ये स्थानीय परिवर्तन खो जाएंगे।",
|
|
13931
14119
|
preview_changes: "परिवर्तनों का पूर्वावलोकन करें",
|
|
@@ -14685,7 +14873,22 @@ var hi = {
|
|
|
14685
14873
|
datetime_automatic_value: "स्वचालित मान",
|
|
14686
14874
|
markdown_paste_behavior: "पेस्ट व्यवहार",
|
|
14687
14875
|
markdown_strip_html: "पेस्ट करते समय HTML हटाएं",
|
|
14688
|
-
markdown_convert_pasted: "पेस्ट किए गए टेक्स्ट को मार्कडाउन में बदलें"
|
|
14876
|
+
markdown_convert_pasted: "पेस्ट किए गए टेक्स्ट को मार्कडाउन में बदलें",
|
|
14877
|
+
entity_saved: "सहेजा गया",
|
|
14878
|
+
more_actions: "अधिक क्रियाएँ",
|
|
14879
|
+
record_history: "इतिहास",
|
|
14880
|
+
inspect_record: "रिकॉर्ड जाँचें",
|
|
14881
|
+
inspect: "जाँचें",
|
|
14882
|
+
close_inspector: "इंस्पेक्टर बंद करें",
|
|
14883
|
+
history_revert: "इस संस्करण पर लौटें",
|
|
14884
|
+
history_revert_title: "डेटा को इस संस्करण पर लौटाएँ?",
|
|
14885
|
+
history_reverted: "चयनित संस्करण पर लौटा दिया गया",
|
|
14886
|
+
history_revert_error: "रिकॉर्ड लौटाने में त्रुटि",
|
|
14887
|
+
history_revert_dirty: "लौटाने से पहले अपने बदलाव सहेजें या रद्द करें",
|
|
14888
|
+
history_empty: "कोई इतिहास उपलब्ध नहीं",
|
|
14889
|
+
history_empty_description: "रिकॉर्ड सहेजने पर एक नया संस्करण बनता है और इतिहास में रखा जाता है।",
|
|
14890
|
+
history_new_entity: "इतिहास केवल मौजूदा रिकॉर्ड के लिए उपलब्ध है",
|
|
14891
|
+
history_no_more: "और इतिहास नहीं"
|
|
14689
14892
|
};
|
|
14690
14893
|
//#endregion
|
|
14691
14894
|
//#region src/locales/pt.ts
|
|
@@ -14726,6 +14929,8 @@ var pt = {
|
|
|
14726
14929
|
clear_filter: "Limpar filtro",
|
|
14727
14930
|
clear_filter_sort: "Limpar filtro/ordenação",
|
|
14728
14931
|
clear_sort: "Limpar ordenação",
|
|
14932
|
+
sort: "Ordenar",
|
|
14933
|
+
sort_by: "Ordenar por",
|
|
14729
14934
|
clear_all: "Limpar tudo",
|
|
14730
14935
|
no_items: "Sem itens",
|
|
14731
14936
|
no_entries_found: "Nenhum registo encontrado",
|
|
@@ -14759,6 +14964,7 @@ var pt = {
|
|
|
14759
14964
|
log_out: "Terminar sessão",
|
|
14760
14965
|
column_cannot_be_edited: "Esta coluna não pode ser editada diretamente",
|
|
14761
14966
|
close: "Fechar",
|
|
14967
|
+
hide_list: "Ocultar lista",
|
|
14762
14968
|
unsaved_local_changes: "Alterações locais não guardadas",
|
|
14763
14969
|
unsaved_local_changes_description: "Este documento foi editado localmente e tem alterações não guardadas. Estas alterações locais serão perdidas se não as aplicar.",
|
|
14764
14970
|
preview_changes: "Pré-visualizar alterações",
|
|
@@ -15518,7 +15724,22 @@ var pt = {
|
|
|
15518
15724
|
datetime_automatic_value: "Valor automático",
|
|
15519
15725
|
markdown_paste_behavior: "Comportamento ao colar",
|
|
15520
15726
|
markdown_strip_html: "Remover HTML ao colar",
|
|
15521
|
-
markdown_convert_pasted: "Converter texto colado para markdown"
|
|
15727
|
+
markdown_convert_pasted: "Converter texto colado para markdown",
|
|
15728
|
+
entity_saved: "Guardado",
|
|
15729
|
+
more_actions: "Mais ações",
|
|
15730
|
+
record_history: "Histórico",
|
|
15731
|
+
inspect_record: "Inspecionar registo",
|
|
15732
|
+
inspect: "Inspecionar",
|
|
15733
|
+
close_inspector: "Fechar o inspetor",
|
|
15734
|
+
history_revert: "Voltar a esta versão",
|
|
15735
|
+
history_revert_title: "Reverter os dados para esta versão?",
|
|
15736
|
+
history_reverted: "Revertido para a versão selecionada",
|
|
15737
|
+
history_revert_error: "Erro ao reverter o registo",
|
|
15738
|
+
history_revert_dirty: "Guarde ou descarte as suas alterações antes de reverter",
|
|
15739
|
+
history_empty: "Nenhum histórico disponível",
|
|
15740
|
+
history_empty_description: "Ao guardar um registo é criada uma nova versão, guardada no histórico.",
|
|
15741
|
+
history_new_entity: "O histórico só está disponível para registos existentes",
|
|
15742
|
+
history_no_more: "Sem mais histórico"
|
|
15522
15743
|
};
|
|
15523
15744
|
//#endregion
|
|
15524
15745
|
//#region src/i18n/RebaseI18nProvider.tsx
|
|
@@ -15852,6 +16073,7 @@ function Rebase(props) {
|
|
|
15852
16073
|
const resolvedApiUrl = apiUrl || client?.baseUrl || (typeof window !== "undefined" ? window.location.origin : "");
|
|
15853
16074
|
if (resolvedApiUrl) return /* @__PURE__ */ jsx(ApiConfigProvider, {
|
|
15854
16075
|
apiUrl: resolvedApiUrl,
|
|
16076
|
+
apiPath: client?.apiPath,
|
|
15855
16077
|
getAuthToken: authController.getAuthToken,
|
|
15856
16078
|
children: content
|
|
15857
16079
|
});
|
|
@@ -15998,7 +16220,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
15998
16220
|
try {
|
|
15999
16221
|
await auth.signOut();
|
|
16000
16222
|
} catch (error) {
|
|
16001
|
-
|
|
16223
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16224
|
+
setAuthProviderError(err);
|
|
16002
16225
|
} finally {
|
|
16003
16226
|
setAuthLoading(false);
|
|
16004
16227
|
}
|
|
@@ -16010,7 +16233,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16010
16233
|
try {
|
|
16011
16234
|
await auth.signInWithEmail(email, password);
|
|
16012
16235
|
} catch (error) {
|
|
16013
|
-
|
|
16236
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16237
|
+
setAuthProviderError(err);
|
|
16014
16238
|
throw error;
|
|
16015
16239
|
} finally {
|
|
16016
16240
|
setAuthLoading(false);
|
|
@@ -16023,7 +16247,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16023
16247
|
try {
|
|
16024
16248
|
await auth.signUp(email, password, displayName);
|
|
16025
16249
|
} catch (error) {
|
|
16026
|
-
|
|
16250
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16251
|
+
setAuthProviderError(err);
|
|
16027
16252
|
throw error;
|
|
16028
16253
|
} finally {
|
|
16029
16254
|
setAuthLoading(false);
|
|
@@ -16036,7 +16261,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16036
16261
|
try {
|
|
16037
16262
|
await auth.signInWithGoogle(payload);
|
|
16038
16263
|
} catch (error) {
|
|
16039
|
-
|
|
16264
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16265
|
+
setAuthProviderError(err);
|
|
16040
16266
|
throw error;
|
|
16041
16267
|
} finally {
|
|
16042
16268
|
setAuthLoading(false);
|
|
@@ -16049,7 +16275,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16049
16275
|
try {
|
|
16050
16276
|
await auth.signInWithOAuth(providerId, payload);
|
|
16051
16277
|
} catch (error) {
|
|
16052
|
-
|
|
16278
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16279
|
+
setAuthProviderError(err);
|
|
16053
16280
|
throw error;
|
|
16054
16281
|
} finally {
|
|
16055
16282
|
setAuthLoading(false);
|
|
@@ -16066,7 +16293,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16066
16293
|
try {
|
|
16067
16294
|
await auth.resetPasswordForEmail(email);
|
|
16068
16295
|
} catch (error) {
|
|
16069
|
-
|
|
16296
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16297
|
+
setAuthProviderError(err);
|
|
16070
16298
|
throw error;
|
|
16071
16299
|
} finally {
|
|
16072
16300
|
setAuthLoading(false);
|
|
@@ -16079,7 +16307,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16079
16307
|
try {
|
|
16080
16308
|
await auth.resetPassword(token, password);
|
|
16081
16309
|
} catch (error) {
|
|
16082
|
-
|
|
16310
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16311
|
+
setAuthProviderError(err);
|
|
16083
16312
|
throw error;
|
|
16084
16313
|
} finally {
|
|
16085
16314
|
setAuthLoading(false);
|
|
@@ -16093,7 +16322,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16093
16322
|
await auth.changePassword(oldPassword, newPassword);
|
|
16094
16323
|
await auth.signOut();
|
|
16095
16324
|
} catch (error) {
|
|
16096
|
-
|
|
16325
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16326
|
+
setAuthProviderError(err);
|
|
16097
16327
|
throw error;
|
|
16098
16328
|
} finally {
|
|
16099
16329
|
setAuthLoading(false);
|
|
@@ -16109,7 +16339,8 @@ function useRebaseAuthController(props = {}) {
|
|
|
16109
16339
|
photoURL
|
|
16110
16340
|
});
|
|
16111
16341
|
} catch (error) {
|
|
16112
|
-
|
|
16342
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
16343
|
+
setAuthProviderError(err);
|
|
16113
16344
|
throw error;
|
|
16114
16345
|
} finally {
|
|
16115
16346
|
setAuthLoading(false);
|
|
@@ -16180,12 +16411,6 @@ function useRebaseAuthController(props = {}) {
|
|
|
16180
16411
|
* here is the unauthenticated `/api/auth/config` probe used to detect
|
|
16181
16412
|
* bootstrap mode and enabled providers before a session exists.
|
|
16182
16413
|
*/
|
|
16183
|
-
/**
|
|
16184
|
-
* @deprecated Use {@link RebaseApiError} (from `@rebasepro/types`, re-exported by
|
|
16185
|
-
* `@rebasepro/client`). Kept as an alias so `instanceof` / imports keep working;
|
|
16186
|
-
* errors thrown here now carry `.code` and `.status` on the unified type.
|
|
16187
|
-
*/
|
|
16188
|
-
var AuthApiError = RebaseApiError;
|
|
16189
16414
|
async function handleResponse(response) {
|
|
16190
16415
|
let data;
|
|
16191
16416
|
try {
|
|
@@ -16238,11 +16463,11 @@ function createAuthConfigCache() {
|
|
|
16238
16463
|
* Concurrent calls are deduplicated: only one network request is made
|
|
16239
16464
|
* and all callers share the same promise.
|
|
16240
16465
|
*/
|
|
16241
|
-
async function fetchAuthConfig(apiUrl, cache) {
|
|
16466
|
+
async function fetchAuthConfig(apiUrl, cache, apiPath = DEFAULT_API_PATH) {
|
|
16242
16467
|
if (cache.cached) return cache.cached;
|
|
16243
16468
|
if (cache.inflight) return cache.inflight;
|
|
16244
16469
|
cache.inflight = (async () => {
|
|
16245
|
-
return handleResponse(await fetchWithHandling(`${apiUrl}/
|
|
16470
|
+
return handleResponse(await fetchWithHandling(`${apiUrl.replace(/\/+$/, "")}${apiPath}/auth/config`, {
|
|
16246
16471
|
method: "GET",
|
|
16247
16472
|
headers: { "Content-Type": "application/json" }
|
|
16248
16473
|
}));
|
|
@@ -16568,7 +16793,7 @@ function useStorageUploadController({ entityId, entityValues, path, value, prope
|
|
|
16568
16793
|
if (!storage) throw Error("Storage meta must be specified");
|
|
16569
16794
|
const processFile = storage?.processFile;
|
|
16570
16795
|
const metadata = storage?.metadata;
|
|
16571
|
-
const size =
|
|
16796
|
+
const size = "medium";
|
|
16572
16797
|
const imageResize = storage?.imageResize;
|
|
16573
16798
|
const internalInitialValue = getInternalInitialValue(multipleFilesSupported, value, metadata, size);
|
|
16574
16799
|
const [initialValue, setInitialValue] = useState(value);
|
|
@@ -16660,7 +16885,8 @@ function useStorageUploadController({ entityId, entityValues, path, value, prope
|
|
|
16660
16885
|
]),
|
|
16661
16886
|
onFileUploadError: useCallback((entry) => {
|
|
16662
16887
|
console.debug("onFileUploadError", entry);
|
|
16663
|
-
|
|
16888
|
+
const newValue = internalValue.filter((item) => item.id !== entry.id);
|
|
16889
|
+
setInternalValue(newValue);
|
|
16664
16890
|
}, [internalValue]),
|
|
16665
16891
|
onFilesAdded: useCallback(async (acceptedFiles) => {
|
|
16666
16892
|
if (!acceptedFiles.length || disabled) return;
|
|
@@ -16775,7 +17001,7 @@ function getColorScheme(enumValues, key) {
|
|
|
16775
17001
|
const labelOrConfig = getLabelOrConfigFrom(enumValues, key);
|
|
16776
17002
|
if (!labelOrConfig?.color) return getColorSchemeForSeed(key.toString());
|
|
16777
17003
|
if (typeof labelOrConfig === "object" && "color" in labelOrConfig) {
|
|
16778
|
-
if (typeof labelOrConfig.color === "string") return
|
|
17004
|
+
if (typeof labelOrConfig.color === "string") return getColorSchemeForKey(labelOrConfig.color);
|
|
16779
17005
|
if (typeof labelOrConfig.color === "object") return labelOrConfig.color;
|
|
16780
17006
|
}
|
|
16781
17007
|
}
|
|
@@ -16809,9 +17035,21 @@ var STUDIO_NAVIGATION_GROUPS = [
|
|
|
16809
17035
|
*/
|
|
16810
17036
|
var CONTAINER_FULL_WIDTH = "100vw";
|
|
16811
17037
|
/** @internal See {@link CONTAINER_FULL_WIDTH}. */
|
|
16812
|
-
var ADDITIONAL_TAB_WIDTH = "55vw";
|
|
16813
|
-
/** @internal See {@link CONTAINER_FULL_WIDTH}. */
|
|
16814
17038
|
var FORM_CONTAINER_WIDTH = "768px";
|
|
17039
|
+
/**
|
|
17040
|
+
* How wide the side panel opens when neither the caller nor the collection
|
|
17041
|
+
* says otherwise.
|
|
17042
|
+
*
|
|
17043
|
+
* Sized from what it has to hold rather than from a round number: the form's
|
|
17044
|
+
* content column plus its metadata rail plus gutters, which is also the width
|
|
17045
|
+
* at which the form stops being a single stack and picks up the same
|
|
17046
|
+
* two-column layout it has in full screen. Capped against the viewport so a
|
|
17047
|
+
* panel never becomes the whole window on a laptop — at that point you want
|
|
17048
|
+
* full screen, and there is a button for it.
|
|
17049
|
+
*
|
|
17050
|
+
* @internal See {@link CONTAINER_FULL_WIDTH}.
|
|
17051
|
+
*/
|
|
17052
|
+
var SIDE_PANEL_DEFAULT_WIDTH = "min(1160px, 88vw)";
|
|
16815
17053
|
//#endregion
|
|
16816
17054
|
//#region src/internal/useRestoreScroll.tsx
|
|
16817
17055
|
var scrollsMap = {};
|
|
@@ -16935,7 +17173,8 @@ var NOOP_BRIDGE = {
|
|
|
16935
17173
|
breadcrumbs: [],
|
|
16936
17174
|
set: () => {},
|
|
16937
17175
|
updateCount: () => {}
|
|
16938
|
-
}
|
|
17176
|
+
},
|
|
17177
|
+
capabilities: { codebase: true }
|
|
16939
17178
|
};
|
|
16940
17179
|
var StudioBridgeContext = createContext(NOOP_BRIDGE);
|
|
16941
17180
|
/**
|
|
@@ -16959,7 +17198,14 @@ function StudioBridgeProvider({ value, children }) {
|
|
|
16959
17198
|
const merged = React.useMemo(() => ({
|
|
16960
17199
|
...NOOP_BRIDGE,
|
|
16961
17200
|
...value
|
|
16962
|
-
}), [
|
|
17201
|
+
}), [
|
|
17202
|
+
value.collectionRegistry,
|
|
17203
|
+
value.sidePanelController,
|
|
17204
|
+
value.urlController,
|
|
17205
|
+
value.navigationState,
|
|
17206
|
+
value.breadcrumbs,
|
|
17207
|
+
value.capabilities
|
|
17208
|
+
]);
|
|
16963
17209
|
return /* @__PURE__ */ jsx(StudioBridgeContext.Provider, {
|
|
16964
17210
|
value: merged,
|
|
16965
17211
|
children
|
|
@@ -16985,6 +17231,10 @@ function useStudioNavigationState() {
|
|
|
16985
17231
|
function useStudioBreadcrumbs() {
|
|
16986
17232
|
return useContext(StudioBridgeContext).breadcrumbs;
|
|
16987
17233
|
}
|
|
17234
|
+
/** What the host can do — see {@link StudioCapabilities}. */
|
|
17235
|
+
function useStudioCapabilities() {
|
|
17236
|
+
return useContext(StudioBridgeContext).capabilities;
|
|
17237
|
+
}
|
|
16988
17238
|
var StudioBridgeRegistryContext = createContext(null);
|
|
16989
17239
|
/**
|
|
16990
17240
|
* Provider that creates a self-assembling bridge.
|
|
@@ -17247,6 +17497,326 @@ function resolveFilterOperators({ property, isArray, engine }) {
|
|
|
17247
17497
|
return typeDefaults.filter((op) => engineOps.has(op) && (narrowingSet === void 0 || narrowingSet.has(op)));
|
|
17248
17498
|
}
|
|
17249
17499
|
//#endregion
|
|
17500
|
+
//#region src/collections/property_presentation.ts
|
|
17501
|
+
function isReadOnly(property) {
|
|
17502
|
+
if (property.admin?.readOnly) return true;
|
|
17503
|
+
if (property.type === "date") {
|
|
17504
|
+
if (property.autoValue) return true;
|
|
17505
|
+
}
|
|
17506
|
+
if (property.type === "reference") return !property.path && !("Field" in (property.admin || {}) && property.admin?.Field);
|
|
17507
|
+
return false;
|
|
17508
|
+
}
|
|
17509
|
+
function isHidden(property) {
|
|
17510
|
+
return typeof property.admin?.disabled === "object" && Boolean(property.admin?.disabled.hidden);
|
|
17511
|
+
}
|
|
17512
|
+
function applyPropertyConditions(property, context) {
|
|
17513
|
+
const { conditions } = property;
|
|
17514
|
+
if (!conditions) return property;
|
|
17515
|
+
const result = { ...property };
|
|
17516
|
+
if (conditions.disabled) {
|
|
17517
|
+
if (evaluateCondition(conditions.disabled, context)) {
|
|
17518
|
+
result.admin = result.admin || {};
|
|
17519
|
+
result.admin.disabled = {
|
|
17520
|
+
clearOnDisabled: conditions.clearOnDisabled ?? false,
|
|
17521
|
+
disabledMessage: conditions.disabledMessage,
|
|
17522
|
+
hidden: false
|
|
17523
|
+
};
|
|
17524
|
+
}
|
|
17525
|
+
}
|
|
17526
|
+
if (conditions.hidden) {
|
|
17527
|
+
if (evaluateCondition(conditions.hidden, context)) {
|
|
17528
|
+
result.admin = result.admin || {};
|
|
17529
|
+
result.admin.disabled = {
|
|
17530
|
+
...typeof result.admin?.disabled === "object" ? result.admin.disabled : {},
|
|
17531
|
+
hidden: true,
|
|
17532
|
+
clearOnDisabled: conditions.clearOnDisabled ?? false
|
|
17533
|
+
};
|
|
17534
|
+
}
|
|
17535
|
+
}
|
|
17536
|
+
if (conditions.readOnly) {
|
|
17537
|
+
if (evaluateCondition(conditions.readOnly, context)) {
|
|
17538
|
+
result.admin = result.admin || {};
|
|
17539
|
+
result.admin.readOnly = true;
|
|
17540
|
+
}
|
|
17541
|
+
}
|
|
17542
|
+
if (conditions.required !== void 0) {
|
|
17543
|
+
const isRequired = evaluateCondition(conditions.required, context);
|
|
17544
|
+
result.validation = {
|
|
17545
|
+
...result.validation,
|
|
17546
|
+
required: isRequired,
|
|
17547
|
+
requiredMessage: conditions.requiredMessage
|
|
17548
|
+
};
|
|
17549
|
+
}
|
|
17550
|
+
if (context.isNew && conditions.defaultValue !== void 0) result.defaultValue = evaluateCondition(conditions.defaultValue, context);
|
|
17551
|
+
if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) result.enum = applyEnumConditions(result.enum, conditions, context);
|
|
17552
|
+
if (result.type === "reference") {
|
|
17553
|
+
if (conditions.referencePath) result.path = evaluateCondition(conditions.referencePath, context);
|
|
17554
|
+
if (conditions.referenceFilter) {
|
|
17555
|
+
result.admin = result.admin || {};
|
|
17556
|
+
result.admin.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
|
|
17557
|
+
}
|
|
17558
|
+
}
|
|
17559
|
+
if (result.type === "array") {
|
|
17560
|
+
if (conditions.canAddElements !== void 0) {
|
|
17561
|
+
result.admin = result.admin || {};
|
|
17562
|
+
result.admin.canAddElements = evaluateCondition(conditions.canAddElements, context);
|
|
17563
|
+
}
|
|
17564
|
+
if (conditions.sortable !== void 0) {
|
|
17565
|
+
result.admin = result.admin || {};
|
|
17566
|
+
result.admin.sortable = evaluateCondition(conditions.sortable, context);
|
|
17567
|
+
}
|
|
17568
|
+
}
|
|
17569
|
+
return result;
|
|
17570
|
+
}
|
|
17571
|
+
/**
|
|
17572
|
+
* Convert an object with numeric keys back to an array.
|
|
17573
|
+
* Firestore stores arrays as {"0": "a", "1": "b"} to avoid nested arrays.
|
|
17574
|
+
*/
|
|
17575
|
+
function objectToArray(obj) {
|
|
17576
|
+
if (Array.isArray(obj)) return obj.map(String);
|
|
17577
|
+
if (obj && typeof obj === "object") {
|
|
17578
|
+
const keys = Object.keys(obj);
|
|
17579
|
+
if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) return keys.sort((a, b) => Number(a) - Number(b)).map((k) => obj[k]).filter((v) => typeof v === "string" || typeof v === "number").map(String);
|
|
17580
|
+
}
|
|
17581
|
+
return [];
|
|
17582
|
+
}
|
|
17583
|
+
/**
|
|
17584
|
+
* Apply enum-specific conditions to filter and modify enum values.
|
|
17585
|
+
*/
|
|
17586
|
+
function applyEnumConditions(enumValues, conditions, context) {
|
|
17587
|
+
let result = [...enumValues];
|
|
17588
|
+
if (conditions.allowedEnumValues) {
|
|
17589
|
+
const allowedArray = objectToArray(evaluateCondition(conditions.allowedEnumValues, context));
|
|
17590
|
+
if (allowedArray.length > 0) result = result.filter((ev) => allowedArray.includes(String(ev.id)));
|
|
17591
|
+
}
|
|
17592
|
+
if (conditions.excludedEnumValues) {
|
|
17593
|
+
const excludedArray = objectToArray(evaluateCondition(conditions.excludedEnumValues, context));
|
|
17594
|
+
if (excludedArray.length > 0) result = result.filter((ev) => !excludedArray.includes(String(ev.id)));
|
|
17595
|
+
}
|
|
17596
|
+
if (conditions.enumConditions) result = result.map((ev) => {
|
|
17597
|
+
const evConditions = conditions.enumConditions?.[ev.id];
|
|
17598
|
+
if (!evConditions) return ev;
|
|
17599
|
+
if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) return null;
|
|
17600
|
+
if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) return {
|
|
17601
|
+
...ev,
|
|
17602
|
+
disabled: true
|
|
17603
|
+
};
|
|
17604
|
+
return ev;
|
|
17605
|
+
}).filter((ev) => ev !== null);
|
|
17606
|
+
return result;
|
|
17607
|
+
}
|
|
17608
|
+
//#endregion
|
|
17609
|
+
//#region src/collections/form-layout.ts
|
|
17610
|
+
/**
|
|
17611
|
+
* Columns in the form grid.
|
|
17612
|
+
*
|
|
17613
|
+
* A local literal rather than an import of `FORM_GRID_COLUMNS`: everything else
|
|
17614
|
+
* this module takes from `@rebasepro/admin-types` is a *type*, erased at build
|
|
17615
|
+
* time, and a runtime value would make this pure function depend on that
|
|
17616
|
+
* package's built output — which is exactly what broke the dev server, since
|
|
17617
|
+
* `@rebasepro/app` resolves that package to its dist. `satisfies PropertySpan`
|
|
17618
|
+
* keeps the two honest: a full-width field and the column count are the same
|
|
17619
|
+
* number by definition.
|
|
17620
|
+
*/
|
|
17621
|
+
var GRID_COLUMNS = 4;
|
|
17622
|
+
/** Does this property's editor need the full width of the column? */
|
|
17623
|
+
function needsFullWidth(property) {
|
|
17624
|
+
switch (property.type) {
|
|
17625
|
+
case "map":
|
|
17626
|
+
case "binary":
|
|
17627
|
+
case "vector": return true;
|
|
17628
|
+
case "array": {
|
|
17629
|
+
if (property.oneOf) return true;
|
|
17630
|
+
const of = Array.isArray(property.of) ? void 0 : property.of;
|
|
17631
|
+
if (!of) return true;
|
|
17632
|
+
if (of.type !== "string" && of.type !== "number") return true;
|
|
17633
|
+
if (of.type === "string" && (of.storage || of.admin?.markdown || of.admin?.multiline)) return true;
|
|
17634
|
+
return false;
|
|
17635
|
+
}
|
|
17636
|
+
case "string": return Boolean(property.storage || property.admin?.markdown || property.admin?.multiline);
|
|
17637
|
+
default: return false;
|
|
17638
|
+
}
|
|
17639
|
+
}
|
|
17640
|
+
/**
|
|
17641
|
+
* The span a property gets when nothing is declared.
|
|
17642
|
+
*
|
|
17643
|
+
* Deliberately coarse: three buckets, chosen by how much room the *editor*
|
|
17644
|
+
* needs, not by how important the field is. Importance is what `admin.form`
|
|
17645
|
+
* sections are for.
|
|
17646
|
+
*/
|
|
17647
|
+
function deriveSpan(property, isTitleProperty) {
|
|
17648
|
+
if (needsFullWidth(property)) return 4;
|
|
17649
|
+
if (isTitleProperty) return 4;
|
|
17650
|
+
switch (property.type) {
|
|
17651
|
+
case "number":
|
|
17652
|
+
case "boolean": return 1;
|
|
17653
|
+
case "date":
|
|
17654
|
+
case "geopoint":
|
|
17655
|
+
case "reference":
|
|
17656
|
+
case "relation": return 2;
|
|
17657
|
+
case "array": return 2;
|
|
17658
|
+
case "string": return 2;
|
|
17659
|
+
default: return 2;
|
|
17660
|
+
}
|
|
17661
|
+
}
|
|
17662
|
+
/**
|
|
17663
|
+
* Can the user still type this id?
|
|
17664
|
+
*
|
|
17665
|
+
* A `manual` id is a real field while creating and frozen once the row exists.
|
|
17666
|
+
* Every generated strategy (`uuid`, `cuid`, a raw SQL default) is never typed.
|
|
17667
|
+
* Getting this wrong in the "frozen" direction would make manual-id collections
|
|
17668
|
+
* uncreatable, so it fails towards showing the field.
|
|
17669
|
+
*/
|
|
17670
|
+
function isIdPropertyEditable(property, status) {
|
|
17671
|
+
if (!("isId" in property) || !property.isId) return true;
|
|
17672
|
+
if (status === "existing") return false;
|
|
17673
|
+
return property.isId === "manual";
|
|
17674
|
+
}
|
|
17675
|
+
function isIdProperty(property) {
|
|
17676
|
+
return Boolean(property && "isId" in property && property.isId);
|
|
17677
|
+
}
|
|
17678
|
+
/**
|
|
17679
|
+
* An audit timestamp the database maintains — `created_at`, `updated_at`.
|
|
17680
|
+
*
|
|
17681
|
+
* Not a guess from the name: `autoValue` says the column is written by the
|
|
17682
|
+
* system on create or update, so the field is never typed. Rendering it as an
|
|
17683
|
+
* input in the middle of the form promised an edit that cannot happen, and the
|
|
17684
|
+
* record block already shows both values — the same two dates twice on one
|
|
17685
|
+
* screen.
|
|
17686
|
+
*/
|
|
17687
|
+
function isAuditTimestamp(property) {
|
|
17688
|
+
return Boolean(property && property.type === "date" && property.autoValue);
|
|
17689
|
+
}
|
|
17690
|
+
/**
|
|
17691
|
+
* Close the trailing gap in each row.
|
|
17692
|
+
*
|
|
17693
|
+
* Without this a section of half-width fields with an odd count leaves a hole —
|
|
17694
|
+
* `name | sku` then `brand | ␣␣` — a field stranded beside half a row of
|
|
17695
|
+
* nothing, which reads as a mistake rather than a layout. The remainder is
|
|
17696
|
+
* spread across the row's derived fields rather than dumped on the last one, so
|
|
17697
|
+
* a lone half-width field becomes full width and a pair grows evenly. Fields
|
|
17698
|
+
* with an explicit span are never resized.
|
|
17699
|
+
*/
|
|
17700
|
+
function fillRows(fields) {
|
|
17701
|
+
const rows = [];
|
|
17702
|
+
let row = [];
|
|
17703
|
+
let used = 0;
|
|
17704
|
+
for (const field of fields) {
|
|
17705
|
+
if (used + field.span > GRID_COLUMNS && row.length) {
|
|
17706
|
+
rows.push(row);
|
|
17707
|
+
row = [];
|
|
17708
|
+
used = 0;
|
|
17709
|
+
}
|
|
17710
|
+
row.push(field);
|
|
17711
|
+
used += field.span;
|
|
17712
|
+
}
|
|
17713
|
+
if (row.length) rows.push(row);
|
|
17714
|
+
return rows.flatMap((entries) => {
|
|
17715
|
+
let gap = GRID_COLUMNS - entries.reduce((sum, f) => sum + f.span, 0);
|
|
17716
|
+
if (gap <= 0) return entries;
|
|
17717
|
+
const growable = entries.map((f, i) => [f, i]).filter(([f]) => !f.spanExplicit);
|
|
17718
|
+
if (!growable.length) return entries;
|
|
17719
|
+
const next = [...entries];
|
|
17720
|
+
let cursor = 0;
|
|
17721
|
+
while (gap > 0) {
|
|
17722
|
+
const [, index] = growable[cursor % growable.length];
|
|
17723
|
+
next[index] = {
|
|
17724
|
+
...next[index],
|
|
17725
|
+
span: next[index].span + 1
|
|
17726
|
+
};
|
|
17727
|
+
gap -= 1;
|
|
17728
|
+
cursor += 1;
|
|
17729
|
+
}
|
|
17730
|
+
return next;
|
|
17731
|
+
});
|
|
17732
|
+
}
|
|
17733
|
+
function buildField(key, collection, titlePropertyKey) {
|
|
17734
|
+
const property = collection.properties?.[key];
|
|
17735
|
+
if (!property) return collection.additionalFields?.some((f) => f.key === key) ? {
|
|
17736
|
+
key,
|
|
17737
|
+
span: 4,
|
|
17738
|
+
additional: true
|
|
17739
|
+
} : void 0;
|
|
17740
|
+
if (isHidden(property)) return void 0;
|
|
17741
|
+
const admin = property.admin;
|
|
17742
|
+
const explicit = admin?.span !== void 0;
|
|
17743
|
+
return {
|
|
17744
|
+
key,
|
|
17745
|
+
span: admin?.span ?? deriveSpan(property, key === titlePropertyKey),
|
|
17746
|
+
additional: false,
|
|
17747
|
+
spanExplicit: explicit
|
|
17748
|
+
};
|
|
17749
|
+
}
|
|
17750
|
+
/**
|
|
17751
|
+
* Resolve the layout for a collection's generated form.
|
|
17752
|
+
*
|
|
17753
|
+
* Never drops a field: anything not named by a section lands in a trailing
|
|
17754
|
+
* group, so adding a column to the database cannot make it silently invisible
|
|
17755
|
+
* in the panel.
|
|
17756
|
+
*/
|
|
17757
|
+
function resolveFormLayout({ collection, fieldKeys, status }) {
|
|
17758
|
+
const config = collection.form;
|
|
17759
|
+
const titlePropertyKey = collection.titleProperty;
|
|
17760
|
+
const available = /* @__PURE__ */ new Map();
|
|
17761
|
+
for (const key of fieldKeys) {
|
|
17762
|
+
const field = buildField(key, collection, titlePropertyKey);
|
|
17763
|
+
if (field) available.set(key, field);
|
|
17764
|
+
}
|
|
17765
|
+
const idKeys = [...available.keys()].filter((key) => isIdProperty(collection.properties?.[key]));
|
|
17766
|
+
if (idKeys.length === 1) {
|
|
17767
|
+
const property = collection.properties?.[idKeys[0]];
|
|
17768
|
+
if (!isIdPropertyEditable(property, status)) available.delete(idKeys[0]);
|
|
17769
|
+
}
|
|
17770
|
+
const showRecordMetaResolved = config?.showRecordMeta ?? !collection.hideIdFromForm;
|
|
17771
|
+
if (showRecordMetaResolved) for (const key of [...available.keys()]) {
|
|
17772
|
+
const property = collection.properties?.[key];
|
|
17773
|
+
if (config?.sidebar?.includes(key)) continue;
|
|
17774
|
+
if (isAuditTimestamp(property)) available.delete(key);
|
|
17775
|
+
}
|
|
17776
|
+
const sidebar = [];
|
|
17777
|
+
if (config?.sidebar) for (const key of config.sidebar) {
|
|
17778
|
+
const field = available.get(key);
|
|
17779
|
+
if (!field) continue;
|
|
17780
|
+
sidebar.push(field);
|
|
17781
|
+
available.delete(key);
|
|
17782
|
+
}
|
|
17783
|
+
const showRecordMeta = showRecordMetaResolved;
|
|
17784
|
+
const sections = [];
|
|
17785
|
+
if (config?.sections?.length) for (const section of config.sections) {
|
|
17786
|
+
const fields = [];
|
|
17787
|
+
for (const key of section.properties) {
|
|
17788
|
+
const field = available.get(key);
|
|
17789
|
+
if (!field) continue;
|
|
17790
|
+
fields.push(field);
|
|
17791
|
+
available.delete(key);
|
|
17792
|
+
}
|
|
17793
|
+
const titled = Boolean(section.title);
|
|
17794
|
+
sections.push({
|
|
17795
|
+
key: section.key,
|
|
17796
|
+
title: section.title,
|
|
17797
|
+
collapsible: section.collapsible ?? titled,
|
|
17798
|
+
collapsed: titled ? Boolean(section.collapsed) : false,
|
|
17799
|
+
fields
|
|
17800
|
+
});
|
|
17801
|
+
}
|
|
17802
|
+
const leftovers = fieldKeys.map((key) => available.get(key)).filter((f) => Boolean(f));
|
|
17803
|
+
if (leftovers.length) sections.push({
|
|
17804
|
+
key: sections.length ? "__other" : "__main",
|
|
17805
|
+
collapsible: false,
|
|
17806
|
+
collapsed: false,
|
|
17807
|
+
fields: leftovers
|
|
17808
|
+
});
|
|
17809
|
+
return {
|
|
17810
|
+
sections: sections.filter((s) => s.fields.length > 0).map((s) => ({
|
|
17811
|
+
...s,
|
|
17812
|
+
fields: fillRows(s.fields)
|
|
17813
|
+
})),
|
|
17814
|
+
sidebar,
|
|
17815
|
+
showRecordMeta,
|
|
17816
|
+
hasRail: sidebar.length > 0 || showRecordMeta
|
|
17817
|
+
};
|
|
17818
|
+
}
|
|
17819
|
+
//#endregion
|
|
17250
17820
|
//#region src/collections/navigation_utils.ts
|
|
17251
17821
|
function removeInitialAndTrailingSlashes(s) {
|
|
17252
17822
|
return removeInitialSlash(removeTrailingSlash(s));
|
|
@@ -17458,115 +18028,6 @@ function getParentReferencesFromPath(props) {
|
|
|
17458
18028
|
return result;
|
|
17459
18029
|
}
|
|
17460
18030
|
//#endregion
|
|
17461
|
-
//#region src/collections/property_presentation.ts
|
|
17462
|
-
function isReadOnly(property) {
|
|
17463
|
-
if (property.admin?.readOnly) return true;
|
|
17464
|
-
if (property.type === "date") {
|
|
17465
|
-
if (property.autoValue) return true;
|
|
17466
|
-
}
|
|
17467
|
-
if (property.type === "reference") return !property.path && !("Field" in (property.admin || {}) && property.admin?.Field);
|
|
17468
|
-
return false;
|
|
17469
|
-
}
|
|
17470
|
-
function isHidden(property) {
|
|
17471
|
-
return typeof property.admin?.disabled === "object" && Boolean(property.admin?.disabled.hidden);
|
|
17472
|
-
}
|
|
17473
|
-
function applyPropertyConditions(property, context) {
|
|
17474
|
-
const { conditions } = property;
|
|
17475
|
-
if (!conditions) return property;
|
|
17476
|
-
const result = { ...property };
|
|
17477
|
-
if (conditions.disabled) {
|
|
17478
|
-
if (evaluateCondition(conditions.disabled, context)) {
|
|
17479
|
-
result.admin = result.admin || {};
|
|
17480
|
-
result.admin.disabled = {
|
|
17481
|
-
clearOnDisabled: conditions.clearOnDisabled ?? false,
|
|
17482
|
-
disabledMessage: conditions.disabledMessage,
|
|
17483
|
-
hidden: false
|
|
17484
|
-
};
|
|
17485
|
-
}
|
|
17486
|
-
}
|
|
17487
|
-
if (conditions.hidden) {
|
|
17488
|
-
if (evaluateCondition(conditions.hidden, context)) {
|
|
17489
|
-
result.admin = result.admin || {};
|
|
17490
|
-
result.admin.disabled = {
|
|
17491
|
-
...typeof result.admin?.disabled === "object" ? result.admin.disabled : {},
|
|
17492
|
-
hidden: true,
|
|
17493
|
-
clearOnDisabled: conditions.clearOnDisabled ?? false
|
|
17494
|
-
};
|
|
17495
|
-
}
|
|
17496
|
-
}
|
|
17497
|
-
if (conditions.readOnly) {
|
|
17498
|
-
if (evaluateCondition(conditions.readOnly, context)) {
|
|
17499
|
-
result.admin = result.admin || {};
|
|
17500
|
-
result.admin.readOnly = true;
|
|
17501
|
-
}
|
|
17502
|
-
}
|
|
17503
|
-
if (conditions.required !== void 0) {
|
|
17504
|
-
const isRequired = evaluateCondition(conditions.required, context);
|
|
17505
|
-
result.validation = {
|
|
17506
|
-
...result.validation,
|
|
17507
|
-
required: isRequired,
|
|
17508
|
-
requiredMessage: conditions.requiredMessage
|
|
17509
|
-
};
|
|
17510
|
-
}
|
|
17511
|
-
if (context.isNew && conditions.defaultValue !== void 0) result.defaultValue = evaluateCondition(conditions.defaultValue, context);
|
|
17512
|
-
if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) result.enum = applyEnumConditions(result.enum, conditions, context);
|
|
17513
|
-
if (result.type === "reference") {
|
|
17514
|
-
if (conditions.referencePath) result.path = evaluateCondition(conditions.referencePath, context);
|
|
17515
|
-
if (conditions.referenceFilter) {
|
|
17516
|
-
result.admin = result.admin || {};
|
|
17517
|
-
result.admin.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
|
|
17518
|
-
}
|
|
17519
|
-
}
|
|
17520
|
-
if (result.type === "array") {
|
|
17521
|
-
if (conditions.canAddElements !== void 0) {
|
|
17522
|
-
result.admin = result.admin || {};
|
|
17523
|
-
result.admin.canAddElements = evaluateCondition(conditions.canAddElements, context);
|
|
17524
|
-
}
|
|
17525
|
-
if (conditions.sortable !== void 0) {
|
|
17526
|
-
result.admin = result.admin || {};
|
|
17527
|
-
result.admin.sortable = evaluateCondition(conditions.sortable, context);
|
|
17528
|
-
}
|
|
17529
|
-
}
|
|
17530
|
-
return result;
|
|
17531
|
-
}
|
|
17532
|
-
/**
|
|
17533
|
-
* Convert an object with numeric keys back to an array.
|
|
17534
|
-
* Firestore stores arrays as {"0": "a", "1": "b"} to avoid nested arrays.
|
|
17535
|
-
*/
|
|
17536
|
-
function objectToArray(obj) {
|
|
17537
|
-
if (Array.isArray(obj)) return obj.map(String);
|
|
17538
|
-
if (obj && typeof obj === "object") {
|
|
17539
|
-
const keys = Object.keys(obj);
|
|
17540
|
-
if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) return keys.sort((a, b) => Number(a) - Number(b)).map((k) => obj[k]).filter((v) => typeof v === "string" || typeof v === "number").map(String);
|
|
17541
|
-
}
|
|
17542
|
-
return [];
|
|
17543
|
-
}
|
|
17544
|
-
/**
|
|
17545
|
-
* Apply enum-specific conditions to filter and modify enum values.
|
|
17546
|
-
*/
|
|
17547
|
-
function applyEnumConditions(enumValues, conditions, context) {
|
|
17548
|
-
let result = [...enumValues];
|
|
17549
|
-
if (conditions.allowedEnumValues) {
|
|
17550
|
-
const allowedArray = objectToArray(evaluateCondition(conditions.allowedEnumValues, context));
|
|
17551
|
-
if (allowedArray.length > 0) result = result.filter((ev) => allowedArray.includes(String(ev.id)));
|
|
17552
|
-
}
|
|
17553
|
-
if (conditions.excludedEnumValues) {
|
|
17554
|
-
const excludedArray = objectToArray(evaluateCondition(conditions.excludedEnumValues, context));
|
|
17555
|
-
if (excludedArray.length > 0) result = result.filter((ev) => !excludedArray.includes(String(ev.id)));
|
|
17556
|
-
}
|
|
17557
|
-
if (conditions.enumConditions) result = result.map((ev) => {
|
|
17558
|
-
const evConditions = conditions.enumConditions?.[ev.id];
|
|
17559
|
-
if (!evConditions) return ev;
|
|
17560
|
-
if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) return null;
|
|
17561
|
-
if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) return {
|
|
17562
|
-
...ev,
|
|
17563
|
-
disabled: true
|
|
17564
|
-
};
|
|
17565
|
-
return ev;
|
|
17566
|
-
}).filter((ev) => ev !== null);
|
|
17567
|
-
return result;
|
|
17568
|
-
}
|
|
17569
|
-
//#endregion
|
|
17570
18031
|
//#region src/collections/title-property.ts
|
|
17571
18032
|
/**
|
|
17572
18033
|
* How good a property is as the human-readable title of an entity.
|
|
@@ -17600,7 +18061,7 @@ var SCORE = {
|
|
|
17600
18061
|
* This is a *bonus* on top of the structural rules — never a requirement, and
|
|
17601
18062
|
* never the mechanism that keeps identifiers out of the title slot.
|
|
17602
18063
|
*/
|
|
17603
|
-
var TITLE_LIKE_KEYS = new Set([
|
|
18064
|
+
var TITLE_LIKE_KEYS = /* @__PURE__ */ new Set([
|
|
17604
18065
|
"name",
|
|
17605
18066
|
"fullname",
|
|
17606
18067
|
"displayname",
|
|
@@ -17730,6 +18191,40 @@ function getTitlePropertyCandidates(collection) {
|
|
|
17730
18191
|
return scored.map((candidate) => candidate.key);
|
|
17731
18192
|
}
|
|
17732
18193
|
/**
|
|
18194
|
+
* The relation a collection *leads* with, if it leads with one.
|
|
18195
|
+
*
|
|
18196
|
+
* A junction-shaped collection — a pipeline entry, a membership, a booking —
|
|
18197
|
+
* holds no label of its own: it is "this candidate, for that vacancy". The
|
|
18198
|
+
* general ranking puts relations last (they only read once the target
|
|
18199
|
+
* resolves), so the title slot would otherwise fall to whatever free text
|
|
18200
|
+
* happens to follow: a note, a comment. Preview surfaces that render one row
|
|
18201
|
+
* per entity (list, cards, board) prefer the leading relation instead.
|
|
18202
|
+
*
|
|
18203
|
+
* The decision is structural — the first property that could be a title, in the
|
|
18204
|
+
* order the developer declared (or ordered) them. Declaration order is a
|
|
18205
|
+
* statement about what the collection is about; property *names* are not
|
|
18206
|
+
* consulted here.
|
|
18207
|
+
*
|
|
18208
|
+
* Returns `undefined` when that first property is anything but a
|
|
18209
|
+
* single-cardinality relation, or when the collection states its own
|
|
18210
|
+
* `titleProperty`.
|
|
18211
|
+
*
|
|
18212
|
+
* @group Collections
|
|
18213
|
+
*/
|
|
18214
|
+
function getLeadingRelationTitleKey(collection) {
|
|
18215
|
+
if (!collection.properties) return void 0;
|
|
18216
|
+
if (collection.titleProperty) return void 0;
|
|
18217
|
+
const idKeys = new Set(getPrimaryKeys(collection));
|
|
18218
|
+
const foreignKeys = getForeignKeyColumns(collection);
|
|
18219
|
+
const order = collection.propertiesOrder ?? Object.keys(collection.properties);
|
|
18220
|
+
for (const key of order) {
|
|
18221
|
+
const property = collection.properties[key];
|
|
18222
|
+
if (!property || isPropertyBuilder(property)) continue;
|
|
18223
|
+
if (scoreTitleCandidate(property, key, idKeys, foreignKeys) === SCORE.DISQUALIFIED) continue;
|
|
18224
|
+
return property.type === "relation" ? key : void 0;
|
|
18225
|
+
}
|
|
18226
|
+
}
|
|
18227
|
+
/**
|
|
17733
18228
|
* The property that should fill the title slot for a collection, ignoring any
|
|
17734
18229
|
* concrete values. Prefer {@link getTitlePropertyKeyForValues} when an entity
|
|
17735
18230
|
* is at hand — it can skip candidates that happen to be empty or to hold an id.
|
|
@@ -17779,6 +18274,6 @@ function getTitlePropertyKeyForValues(collection, values, entityId) {
|
|
|
17779
18274
|
return candidates[0];
|
|
17780
18275
|
}
|
|
17781
18276
|
//#endregion
|
|
17782
|
-
export {
|
|
18277
|
+
export { AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_API_PATH, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NavigationBlockerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, SIDE_PANEL_DEFAULT_WIDTH, STUDIO_NAVIGATION_GROUPS, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, addInitialSlash, apiBaseOf, applyPropertyConditions, buildCollapsedDefaults, buildEnumLabel, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, deriveSpan, en, es, fetchAuthConfig, fillRows, flattenKeys, getAdminEntityChildViews, getAdminSubcollections, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getLastSegment, getLeadingRelationTitleKey, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, iconsSearch, isAuditTimestamp, isEnumValueDisabled, isFilterableRelation, isHidden, isIdPropertyEditable, isReadOnly, isSchemaDriftError, looksLikeIdentifierValue, populateFetchCache, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, resolveCollectionPathIds, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, resolveFormLayout, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiBase, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCapabilities, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
|
|
17783
18278
|
|
|
17784
18279
|
//# sourceMappingURL=index.es.js.map
|