@rebasepro/core 0.1.2 → 0.2.1
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/LICENSE +1 -1
- package/dist/components/LoginView/LoginView.d.ts +1 -6
- package/dist/contexts/SnackbarProvider.d.ts +1 -1
- package/dist/hooks/data/save.d.ts +2 -2
- package/dist/hooks/data/useEntityFetch.d.ts +5 -0
- package/dist/hooks/useResolvedComponent.d.ts +1 -1
- package/dist/index.es.js +386 -309
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +436 -358
- package/dist/index.umd.js.map +1 -1
- package/package.json +22 -23
- package/src/components/AIIcon.tsx +1 -1
- package/src/components/Debug/UIReferenceView.tsx +52 -3
- package/src/components/Debug/UIStyleGuide.tsx +1 -2
- package/src/components/ErrorView.tsx +1 -2
- package/src/components/LanguageToggle.tsx +1 -9
- package/src/components/LoginView/LoginView.tsx +21 -12
- package/src/components/UserDisplay.tsx +1 -2
- package/src/components/UserSelectPopover.tsx +13 -2
- package/src/components/UserSettingsView.tsx +12 -3
- package/src/components/common/useDataTableController.tsx +2 -2
- package/src/contexts/SnackbarProvider.tsx +2 -1
- package/src/core/PluginLifecycleManager.tsx +0 -1
- package/src/core/Rebase.tsx +1 -1
- package/src/hooks/data/save.ts +4 -4
- package/src/hooks/data/useCollectionFetch.tsx +4 -4
- package/src/hooks/data/useEntityFetch.tsx +13 -3
- package/src/hooks/data/useRelationSelector.tsx +2 -2
- package/src/hooks/useResolvedComponent.tsx +7 -6
- package/src/hooks/useStudioBridge.tsx +0 -1
- package/src/locales/en.ts +8 -0
- package/src/util/icons.tsx +16 -8
- package/src/util/previews.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "Rebase core — framework-agnostic runtime for data-driven admin panels",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -33,30 +33,30 @@
|
|
|
33
33
|
"exports": {
|
|
34
34
|
".": {
|
|
35
35
|
"types": "./dist/index.d.ts",
|
|
36
|
-
"development": "./
|
|
36
|
+
"development": "./dist/index.es.js",
|
|
37
37
|
"import": "./dist/index.es.js",
|
|
38
38
|
"require": "./dist/index.umd.js"
|
|
39
39
|
},
|
|
40
40
|
"./vitePlugin": {
|
|
41
41
|
"types": "./dist/vitePlugin.d.ts",
|
|
42
|
-
"development": "./
|
|
42
|
+
"development": "./dist/vitePlugin.js",
|
|
43
43
|
"import": "./dist/vitePlugin.js",
|
|
44
44
|
"require": "./dist/vitePlugin.js"
|
|
45
45
|
},
|
|
46
46
|
"./package.json": "./package.json"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"compressorjs": "^1.
|
|
49
|
+
"compressorjs": "^1.3.0",
|
|
50
50
|
"fast-equals": "6.0.0",
|
|
51
|
-
"fuse.js": "^7.
|
|
52
|
-
"i18next": "^23.16.
|
|
51
|
+
"fuse.js": "^7.3.0",
|
|
52
|
+
"i18next": "^23.16.8",
|
|
53
53
|
"notistack": "^3.0.2",
|
|
54
54
|
"react-i18next": "^14.1.3",
|
|
55
|
-
"@rebasepro/common": "0.1
|
|
56
|
-
"@rebasepro/types": "0.1
|
|
57
|
-
"@rebasepro/
|
|
58
|
-
"@rebasepro/
|
|
59
|
-
"@rebasepro/
|
|
55
|
+
"@rebasepro/common": "0.2.1",
|
|
56
|
+
"@rebasepro/types": "0.2.1",
|
|
57
|
+
"@rebasepro/utils": "0.2.1",
|
|
58
|
+
"@rebasepro/ui": "0.2.1",
|
|
59
|
+
"@rebasepro/formex": "0.2.1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"react": ">=19.0.0",
|
|
@@ -65,26 +65,25 @@
|
|
|
65
65
|
"react-router-dom": "^7.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@jest/globals": "^30.
|
|
69
|
-
"@testing-library/react": "^16.3.
|
|
68
|
+
"@jest/globals": "^30.4.1",
|
|
69
|
+
"@testing-library/react": "^16.3.2",
|
|
70
70
|
"@testing-library/user-event": "^14.6.1",
|
|
71
71
|
"@types/jest": "^29.5.14",
|
|
72
|
-
"@types/node": "^20.19.
|
|
73
|
-
"@types/react": "^19.
|
|
74
|
-
"@types/react-dom": "^19.
|
|
72
|
+
"@types/node": "^20.19.41",
|
|
73
|
+
"@types/react": "^19.2.15",
|
|
74
|
+
"@types/react-dom": "^19.2.3",
|
|
75
75
|
"@types/react-measure": "^2.0.12",
|
|
76
|
-
"@vitejs/plugin-react": "^4.
|
|
76
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
77
77
|
"babel-plugin-react-compiler": "beta",
|
|
78
78
|
"cross-env": "^7.0.3",
|
|
79
|
-
"
|
|
79
|
+
"esbuild": "^0.25.12",
|
|
80
80
|
"jest": "^29.7.0",
|
|
81
|
-
"jest-environment-jsdom": "^30.
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"ts-jest": "^29.4.5",
|
|
81
|
+
"jest-environment-jsdom": "^30.4.1",
|
|
82
|
+
"react-router-dom": "^7.15.1",
|
|
83
|
+
"ts-jest": "^29.4.10",
|
|
85
84
|
"tsd": "^0.31.2",
|
|
86
85
|
"typescript": "^5.9.3",
|
|
87
|
-
"vite": "^7.
|
|
86
|
+
"vite": "^7.3.3"
|
|
88
87
|
},
|
|
89
88
|
"files": [
|
|
90
89
|
"dist",
|
|
@@ -11,8 +11,57 @@
|
|
|
11
11
|
* DrawerNavigationGroup.tsx, UsersView.tsx, RolesView.tsx
|
|
12
12
|
*/
|
|
13
13
|
import React, { useState } from "react";
|
|
14
|
-
import {
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
Alert,
|
|
16
|
+
Avatar,
|
|
17
|
+
BooleanSwitch,
|
|
18
|
+
Button,
|
|
19
|
+
Checkbox,
|
|
20
|
+
ChevronDownIcon,
|
|
21
|
+
ChevronsLeftIcon,
|
|
22
|
+
ChevronsRightIcon,
|
|
23
|
+
Chip,
|
|
24
|
+
CircularProgress,
|
|
25
|
+
cls,
|
|
26
|
+
defaultBorderMixin,
|
|
27
|
+
FilterIcon,
|
|
28
|
+
FolderIcon,
|
|
29
|
+
IconButton,
|
|
30
|
+
iconSize,
|
|
31
|
+
KanbanIcon,
|
|
32
|
+
LayoutGridIcon,
|
|
33
|
+
ListIcon,
|
|
34
|
+
LoadingButton,
|
|
35
|
+
LogOutIcon,
|
|
36
|
+
Menu,
|
|
37
|
+
MenuItem,
|
|
38
|
+
MoonIcon,
|
|
39
|
+
MultiSelect,
|
|
40
|
+
MultiSelectItem,
|
|
41
|
+
PencilIcon,
|
|
42
|
+
PlusIcon,
|
|
43
|
+
SearchBar,
|
|
44
|
+
Select,
|
|
45
|
+
SelectItem,
|
|
46
|
+
Separator,
|
|
47
|
+
SettingsIcon,
|
|
48
|
+
Skeleton,
|
|
49
|
+
SunIcon,
|
|
50
|
+
SunMoonIcon,
|
|
51
|
+
Tab,
|
|
52
|
+
Table,
|
|
53
|
+
TableBody,
|
|
54
|
+
TableCell,
|
|
55
|
+
TableHeader,
|
|
56
|
+
TableRow,
|
|
57
|
+
Tabs,
|
|
58
|
+
TagIcon,
|
|
59
|
+
TextField,
|
|
60
|
+
Tooltip,
|
|
61
|
+
Trash2Icon,
|
|
62
|
+
Typography,
|
|
63
|
+
UserIcon
|
|
64
|
+
} from "@rebasepro/ui";
|
|
16
65
|
import { RebaseLogo } from "../RebaseLogo";
|
|
17
66
|
|
|
18
67
|
const SECTIONS = [
|
|
@@ -147,7 +196,7 @@ block: "start" });
|
|
|
147
196
|
<div className="mt-3 flex-grow overflow-hidden">
|
|
148
197
|
<div className="my-2 mx-2 flex flex-col">
|
|
149
198
|
<div className="overflow-hidden rounded-lg bg-surface-50 dark:bg-surface-950/30">
|
|
150
|
-
{[<FolderIcon size={iconSize.small}/>, <UserIcon size={iconSize.small}/>, <TagIcon size={iconSize.small}/>].map((icon, i) => (
|
|
199
|
+
{[<FolderIcon key="folder" size={iconSize.small}/>, <UserIcon key="user" size={iconSize.small}/>, <TagIcon key="tag" size={iconSize.small}/>].map((icon, i) => (
|
|
151
200
|
<div key={i} className="rounded-lg truncate hover:bg-surface-accent-300/75 dark:hover:bg-surface-accent-800/75 flex flex-row items-center h-10">
|
|
152
201
|
<div className="shrink-0 flex items-center justify-center w-[56px] h-[40px] text-text-secondary dark:text-text-secondary-dark">
|
|
153
202
|
{icon}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import { Trash2Icon } from "lucide-react";
|
|
3
|
+
import { Button, cls, Container, IconButton, Paper, Separator, Trash2Icon, Typography } from "@rebasepro/ui";
|
|
5
4
|
|
|
6
5
|
export const UIStyleGuide = () => {
|
|
7
6
|
const typographyVariants = [
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ErrorTooltip } from "./ErrorTooltip";
|
|
3
|
-
import {
|
|
4
|
-
import { AlertTriangleIcon } from "lucide-react";
|
|
3
|
+
import { AlertTriangleIcon, Button, Typography } from "@rebasepro/ui";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @group Components
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { IconButton, Menu, MenuItem, Typography
|
|
3
|
-
import { CheckIcon, LanguagesIcon } from "lucide-react";
|
|
2
|
+
import { CheckIcon, IconButton, iconSize, LanguagesIcon, Menu, MenuItem, Typography } from "@rebasepro/ui";
|
|
4
3
|
import { useTranslation } from "../hooks";
|
|
5
4
|
|
|
6
5
|
export function LanguageToggle() {
|
|
@@ -15,49 +14,42 @@ export function LanguageToggle() {
|
|
|
15
14
|
</IconButton>}>
|
|
16
15
|
<MenuItem onClick={() => i18n.changeLanguage("en")}>
|
|
17
16
|
<div className="flex w-full items-center justify-between gap-4">
|
|
18
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
19
17
|
<Typography variant="body2" className={i18n.language === "en" ? "font-bold" : ""}>English</Typography>
|
|
20
18
|
{i18n.language === "en" && <CheckIcon size={iconSize.small}/>}
|
|
21
19
|
</div>
|
|
22
20
|
</MenuItem>
|
|
23
21
|
<MenuItem onClick={() => i18n.changeLanguage("es")}>
|
|
24
22
|
<div className="flex w-full items-center justify-between gap-4">
|
|
25
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
26
23
|
<Typography variant="body2" className={i18n.language === "es" ? "font-bold" : ""}>Español</Typography>
|
|
27
24
|
{i18n.language === "es" && <CheckIcon size={iconSize.small}/>}
|
|
28
25
|
</div>
|
|
29
26
|
</MenuItem>
|
|
30
27
|
<MenuItem onClick={() => i18n.changeLanguage("de")}>
|
|
31
28
|
<div className="flex w-full items-center justify-between gap-4">
|
|
32
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
33
29
|
<Typography variant="body2" className={i18n.language === "de" ? "font-bold" : ""}>Deutsch</Typography>
|
|
34
30
|
{i18n.language === "de" && <CheckIcon size={iconSize.small}/>}
|
|
35
31
|
</div>
|
|
36
32
|
</MenuItem>
|
|
37
33
|
<MenuItem onClick={() => i18n.changeLanguage("fr")}>
|
|
38
34
|
<div className="flex w-full items-center justify-between gap-4">
|
|
39
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
40
35
|
<Typography variant="body2" className={i18n.language === "fr" ? "font-bold" : ""}>Français</Typography>
|
|
41
36
|
{i18n.language === "fr" && <CheckIcon size={iconSize.small}/>}
|
|
42
37
|
</div>
|
|
43
38
|
</MenuItem>
|
|
44
39
|
<MenuItem onClick={() => i18n.changeLanguage("it")}>
|
|
45
40
|
<div className="flex w-full items-center justify-between gap-4">
|
|
46
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
47
41
|
<Typography variant="body2" className={i18n.language === "it" ? "font-bold" : ""}>Italiano</Typography>
|
|
48
42
|
{i18n.language === "it" && <CheckIcon size={iconSize.small}/>}
|
|
49
43
|
</div>
|
|
50
44
|
</MenuItem>
|
|
51
45
|
<MenuItem onClick={() => i18n.changeLanguage("hi")}>
|
|
52
46
|
<div className="flex w-full items-center justify-between gap-4">
|
|
53
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
54
47
|
<Typography variant="body2" className={i18n.language === "hi" ? "font-bold" : ""}>हिन्दी</Typography>
|
|
55
48
|
{i18n.language === "hi" && <CheckIcon size={iconSize.small}/>}
|
|
56
49
|
</div>
|
|
57
50
|
</MenuItem>
|
|
58
51
|
<MenuItem onClick={() => i18n.changeLanguage("pt")}>
|
|
59
52
|
<div className="flex w-full items-center justify-between gap-4">
|
|
60
|
-
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
61
53
|
<Typography variant="body2" className={i18n.language === "pt" ? "font-bold" : ""}>Português</Typography>
|
|
62
54
|
{i18n.language === "pt" && <CheckIcon size={iconSize.small}/>}
|
|
63
55
|
</div>
|
|
@@ -18,8 +18,22 @@ declare global {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
|
|
21
|
+
import {
|
|
22
|
+
ArrowLeftIcon,
|
|
23
|
+
Button,
|
|
24
|
+
cls,
|
|
25
|
+
IconButton,
|
|
26
|
+
iconSize,
|
|
27
|
+
LoadingButton,
|
|
28
|
+
MailIcon,
|
|
29
|
+
Menu,
|
|
30
|
+
MenuItem,
|
|
31
|
+
MoonIcon,
|
|
32
|
+
SunIcon,
|
|
33
|
+
SunMoonIcon,
|
|
34
|
+
TextField,
|
|
35
|
+
Typography
|
|
36
|
+
} from "@rebasepro/ui";
|
|
23
37
|
import { AuthControllerExtended, User } from "@rebasepro/types";
|
|
24
38
|
import { ErrorView } from "../ErrorView";
|
|
25
39
|
import { RebaseLogo } from "../RebaseLogo";
|
|
@@ -73,11 +87,7 @@ export interface LoginViewProps {
|
|
|
73
87
|
*/
|
|
74
88
|
notAllowedError?: string | Error;
|
|
75
89
|
|
|
76
|
-
|
|
77
|
-
* Override: enable Google login button.
|
|
78
|
-
* If not set, checks `authController.capabilities.googleLogin`.
|
|
79
|
-
*/
|
|
80
|
-
googleEnabled?: boolean;
|
|
90
|
+
|
|
81
91
|
|
|
82
92
|
/**
|
|
83
93
|
* Google client ID for Google OAuth.
|
|
@@ -112,7 +122,6 @@ export function LoginView({
|
|
|
112
122
|
disableSignupScreen = false,
|
|
113
123
|
disabled = false,
|
|
114
124
|
notAllowedError,
|
|
115
|
-
googleEnabled,
|
|
116
125
|
googleClientId,
|
|
117
126
|
needsSetup,
|
|
118
127
|
registrationEnabled
|
|
@@ -140,7 +149,7 @@ export function LoginView({
|
|
|
140
149
|
?? ("needsSetup" in authController && !!(authController as { needsSetup?: boolean }).needsSetup)
|
|
141
150
|
?? false;
|
|
142
151
|
const canRegister = registrationEnabled ?? caps.registration ?? false;
|
|
143
|
-
const hasGoogleLogin =
|
|
152
|
+
const hasGoogleLogin = googleClientId && (caps.enabledProviders?.includes("google") ?? caps.googleLogin ?? false);
|
|
144
153
|
const hasPasswordReset = caps.passwordReset ?? !!authController.forgotPassword;
|
|
145
154
|
|
|
146
155
|
const showRegistration = !disableSignupScreen && canRegister;
|
|
@@ -190,7 +199,7 @@ export function LoginView({
|
|
|
190
199
|
return (
|
|
191
200
|
<div
|
|
192
201
|
className={cls(
|
|
193
|
-
"relative flex items-center justify-center h-screen w-screen p-4 transition-opacity duration-500 bg-
|
|
202
|
+
"relative flex items-center justify-center h-screen w-screen p-4 transition-opacity duration-500 bg-surface-50 dark:bg-surface-800",
|
|
194
203
|
fadeIn ? "opacity-100" : "opacity-0"
|
|
195
204
|
)}>
|
|
196
205
|
|
|
@@ -443,7 +452,7 @@ function LoginForm({
|
|
|
443
452
|
useEffect(() => {
|
|
444
453
|
if (!document) return;
|
|
445
454
|
const escFunction = (event: KeyboardEvent) => {
|
|
446
|
-
if (event.
|
|
455
|
+
if (event.key === "Escape") {
|
|
447
456
|
onClose();
|
|
448
457
|
}
|
|
449
458
|
};
|
|
@@ -631,7 +640,7 @@ function ForgotPasswordForm({
|
|
|
631
640
|
useEffect(() => {
|
|
632
641
|
if (!document) return;
|
|
633
642
|
const escFunction = (event: KeyboardEvent) => {
|
|
634
|
-
if (event.
|
|
643
|
+
if (event.key === "Escape") {
|
|
635
644
|
onClose();
|
|
636
645
|
}
|
|
637
646
|
};
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
CircleUserIcon,
|
|
5
|
+
CircularProgress,
|
|
6
|
+
cls,
|
|
7
|
+
defaultBorderMixin,
|
|
8
|
+
IconButton,
|
|
9
|
+
iconSize,
|
|
10
|
+
Popover,
|
|
11
|
+
SearchBar,
|
|
12
|
+
SearchIcon,
|
|
13
|
+
Typography,
|
|
14
|
+
XIcon
|
|
15
|
+
} from "@rebasepro/ui";
|
|
5
16
|
import { User } from "@rebasepro/types";
|
|
6
17
|
|
|
7
18
|
/**
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useEffect, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
Avatar,
|
|
5
|
+
Button,
|
|
6
|
+
CircularProgress,
|
|
7
|
+
IconButton,
|
|
8
|
+
Tab,
|
|
9
|
+
Tabs,
|
|
10
|
+
TextField,
|
|
11
|
+
Trash2Icon,
|
|
12
|
+
Typography
|
|
13
|
+
} from "@rebasepro/ui";
|
|
5
14
|
import { useAuthController, useTranslation } from "../hooks";
|
|
6
15
|
|
|
7
16
|
interface SessionInfo {
|
|
@@ -22,7 +31,7 @@ interface ExtendedAuthController {
|
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
export function UserSettingsView() {
|
|
25
|
-
const authController = useAuthController() as
|
|
34
|
+
const authController = useAuthController() as ExtendedAuthController;
|
|
26
35
|
const user = authController.user;
|
|
27
36
|
const { t } = useTranslation();
|
|
28
37
|
|
|
@@ -270,7 +270,7 @@ export function useDataTableController<M extends Record<string, any> = any, USER
|
|
|
270
270
|
limit: itemCount,
|
|
271
271
|
orderBy: orderByParams,
|
|
272
272
|
searchString
|
|
273
|
-
}, (
|
|
273
|
+
}, (res) => onEntitiesUpdate(res.data as Entity<M>[]), onError);
|
|
274
274
|
} else {
|
|
275
275
|
accessor.find({
|
|
276
276
|
where: whereParams,
|
|
@@ -278,7 +278,7 @@ export function useDataTableController<M extends Record<string, any> = any, USER
|
|
|
278
278
|
orderBy: orderByParams,
|
|
279
279
|
searchString
|
|
280
280
|
})
|
|
281
|
-
.then((
|
|
281
|
+
.then((res) => onEntitiesUpdate(res.data as Entity<M>[]))
|
|
282
282
|
.catch(onError);
|
|
283
283
|
unsubscribe = () => undefined;
|
|
284
284
|
}
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
import React, { PropsWithChildren } from "react";
|
|
4
4
|
import { SnackbarProvider as NotistackSnackbarProvider } from "notistack";
|
|
5
5
|
|
|
6
|
-
export const SnackbarProvider: React.FC<PropsWithChildren
|
|
6
|
+
export const SnackbarProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
|
7
7
|
|
|
8
8
|
return (
|
|
9
9
|
<NotistackSnackbarProvider maxSnack={3}
|
|
10
|
+
preventDuplicate={true}
|
|
10
11
|
autoHideDuration={3500}>
|
|
11
12
|
{children}
|
|
12
13
|
</NotistackSnackbarProvider>
|
|
@@ -58,7 +58,6 @@ export function PluginLifecycleManager({
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
// Only run on mount/unmount — plugins array identity should be stable
|
|
61
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
61
|
}, []);
|
|
63
62
|
|
|
64
63
|
// ── Auth state change lifecycle ──────────────────────────────────
|
package/src/core/Rebase.tsx
CHANGED
|
@@ -89,7 +89,7 @@ export function Rebase<USER extends User>(props: RebaseProps<USER>) {
|
|
|
89
89
|
loading: false,
|
|
90
90
|
users: [],
|
|
91
91
|
getUser: (uid: string) => null
|
|
92
|
-
} as
|
|
92
|
+
} as UserManagementDelegate<USER>, [plugins, _userManagement]);
|
|
93
93
|
|
|
94
94
|
// Auth fallback logic
|
|
95
95
|
const clientAuthController = useAuthSubscription(authControllerProp ? undefined : client?.auth);
|
package/src/hooks/data/save.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { RebaseData } from "@rebasepro/types";
|
|
|
5
5
|
/**
|
|
6
6
|
* @group Hooks and utilities
|
|
7
7
|
*/
|
|
8
|
-
export type SaveEntityWithCallbacksProps<M extends Record<string,
|
|
8
|
+
export type SaveEntityWithCallbacksProps<M extends Record<string, unknown>> =
|
|
9
9
|
SaveEntityProps<M> &
|
|
10
10
|
{
|
|
11
11
|
afterSave?: (updatedEntity: Entity<M>) => void,
|
|
@@ -31,7 +31,7 @@ export type SaveEntityWithCallbacksProps<M extends Record<string, any>> =
|
|
|
31
31
|
* @param afterSaveError
|
|
32
32
|
* @group Hooks and utilities
|
|
33
33
|
*/
|
|
34
|
-
export async function saveEntityWithCallbacks<M extends Record<string,
|
|
34
|
+
export async function saveEntityWithCallbacks<M extends Record<string, unknown>>({
|
|
35
35
|
collection,
|
|
36
36
|
path,
|
|
37
37
|
entityId,
|
|
@@ -57,9 +57,9 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>>({
|
|
|
57
57
|
|
|
58
58
|
let savePromise: Promise<Entity<M>>;
|
|
59
59
|
if (status === "new" || status === "copy") {
|
|
60
|
-
savePromise = accessor.create(values, entityId) as
|
|
60
|
+
savePromise = accessor.create(values, entityId) as Promise<Entity<M>>;
|
|
61
61
|
} else {
|
|
62
|
-
savePromise = accessor.update(entityId!, values) as
|
|
62
|
+
savePromise = accessor.update(entityId!, values) as Promise<Entity<M>>;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return savePromise.then((entity) => {
|
|
@@ -125,7 +125,7 @@ export function useCollectionFetch<M extends Record<string, any>, USER extends U
|
|
|
125
125
|
|
|
126
126
|
// Eagerly include relations to avoid N+1 fetches.
|
|
127
127
|
const hasRelations = collection.properties && Object.values(collection.properties).some(
|
|
128
|
-
(p
|
|
128
|
+
(p) => p.type === "relation" || p.type === "reference"
|
|
129
129
|
);
|
|
130
130
|
const includeParams = hasRelations ? ["*"] : undefined;
|
|
131
131
|
|
|
@@ -136,7 +136,7 @@ export function useCollectionFetch<M extends Record<string, any>, USER extends U
|
|
|
136
136
|
orderBy: orderByParams,
|
|
137
137
|
searchString,
|
|
138
138
|
include: includeParams
|
|
139
|
-
}, onEntitiesUpdate as
|
|
139
|
+
}, (res) => onEntitiesUpdate({ data: res.data as Entity<M>[], meta: res.meta }), onError);
|
|
140
140
|
} else {
|
|
141
141
|
accessor.find({
|
|
142
142
|
where: whereParams,
|
|
@@ -145,12 +145,12 @@ export function useCollectionFetch<M extends Record<string, any>, USER extends U
|
|
|
145
145
|
searchString,
|
|
146
146
|
include: includeParams
|
|
147
147
|
})
|
|
148
|
-
.then(onEntitiesUpdate as
|
|
148
|
+
.then((res) => onEntitiesUpdate({ data: res.data as Entity<M>[], meta: res.meta }))
|
|
149
149
|
.catch(onError);
|
|
150
150
|
return () => {
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
-
}, [path, itemCount, currentSort, sortByProperty, filterValues, searchString]);
|
|
153
|
+
}, [path, itemCount, currentSort, sortByProperty, filterValues, searchString, dataClient, collection]);
|
|
154
154
|
|
|
155
155
|
return useMemo(() => ({
|
|
156
156
|
data,
|
|
@@ -39,6 +39,16 @@ export function populateEntityFetchCache<M extends Record<string, any>>(path: st
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Clear the entity fetch cache. Call this on auth state changes (e.g. logout)
|
|
44
|
+
* to prevent stale data from a previous session leaking into the next.
|
|
45
|
+
*/
|
|
46
|
+
export function clearEntityFetchCache(): void {
|
|
47
|
+
for (const key of Object.keys(CACHE)) {
|
|
48
|
+
delete CACHE[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
42
52
|
/**
|
|
43
53
|
* This hook is used to fetch an entity.
|
|
44
54
|
* It gives real time updates if the driver supports it.
|
|
@@ -104,10 +114,10 @@ export function useEntityFetch<M extends Record<string, any>, USER extends User
|
|
|
104
114
|
const accessor = dataClient.collection(path);
|
|
105
115
|
|
|
106
116
|
if (accessor.listenById) {
|
|
107
|
-
return accessor.listenById(entityId, onEntityUpdate as
|
|
117
|
+
return accessor.listenById(entityId, (entity) => onEntityUpdate(entity as Entity<M> | undefined), onError);
|
|
108
118
|
} else {
|
|
109
119
|
accessor.findById(entityId)
|
|
110
|
-
.then(onEntityUpdate as
|
|
120
|
+
.then((entity) => onEntityUpdate(entity as Entity<M> | undefined))
|
|
111
121
|
.catch(onError);
|
|
112
122
|
return () => {
|
|
113
123
|
};
|
|
@@ -119,7 +129,7 @@ export function useEntityFetch<M extends Record<string, any>, USER extends User
|
|
|
119
129
|
return () => {
|
|
120
130
|
};
|
|
121
131
|
}
|
|
122
|
-
}, [entityId, path]);
|
|
132
|
+
}, [entityId, path, dataClient, collection, useCache, databaseId]);
|
|
123
133
|
|
|
124
134
|
return useMemo(() => ({
|
|
125
135
|
entity,
|
|
@@ -181,7 +181,7 @@ export function useRelationSelector<M extends Record<string, any> = any>(
|
|
|
181
181
|
limit: limit,
|
|
182
182
|
orderBy: undefined,
|
|
183
183
|
searchString: currentSearch
|
|
184
|
-
}, onEntitiesUpdate as
|
|
184
|
+
}, (res) => onEntitiesUpdate({ data: res.data as Entity<M>[], meta: res.meta }), onErrorUpdate);
|
|
185
185
|
} else {
|
|
186
186
|
accessor.find({
|
|
187
187
|
where: whereParams,
|
|
@@ -190,7 +190,7 @@ export function useRelationSelector<M extends Record<string, any> = any>(
|
|
|
190
190
|
orderBy: undefined,
|
|
191
191
|
searchString: currentSearch
|
|
192
192
|
})
|
|
193
|
-
.then(onEntitiesUpdate as
|
|
193
|
+
.then((res) => onEntitiesUpdate({ data: res.data as Entity<M>[], meta: res.meta }))
|
|
194
194
|
.catch(onErrorUpdate);
|
|
195
195
|
unsubscribe = () => {};
|
|
196
196
|
}
|
|
@@ -13,7 +13,7 @@ import { isLazyComponentRef } from "@rebasepro/types";
|
|
|
13
13
|
* to the `React.lazy()` wrapper it produced. Strings are keyed by a separate
|
|
14
14
|
* plain Map since they can't be WeakMap keys.
|
|
15
15
|
*/
|
|
16
|
-
const lazyCache = new WeakMap<object |
|
|
16
|
+
const lazyCache = new WeakMap<object | ((..._args: any[]) => any), React.ComponentType<any>>();
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Resolves a `ComponentRef` into a renderable `React.ComponentType`.
|
|
@@ -50,7 +50,7 @@ const lazyCache = new WeakMap<object | Function, React.ComponentType<any>>();
|
|
|
50
50
|
* </Suspense>
|
|
51
51
|
* );
|
|
52
52
|
* ```
|
|
53
|
-
*/
|
|
53
|
+
* */
|
|
54
54
|
export function useResolvedComponent<P = unknown>(
|
|
55
55
|
ref: ComponentRef<P> | undefined
|
|
56
56
|
): React.ComponentType<P> | undefined {
|
|
@@ -62,13 +62,14 @@ export function useResolvedComponent<P = unknown>(
|
|
|
62
62
|
* same loader always returns the same lazy component identity.
|
|
63
63
|
*/
|
|
64
64
|
function getOrCreateLazy<P>(
|
|
65
|
-
key: object |
|
|
65
|
+
key: object | ((..._args: any[]) => any),
|
|
66
66
|
loader: () => Promise<{ default: React.ComponentType<P> }>
|
|
67
67
|
): React.ComponentType<P> {
|
|
68
68
|
const cached = lazyCache.get(key);
|
|
69
69
|
if (cached) return cached as React.ComponentType<P>;
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
// SAFETY: React.lazy returns LazyExoticComponent which is structurally compatible with ComponentType<P>
|
|
72
|
+
const LazyComponent = lazy(loader) as React.ComponentType<P>;
|
|
72
73
|
lazyCache.set(key, LazyComponent);
|
|
73
74
|
return LazyComponent;
|
|
74
75
|
}
|
|
@@ -100,14 +101,14 @@ export function resolveComponentRef<P = unknown>(
|
|
|
100
101
|
// The object has { __rebaseLazy: true, load: () => import(...) }.
|
|
101
102
|
if (isLazyComponentRef(ref)) {
|
|
102
103
|
return getOrCreateLazy<P>(
|
|
103
|
-
ref as
|
|
104
|
+
ref as object,
|
|
104
105
|
() => (ref as LazyComponentRef<P>).load()
|
|
105
106
|
);
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
// 3. Function — either a React component or a lazy import loader.
|
|
109
110
|
if (typeof ref === "function") {
|
|
110
|
-
const fn = ref as
|
|
111
|
+
const fn = ref as (..._args: any[]) => any;
|
|
111
112
|
|
|
112
113
|
// Class components (React.Component / PureComponent) have this flag
|
|
113
114
|
if (fn.prototype?.isReactComponent) {
|
package/src/locales/en.ts
CHANGED
|
@@ -25,6 +25,8 @@ export const en: RebaseTranslations = {
|
|
|
25
25
|
copy: "Copy",
|
|
26
26
|
delete: "Delete",
|
|
27
27
|
delete_not_allowed: "You have selected at least one entity you cannot delete",
|
|
28
|
+
edit_entity: "Edit",
|
|
29
|
+
back_to_detail: "Back to details",
|
|
28
30
|
|
|
29
31
|
// ─── Delete dialog ───────────────────────────────────────────
|
|
30
32
|
delete_confirmation_title: "Delete?",
|
|
@@ -414,6 +416,12 @@ export const en: RebaseTranslations = {
|
|
|
414
416
|
|
|
415
417
|
no_filterable_properties: "No filterable properties available",
|
|
416
418
|
apply_filters: "Apply filters",
|
|
419
|
+
|
|
420
|
+
// ─── Filter Presets ──────────────────────────────────────────
|
|
421
|
+
filter_presets: "Presets",
|
|
422
|
+
filter_preset_apply: "Apply preset",
|
|
423
|
+
filter_preset_active: "Active: {{label}}",
|
|
424
|
+
|
|
417
425
|
list: "List",
|
|
418
426
|
table_view_mode: "Table",
|
|
419
427
|
cards: "Cards",
|