Upgrade to react-admin v5 (#633)

* bump dependencies
* replace react-query with @tanstack/react-query
* remove darkTheme
* remove obsolete fullWidth
* fix pagination
* fix language for ra.navigation
* fix 'record' is possibly 'undefined'
* add missing @mui/utils
* bump yarn to 4.4.x
This commit is contained in:
Dirk Klimpel 2025-05-09 20:53:41 +02:00 committed by GitHub
parent d5c10b6e02
commit e8e28b5df1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1917 additions and 1789 deletions

File diff suppressed because one or more lines are too long

925
.yarn/releases/yarn-4.4.1.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
yarnPath: .yarn/releases/yarn-4.1.1.cjs yarnPath: .yarn/releases/yarn-4.4.1.cjs

View File

@ -10,9 +10,10 @@
"type": "git", "type": "git",
"url": "https://github.com/Awesome-Technologies/synapse-admin" "url": "https://github.com/Awesome-Technologies/synapse-admin"
}, },
"packageManager": "yarn@4.1.1", "packageManager": "yarn@4.4.1",
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.7.0", "@eslint/js": "^9.7.0",
"@mui/utils": "^6.1.3",
"@testing-library/dom": "^10.4.0", "@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.0.0", "@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^16.0.0", "@testing-library/react": "^16.0.0",
@ -51,25 +52,25 @@
"@haxqer/ra-language-chinese": "^4.16.2", "@haxqer/ra-language-chinese": "^4.16.2",
"@mui/icons-material": "^5.16.4", "@mui/icons-material": "^5.16.4",
"@mui/material": "^5.16.4", "@mui/material": "^5.16.4",
"@tanstack/react-query": "^5.59.12",
"history": "^5.3.0", "history": "^5.3.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"query-string": "^7.1.3", "query-string": "^7.1.3",
"ra-core": "^4.16.20", "ra-core": "^5.2.3",
"ra-i18n-polyglot": "^4.16.20", "ra-i18n-polyglot": "^5.2.3",
"ra-language-english": "^4.16.20", "ra-language-english": "^5.2.3",
"ra-language-farsi": "^4.2.0", "ra-language-farsi": "^5.0.0",
"ra-language-french": "^4.16.20", "ra-language-french": "^5.2.3",
"ra-language-italian": "^3.13.1", "ra-language-italian": "^3.13.1",
"ra-language-russian": "^4.14.2", "ra-language-russian": "^4.14.2",
"react": "^18.3.1", "react": "^18.3.1",
"react-admin": "^4.16.20", "react-admin": "^5.2.3",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.52.1", "react-hook-form": "^7.52.1",
"react-is": "^18.3.1", "react-is": "^18.3.1",
"react-query": "^3.39.3", "react-router": "^6.28.1",
"react-router": "^6.25.1", "react-router-dom": "^6.28.1"
"react-router-dom": "^6.25.1"
}, },
"scripts": { "scripts": {
"start": "vite serve", "start": "vite serve",

View File

@ -1,4 +1,6 @@
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import fetchMock from "jest-fetch-mock";
fetchMock.enableMocks();
import App from "./App"; import App from "./App";

View File

@ -53,7 +53,6 @@ const App = () => (
authProvider={authProvider} authProvider={authProvider}
dataProvider={dataProvider} dataProvider={dataProvider}
i18nProvider={i18nProvider} i18nProvider={i18nProvider}
darkTheme={{ palette: { mode: "dark" } }}
> >
<CustomRoutes> <CustomRoutes>
<Route path="/import_users" element={<ImportFeature />} /> <Route path="/import_users" element={<ImportFeature />} />

View File

@ -20,7 +20,7 @@ import {
useTranslate, useTranslate,
useUnselectAll, useUnselectAll,
} from "react-admin"; } from "react-admin";
import { useMutation } from "react-query"; import { useMutation } from "@tanstack/react-query";
const ServerNoticeDialog = ({ open, onClose, onSubmit }) => { const ServerNoticeDialog = ({ open, onClose, onSubmit }) => {
const translate = useTranslate(); const translate = useTranslate();
@ -43,7 +43,6 @@ const ServerNoticeDialog = ({ open, onClose, onSubmit }) => {
<TextInput <TextInput
source="body" source="body"
label="resources.servernotices.fields.body" label="resources.servernotices.fields.body"
fullWidth
multiline multiline
rows="4" rows="4"
resettable resettable
@ -64,6 +63,10 @@ export const ServerNoticeButton = () => {
const handleDialogOpen = () => setOpen(true); const handleDialogOpen = () => setOpen(true);
const handleDialogClose = () => setOpen(false); const handleDialogClose = () => setOpen(false);
if (!record) {
return;
}
const handleSend = (values: Partial<RaRecord>) => { const handleSend = (values: Partial<RaRecord>) => {
create( create(
"servernotices", "servernotices",
@ -100,28 +103,26 @@ export const ServerNoticeBulkButton = () => {
const unselectAllUsers = useUnselectAll("users"); const unselectAllUsers = useUnselectAll("users");
const dataProvider = useDataProvider(); const dataProvider = useDataProvider();
const { mutate: sendNotices, isLoading } = useMutation( const { mutate: sendNotices, isPending } = useMutation({
data => mutationFn: (data) =>
dataProvider.createMany("servernotices", { dataProvider.createMany("servernotices", {
ids: selectedIds, ids: selectedIds,
data: data, data: data,
}), }),
{ onSuccess: () => {
onSuccess: () => { notify("resources.servernotices.action.send_success");
notify("resources.servernotices.action.send_success"); unselectAllUsers();
unselectAllUsers(); closeDialog();
closeDialog(); },
}, onError: () =>
onError: () => notify("resources.servernotices.action.send_failure", {
notify("resources.servernotices.action.send_failure", { type: "error",
type: "error", }),
}), });
}
);
return ( return (
<> <>
<Button label="resources.servernotices.send" onClick={openDialog} disabled={isLoading}> <Button label="resources.servernotices.send" onClick={openDialog} disabled={isPending}>
<MessageIcon /> <MessageIcon />
</Button> </Button>
<ServerNoticeDialog open={open} onClose={closeDialog} onSubmit={sendNotices} /> <ServerNoticeDialog open={open} onClose={closeDialog} onSubmit={sendNotices} />

View File

@ -28,7 +28,7 @@ import {
useRefresh, useRefresh,
useTranslate, useTranslate,
} from "react-admin"; } from "react-admin";
import { useMutation } from "react-query"; import { useMutation } from "@tanstack/react-query";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { dateParser } from "./date"; import { dateParser } from "./date";
@ -55,14 +55,12 @@ const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
<DialogContentText>{translate("delete_media.helper.send")}</DialogContentText> <DialogContentText>{translate("delete_media.helper.send")}</DialogContentText>
<SimpleForm toolbar={<DeleteMediaToolbar />} onSubmit={onSubmit}> <SimpleForm toolbar={<DeleteMediaToolbar />} onSubmit={onSubmit}>
<DateTimeInput <DateTimeInput
fullWidth
source="before_ts" source="before_ts"
label="delete_media.fields.before_ts" label="delete_media.fields.before_ts"
defaultValue={0} defaultValue={0}
parse={dateParser} parse={dateParser}
/> />
<NumberInput <NumberInput
fullWidth
source="size_gt" source="size_gt"
label="delete_media.fields.size_gt" label="delete_media.fields.size_gt"
defaultValue={0} defaultValue={0}
@ -70,7 +68,6 @@ const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
step={1024} step={1024}
/> />
<BooleanInput <BooleanInput
fullWidth
source="keep_profiles" source="keep_profiles"
label="delete_media.fields.keep_profiles" label="delete_media.fields.keep_profiles"
defaultValue={true} defaultValue={true}
@ -86,20 +83,18 @@ export const DeleteMediaButton = (props: ButtonProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const notify = useNotify(); const notify = useNotify();
const dataProvider = useDataProvider<SynapseDataProvider>(); const dataProvider = useDataProvider<SynapseDataProvider>();
const { mutate: deleteMedia, isLoading } = useMutation( const { mutate: deleteMedia, isPending } = useMutation({
(values: DeleteMediaParams) => dataProvider.deleteMedia(values), mutationFn: (values: DeleteMediaParams) => dataProvider.deleteMedia(values),
{ onSuccess: () => {
onSuccess: () => { notify("delete_media.action.send_success");
notify("delete_media.action.send_success"); closeDialog();
closeDialog(); },
}, onError: () => {
onError: () => { notify("delete_media.action.send_failure", {
notify("delete_media.action.send_failure", { type: "error",
type: "error", });
}); },
}, });
}
);
const openDialog = () => setOpen(true); const openDialog = () => setOpen(true);
const closeDialog = () => setOpen(false); const closeDialog = () => setOpen(false);
@ -110,7 +105,7 @@ export const DeleteMediaButton = (props: ButtonProps) => {
{...props} {...props}
label="delete_media.action.send" label="delete_media.action.send"
onClick={openDialog} onClick={openDialog}
disabled={isLoading} disabled={isPending}
sx={{ sx={{
color: theme.palette.error.main, color: theme.palette.error.main,
"&:hover": { "&:hover": {

View File

@ -4,6 +4,14 @@ import { SynapseTranslationMessages } from ".";
const de: SynapseTranslationMessages = { const de: SynapseTranslationMessages = {
...formalGermanMessages, ...formalGermanMessages,
ra: {
...formalGermanMessages.ra,
navigation: {
...formalGermanMessages.ra.navigation,
no_filtered_results: "Keine Ergebnisse",
clear_filters: "Alle Filter entfernen",
},
},
synapseadmin: { synapseadmin: {
auth: { auth: {
base_url: "Heimserver URL", base_url: "Heimserver URL",

View File

@ -4,6 +4,14 @@ import { SynapseTranslationMessages } from ".";
const ru: SynapseTranslationMessages = { const ru: SynapseTranslationMessages = {
...russianMessages, ...russianMessages,
ra: {
...russianMessages.ra,
navigation: {
...russianMessages.ra.navigation,
no_filtered_results: "Нет результатов",
clear_filters: "Все фильтры сбросить",
},
},
synapseadmin: { synapseadmin: {
auth: { auth: {
base_url: "Адрес домашнего сервера", base_url: "Адрес домашнего сервера",

View File

@ -4,6 +4,14 @@ import { SynapseTranslationMessages } from ".";
const zh: SynapseTranslationMessages = { const zh: SynapseTranslationMessages = {
...chineseMessages, ...chineseMessages,
ra: {
...chineseMessages.ra,
navigation: {
...chineseMessages.ra.navigation,
no_filtered_results: "没有结果",
clear_filters: "清除所有过滤器",
},
},
synapseadmin: { synapseadmin: {
auth: { auth: {
base_url: "服务器 URL", base_url: "服务器 URL",

View File

@ -217,7 +217,6 @@ const LoginPage = () => {
disabled={loading || !supportPassAuth} disabled={loading || !supportPassAuth}
onBlur={handleUsernameChange} onBlur={handleUsernameChange}
resettable resettable
fullWidth
validate={required()} validate={required()}
/> />
</Box> </Box>
@ -229,7 +228,6 @@ const LoginPage = () => {
autoComplete="current-password" autoComplete="current-password"
disabled={loading || !supportPassAuth} disabled={loading || !supportPassAuth}
resettable resettable
fullWidth
validate={required()} validate={required()}
/> />
</Box> </Box>
@ -242,7 +240,6 @@ const LoginPage = () => {
disabled={loading} disabled={loading}
readOnly={allowSingleBaseUrl} readOnly={allowSingleBaseUrl}
resettable={allowAnyBaseUrl} resettable={allowAnyBaseUrl}
fullWidth
validate={[required(), validateBaseUrl]} validate={[required(), validateBaseUrl]}
> >
{allowMultipleBaseUrls && {allowMultipleBaseUrls &&

View File

@ -25,7 +25,7 @@ import {
useRefresh, useRefresh,
useUnselectAll, useUnselectAll,
} from "react-admin"; } from "react-admin";
import { useMutation } from "react-query"; import { useMutation } from "@tanstack/react-query";
import AvatarField from "../components/AvatarField"; import AvatarField from "../components/AvatarField";
@ -70,27 +70,25 @@ export const RoomDirectoryBulkPublishButton = (props: ButtonProps) => {
const refresh = useRefresh(); const refresh = useRefresh();
const unselectAllRooms = useUnselectAll("rooms"); const unselectAllRooms = useUnselectAll("rooms");
const dataProvider = useDataProvider(); const dataProvider = useDataProvider();
const { mutate, isLoading } = useMutation( const { mutate, isPending } = useMutation({
() => mutationFn: () =>
dataProvider.createMany("room_directory", { dataProvider.createMany("room_directory", {
ids: selectedIds, ids: selectedIds,
data: {}, data: {},
}), }),
{ onSuccess: () => {
onSuccess: () => { notify("resources.room_directory.action.send_success");
notify("resources.room_directory.action.send_success"); unselectAllRooms();
unselectAllRooms(); refresh();
refresh(); },
}, onError: () =>
onError: () => notify("resources.room_directory.action.send_failure", {
notify("resources.room_directory.action.send_failure", { type: "error",
type: "error", }),
}), });
}
);
return ( return (
<Button {...props} label="resources.room_directory.action.create" onClick={mutate} disabled={isLoading}> <Button {...props} label="resources.room_directory.action.create" onClick={mutate} disabled={isPending}>
<RoomDirectoryIcon /> <RoomDirectoryIcon />
</Button> </Button>
); );
@ -102,6 +100,10 @@ export const RoomDirectoryPublishButton = (props: ButtonProps) => {
const refresh = useRefresh(); const refresh = useRefresh();
const [create, { isLoading }] = useCreate(); const [create, { isLoading }] = useCreate();
if (!record) {
return;
}
const handleSend = () => { const handleSend = () => {
create( create(
"room_directory", "room_directory",

View File

@ -1,7 +1,14 @@
import { stringify } from "query-string"; import { stringify } from "query-string";
import {
import { DataProvider, DeleteParams, Identifier, Options, RaRecord, fetchUtils } from "react-admin"; DataProvider,
DeleteParams,
Identifier,
Options,
PaginationPayload,
RaRecord,
SortPayload,
fetchUtils
} from "react-admin";
import storage from "../storage"; import storage from "../storage";
// Adds the access token to all requests // Adds the access token to all requests
@ -492,8 +499,8 @@ const dataProvider: SynapseDataProvider = {
getList: async (resource, params) => { getList: async (resource, params) => {
console.log("getList " + resource); console.log("getList " + resource);
const { user_id, name, guests, deactivated, locked, search_term, destination, valid } = params.filter; const { user_id, name, guests, deactivated, locked, search_term, destination, valid } = params.filter;
const { page, perPage } = params.pagination; const { page, perPage } = params.pagination as PaginationPayload;
const { field, order } = params.sort; const { field, order } = params.sort as SortPayload;
const from = (page - 1) * perPage; const from = (page - 1) * perPage;
const query = { const query = {
from: from, from: from,

1716
yarn.lock

File diff suppressed because it is too large Load Diff