mirror of
https://github.com/etkecc/synapse-admin.git
synced 2024-11-21 15:25:22 +03:00
Transform code base to typescript
Change-Id: Ia1f862fb5962ddd54b8d7643abbc39bb314d1f8e
This commit is contained in:
parent
03fcd8126a
commit
2466af6936
45 changed files with 1081 additions and 516 deletions
|
@ -119,7 +119,7 @@
|
||||||
<div class="loader">Loading...</div>
|
<div class="loader">Loading...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/src/index.jsx"></script>
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
<footer
|
<footer
|
||||||
style="position: relative; z-index: 2; height: 2em; margin-top: -2em; line-height: 2em; background-color: #eee; border: 0.5px solid #ddd">
|
style="position: relative; z-index: 2; height: 2em; margin-top: -2em; line-height: 2em; background-color: #eee; border: 0.5px solid #ddd">
|
||||||
<a id="copyright" href="https://github.com/Awesome-Technologies/synapse-admin"
|
<a id="copyright" href="https://github.com/Awesome-Technologies/synapse-admin"
|
||||||
|
|
13
jest.config.ts
Normal file
13
jest.config.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import type { JestConfigWithTsJest } from "ts-jest";
|
||||||
|
|
||||||
|
const config: JestConfigWithTsJest = {
|
||||||
|
preset: "ts-jest",
|
||||||
|
testEnvironment: "jsdom",
|
||||||
|
collectCoverage: true,
|
||||||
|
coveragePathIgnorePatterns: ["node_modules", "dist"],
|
||||||
|
coverageDirectory: "<rootDir>/coverage/",
|
||||||
|
coverageReporters: ["html", "text", "text-summary", "cobertura"],
|
||||||
|
extensionsToTreatAsEsm: [".ts", ".tsx"],
|
||||||
|
setupFilesAfterEnv: ["<rootDir>/src/jest.setup.ts"],
|
||||||
|
};
|
||||||
|
export default config;
|
33
package.json
33
package.json
|
@ -12,15 +12,16 @@
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.1.1",
|
"packageManager": "yarn@4.1.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.4",
|
|
||||||
"@babel/preset-env": "^7.24.4",
|
|
||||||
"@babel/preset-react": "^7.24.1",
|
|
||||||
"@testing-library/dom": "^10.0.0",
|
"@testing-library/dom": "^10.0.0",
|
||||||
"@testing-library/jest-dom": "^6.0.0",
|
"@testing-library/jest-dom": "^6.0.0",
|
||||||
"@testing-library/react": "^15.0.2",
|
"@testing-library/react": "^15.0.2",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/lodash": "^4.17.0",
|
||||||
|
"@types/node": "^20.12.7",
|
||||||
|
"@types/papaparse": "^5.3.14",
|
||||||
|
"@types/react": "^18.2.79",
|
||||||
"@vitejs/plugin-react": "^4.0.0",
|
"@vitejs/plugin-react": "^4.0.0",
|
||||||
"babel-jest": "^29.7.0",
|
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
|
@ -30,6 +31,9 @@
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"react-test-renderer": "^18.2.0",
|
"react-test-renderer": "^18.2.0",
|
||||||
|
"ts-jest": "^29.1.2",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.5",
|
||||||
"vite": "^5.0.0",
|
"vite": "^5.0.0",
|
||||||
"vite-plugin-version-mark": "^0.0.13"
|
"vite-plugin-version-mark": "^0.0.13"
|
||||||
},
|
},
|
||||||
|
@ -65,32 +69,15 @@
|
||||||
"fix:other": "yarn prettier --write",
|
"fix:other": "yarn prettier --write",
|
||||||
"fix:code": "yarn test:lint --fix",
|
"fix:code": "yarn test:lint --fix",
|
||||||
"fix": "yarn fix:code && yarn fix:other",
|
"fix": "yarn fix:code && yarn fix:other",
|
||||||
"prettier": "prettier \"**/*.{js,jsx,json,md,scss,yaml,yml}\"",
|
"prettier": "prettier \"**/*.{js,jsx,ts,tsx,json,md,scss,yaml,yml}\"",
|
||||||
"test:code": "jest",
|
"test:code": "jest",
|
||||||
"test:lint": "eslint --ignore-path .gitignore --ext .js,.jsx .",
|
"test:lint": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.jsx .",
|
||||||
"test:style": "yarn prettier --check",
|
"test:style": "yarn prettier --check",
|
||||||
"test": "yarn test:style && yarn test:lint && yarn test:code"
|
"test": "yarn test:style && yarn test:lint && yarn test:code"
|
||||||
},
|
},
|
||||||
"babel": {
|
|
||||||
"presets": [
|
|
||||||
"@babel/preset-env",
|
|
||||||
[
|
|
||||||
"@babel/preset-react",
|
|
||||||
{
|
|
||||||
"runtime": "automatic"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"testEnvironment": "jsdom",
|
|
||||||
"setupFilesAfterEnv": [
|
|
||||||
"<rootDir>/src/setupTests.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
Admin,
|
Admin,
|
||||||
CustomRoutes,
|
CustomRoutes,
|
||||||
|
@ -6,7 +5,7 @@ import {
|
||||||
resolveBrowserLocale,
|
resolveBrowserLocale,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import polyglotI18nProvider from "ra-i18n-polyglot";
|
import polyglotI18nProvider from "ra-i18n-polyglot";
|
||||||
import merge from "lodash/merge";
|
import { merge } from "lodash";
|
||||||
import authProvider from "./synapse/authProvider";
|
import authProvider from "./synapse/authProvider";
|
||||||
import dataProvider from "./synapse/dataProvider";
|
import dataProvider from "./synapse/dataProvider";
|
||||||
import users from "./components/users";
|
import users from "./components/users";
|
|
@ -1,5 +0,0 @@
|
||||||
import { createContext, useContext } from "react";
|
|
||||||
|
|
||||||
export const AppContext = createContext({});
|
|
||||||
|
|
||||||
export const useAppContext = () => useContext(AppContext);
|
|
9
src/AppContext.tsx
Normal file
9
src/AppContext.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
interface AppContextType {
|
||||||
|
restrictBaseUrl: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppContext = createContext({});
|
||||||
|
|
||||||
|
export const useAppContext = () => useContext(AppContext) as AppContextType;
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import { RecordContextProvider } from "react-admin";
|
import { RecordContextProvider } from "react-admin";
|
||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
import AvatarField from "./AvatarField";
|
import AvatarField from "./AvatarField";
|
|
@ -1,5 +1,4 @@
|
||||||
import React from "react";
|
import { get } from "lodash";
|
||||||
import get from "lodash/get";
|
|
||||||
import { Avatar } from "@mui/material";
|
import { Avatar } from "@mui/material";
|
||||||
import { useRecordContext } from "react-admin";
|
import { useRecordContext } from "react-admin";
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
Datagrid,
|
Datagrid,
|
||||||
DateField,
|
DateField,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
List,
|
List,
|
||||||
|
ListProps,
|
||||||
NumberField,
|
NumberField,
|
||||||
Pagination,
|
Pagination,
|
||||||
ReferenceField,
|
ReferenceField,
|
||||||
|
ResourceProps,
|
||||||
Show,
|
Show,
|
||||||
|
ShowProps,
|
||||||
Tab,
|
Tab,
|
||||||
TabbedShowLayout,
|
TabbedShowLayout,
|
||||||
TextField,
|
TextField,
|
||||||
|
@ -20,7 +22,7 @@ import PageviewIcon from "@mui/icons-material/Pageview";
|
||||||
import ReportIcon from "@mui/icons-material/Warning";
|
import ReportIcon from "@mui/icons-material/Warning";
|
||||||
import ViewListIcon from "@mui/icons-material/ViewList";
|
import ViewListIcon from "@mui/icons-material/ViewList";
|
||||||
|
|
||||||
const date_format = {
|
const date_format: Intl.DateTimeFormatOptions = {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
|
@ -33,7 +35,7 @@ const ReportPagination = () => (
|
||||||
<Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
<Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ReportShow = props => {
|
export const ReportShow = (props: ShowProps) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Show {...props} actions={<ReportShowActions />}>
|
<Show {...props} actions={<ReportShowActions />}>
|
||||||
|
@ -120,7 +122,7 @@ const ReportShowActions = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReportList = props => (
|
export const ReportList = (props: ListProps) => (
|
||||||
<List
|
<List
|
||||||
{...props}
|
{...props}
|
||||||
pagination={<ReportPagination />}
|
pagination={<ReportPagination />}
|
||||||
|
@ -141,7 +143,7 @@ export const ReportList = props => (
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "reports",
|
name: "reports",
|
||||||
icon: ReportIcon,
|
icon: ReportIcon,
|
||||||
list: ReportList,
|
list: ReportList,
|
|
@ -1,6 +1,10 @@
|
||||||
import React, { useState } from "react";
|
import { ChangeEvent, useState } from "react";
|
||||||
import { useDataProvider, useNotify, Title } from "react-admin";
|
import { useDataProvider, useNotify, RaRecord, Title } from "react-admin";
|
||||||
import { parse as parseCsv, unparse as unparseCsv } from "papaparse";
|
import {
|
||||||
|
parse as parseCsv,
|
||||||
|
unparse as unparseCsv,
|
||||||
|
ParseResult,
|
||||||
|
} from "papaparse";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
@ -12,36 +16,63 @@ import {
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
NativeSelect,
|
NativeSelect,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useTranslate } from "ra-core";
|
import { DataProvider, useTranslate } from "ra-core";
|
||||||
import { generateRandomUser } from "./users";
|
import { generateRandomUser } from "./users";
|
||||||
|
|
||||||
const LOGGING = true;
|
const LOGGING = true;
|
||||||
|
|
||||||
const expectedFields = ["id", "displayname"].sort();
|
const expectedFields = ["id", "displayname"].sort();
|
||||||
const optionalFields = [
|
|
||||||
"user_type",
|
|
||||||
"guest",
|
|
||||||
"admin",
|
|
||||||
"deactivated",
|
|
||||||
"avatar_url",
|
|
||||||
"password",
|
|
||||||
].sort();
|
|
||||||
|
|
||||||
function TranslatableOption({ value, text }) {
|
function TranslatableOption({ value, text }) {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return <option value={value}>{translate(text)}</option>;
|
return <option value={value}>{translate(text)}</option>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Progress = {
|
||||||
|
done: number;
|
||||||
|
limit: number;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
interface ImportLine {
|
||||||
|
id: string;
|
||||||
|
displayname: string;
|
||||||
|
user_type?: string;
|
||||||
|
name?: string;
|
||||||
|
deactivated?: boolean;
|
||||||
|
guest?: boolean;
|
||||||
|
admin?: boolean;
|
||||||
|
is_admin?: boolean;
|
||||||
|
password?: string;
|
||||||
|
avatar_url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChangeStats {
|
||||||
|
total: number;
|
||||||
|
id: number;
|
||||||
|
is_guest: number;
|
||||||
|
admin: number;
|
||||||
|
password: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportResult {
|
||||||
|
skippedRecords: RaRecord[];
|
||||||
|
erroredRecords: RaRecord[];
|
||||||
|
succeededRecords: RaRecord[];
|
||||||
|
totalRecordCount: number;
|
||||||
|
changeStats: ChangeStats;
|
||||||
|
wasDryRun: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const FilePicker = () => {
|
const FilePicker = () => {
|
||||||
const [values, setValues] = useState(null);
|
const [values, setValues] = useState<ImportLine[]>([]);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState<string | string[] | null>(null);
|
||||||
const [stats, setStats] = useState(null);
|
const [stats, setStats] = useState<ChangeStats | null>(null);
|
||||||
const [dryRun, setDryRun] = useState(true);
|
const [dryRun, setDryRun] = useState(true);
|
||||||
|
|
||||||
const [progress, setProgress] = useState(null);
|
const [progress, setProgress] = useState<Progress>(null);
|
||||||
|
|
||||||
const [importResults, setImportResults] = useState(null);
|
const [importResults, setImportResults] = useState<ImportResult | null>(null);
|
||||||
const [skippedRecords, setSkippedRecords] = useState(null);
|
const [skippedRecords, setSkippedRecords] = useState<string>("");
|
||||||
|
|
||||||
const [conflictMode, setConflictMode] = useState("stop");
|
const [conflictMode, setConflictMode] = useState("stop");
|
||||||
const [passwordMode, setPasswordMode] = useState(true);
|
const [passwordMode, setPasswordMode] = useState(true);
|
||||||
|
@ -52,14 +83,15 @@ const FilePicker = () => {
|
||||||
|
|
||||||
const dataProvider = useDataProvider();
|
const dataProvider = useDataProvider();
|
||||||
|
|
||||||
const onFileChange = async e => {
|
const onFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (progress !== null) return;
|
if (progress !== null) return;
|
||||||
|
|
||||||
setValues(null);
|
setValues([]);
|
||||||
setError(null);
|
setError(null);
|
||||||
setStats(null);
|
setStats(null);
|
||||||
setImportResults(null);
|
setImportResults(null);
|
||||||
const file = e.target.files ? e.target.files[0] : null;
|
const file = e.target.files ? e.target.files[0] : null;
|
||||||
|
if (!file) return;
|
||||||
/* Let's refuse some unreasonably big files instead of freezing
|
/* Let's refuse some unreasonably big files instead of freezing
|
||||||
* up the browser */
|
* up the browser */
|
||||||
if (file.size > 100000000) {
|
if (file.size > 100000000) {
|
||||||
|
@ -71,12 +103,12 @@ const FilePicker = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
parseCsv(file, {
|
parseCsv<ImportLine>(file, {
|
||||||
header: true,
|
header: true,
|
||||||
skipEmptyLines: true /* especially for a final EOL in the csv file */,
|
skipEmptyLines: true /* especially for a final EOL in the csv file */,
|
||||||
complete: result => {
|
complete: result => {
|
||||||
if (result.error) {
|
if (result.errors) {
|
||||||
setError(result.error);
|
setError(result.errors.map(e => e.toString()));
|
||||||
}
|
}
|
||||||
/* Papaparse is very lenient, we may be able to salvage
|
/* Papaparse is very lenient, we may be able to salvage
|
||||||
* the data in the file. */
|
* the data in the file. */
|
||||||
|
@ -84,31 +116,25 @@ const FilePicker = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
setError(true);
|
setError("Unknown error");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyCsv = (
|
const verifyCsv = (
|
||||||
{ data, meta, errors },
|
{ data, meta, errors }: ParseResult<ImportLine>,
|
||||||
{ setValues, setStats, setError }
|
{ setValues, setStats, setError }
|
||||||
) => {
|
) => {
|
||||||
/* First, verify the presence of required fields */
|
/* First, verify the presence of required fields */
|
||||||
let eF = Array.from(expectedFields);
|
const missingFields = expectedFields.filter(eF =>
|
||||||
let oF = Array.from(optionalFields);
|
meta.fields?.find(mF => eF === mF)
|
||||||
|
);
|
||||||
|
|
||||||
meta.fields.forEach(name => {
|
if (missingFields.length > 0) {
|
||||||
if (eF.includes(name)) {
|
|
||||||
eF = eF.filter(v => v !== name);
|
|
||||||
}
|
|
||||||
if (oF.includes(name)) {
|
|
||||||
oF = oF.filter(v => v !== name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (eF.length !== 0) {
|
|
||||||
setError(
|
setError(
|
||||||
translate("import_users.error.required_field", { field: eF[0] })
|
translate("import_users.error.required_field", {
|
||||||
|
field: missingFields[0],
|
||||||
|
})
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +145,7 @@ const FilePicker = () => {
|
||||||
/* Collect some stats to prevent sneaky csv files from adding admin
|
/* Collect some stats to prevent sneaky csv files from adding admin
|
||||||
users or something.
|
users or something.
|
||||||
*/
|
*/
|
||||||
let stats = {
|
const stats = {
|
||||||
user_types: { default: 0 },
|
user_types: { default: 0 },
|
||||||
is_guest: 0,
|
is_guest: 0,
|
||||||
admin: 0,
|
admin: 0,
|
||||||
|
@ -131,6 +157,7 @@ const FilePicker = () => {
|
||||||
total: data.length,
|
total: data.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var errorMessages = errors.map(e => e.message);
|
||||||
data.forEach((line, idx) => {
|
data.forEach((line, idx) => {
|
||||||
if (line.user_type === undefined || line.user_type === "") {
|
if (line.user_type === undefined || line.user_type === "") {
|
||||||
stats.user_types.default++;
|
stats.user_types.default++;
|
||||||
|
@ -141,14 +168,13 @@ const FilePicker = () => {
|
||||||
* resource so it gives sensible field names and doesn't duplicate
|
* resource so it gives sensible field names and doesn't duplicate
|
||||||
* id as "name"?
|
* id as "name"?
|
||||||
*/
|
*/
|
||||||
if (meta.fields.includes("name")) {
|
if (meta.fields?.includes("name")) {
|
||||||
delete line.name;
|
delete line.name;
|
||||||
}
|
}
|
||||||
if (meta.fields.includes("user_type")) {
|
if (meta.fields?.includes("user_type")) {
|
||||||
delete line.user_type;
|
delete line.user_type;
|
||||||
}
|
}
|
||||||
if (meta.fields.includes("is_admin")) {
|
if (meta.fields?.includes("is_admin")) {
|
||||||
line.admin = line.is_admin;
|
|
||||||
delete line.is_admin;
|
delete line.is_admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +184,7 @@ const FilePicker = () => {
|
||||||
line[f] = true; // we need true booleans instead of strings
|
line[f] = true; // we need true booleans instead of strings
|
||||||
} else {
|
} else {
|
||||||
if (line[f] !== "false" && line[f] !== "") {
|
if (line[f] !== "false" && line[f] !== "") {
|
||||||
errors.push(
|
errorMessages.push(
|
||||||
translate("import_users.error.invalid_value", {
|
translate("import_users.error.invalid_value", {
|
||||||
field: f,
|
field: f,
|
||||||
row: idx,
|
row: idx,
|
||||||
|
@ -182,8 +208,8 @@ const FilePicker = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errorMessages.length > 0) {
|
||||||
setError(errors);
|
setError(errorMessages);
|
||||||
}
|
}
|
||||||
setStats(stats);
|
setStats(stats);
|
||||||
setValues(data);
|
setValues(data);
|
||||||
|
@ -191,7 +217,7 @@ const FilePicker = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const runImport = async _e => {
|
const runImport = async () => {
|
||||||
if (progress !== null) {
|
if (progress !== null) {
|
||||||
notify("import_users.errors.already_in_progress");
|
notify("import_users.errors.already_in_progress");
|
||||||
return;
|
return;
|
||||||
|
@ -220,26 +246,27 @@ const FilePicker = () => {
|
||||||
// which doesn't look very good.
|
// which doesn't look very good.
|
||||||
|
|
||||||
const doImport = async (
|
const doImport = async (
|
||||||
dataProvider,
|
dataProvider: DataProvider,
|
||||||
data,
|
data: ImportLine[],
|
||||||
conflictMode,
|
conflictMode: string,
|
||||||
passwordMode,
|
passwordMode: boolean,
|
||||||
useridMode,
|
useridMode: string,
|
||||||
dryRun,
|
dryRun: boolean,
|
||||||
setProgress,
|
setProgress: (progress: Progress) => void,
|
||||||
setError
|
setError: (message: string) => void
|
||||||
) => {
|
): Promise<ImportResult> => {
|
||||||
let skippedRecords = [];
|
const skippedRecords: ImportLine[] = [];
|
||||||
let erroredRecords = [];
|
const erroredRecords: ImportLine[] = [];
|
||||||
let succeededRecords = [];
|
const succeededRecords: ImportLine[] = [];
|
||||||
let changeStats = {
|
const changeStats: ChangeStats = {
|
||||||
toAdmin: 0,
|
total: 0,
|
||||||
toGuest: 0,
|
id: 0,
|
||||||
toRegular: 0,
|
is_guest: 0,
|
||||||
replacedPassword: 0,
|
admin: 0,
|
||||||
|
password: 0,
|
||||||
};
|
};
|
||||||
let entriesDone = 0;
|
let entriesDone = 0;
|
||||||
let entriesCount = data.length;
|
const entriesCount = data.length;
|
||||||
try {
|
try {
|
||||||
setProgress({ done: entriesDone, limit: entriesCount });
|
setProgress({ done: entriesDone, limit: entriesCount });
|
||||||
for (const entry of data) {
|
for (const entry of data) {
|
||||||
|
@ -305,9 +332,9 @@ const FilePicker = () => {
|
||||||
"will check for existence of record " + JSON.stringify(userRecord)
|
"will check for existence of record " + JSON.stringify(userRecord)
|
||||||
);
|
);
|
||||||
let retries = 0;
|
let retries = 0;
|
||||||
const submitRecord = recordData => {
|
const submitRecord = (recordData: ImportLine) => {
|
||||||
return dataProvider.getOne("users", { id: recordData.id }).then(
|
return dataProvider.getOne("users", { id: recordData.id }).then(
|
||||||
async _alreadyExists => {
|
async () => {
|
||||||
if (LOGGING) console.log("already existed");
|
if (LOGGING) console.log("already existed");
|
||||||
|
|
||||||
if (useridMode === "update" || conflictMode === "skip") {
|
if (useridMode === "update" || conflictMode === "skip") {
|
||||||
|
@ -332,7 +359,7 @@ const FilePicker = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async _okToSubmit => {
|
async () => {
|
||||||
if (LOGGING)
|
if (LOGGING)
|
||||||
console.log(
|
console.log(
|
||||||
"OK to create record " +
|
"OK to create record " +
|
||||||
|
@ -360,7 +387,7 @@ const FilePicker = () => {
|
||||||
setError(
|
setError(
|
||||||
translate("import_users.error.at_entry", {
|
translate("import_users.error.at_entry", {
|
||||||
entry: entriesDone + 1,
|
entry: entriesDone + 1,
|
||||||
message: e.message,
|
message: e instanceof Error ? e.message : String(e),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
setProgress(null);
|
setProgress(null);
|
||||||
|
@ -387,7 +414,7 @@ const FilePicker = () => {
|
||||||
element.click();
|
element.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onConflictModeChanged = async e => {
|
const onConflictModeChanged = async (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
if (progress !== null) {
|
if (progress !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -396,7 +423,7 @@ const FilePicker = () => {
|
||||||
setConflictMode(value);
|
setConflictMode(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPasswordModeChange = e => {
|
const onPasswordModeChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (progress !== null) {
|
if (progress !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +431,7 @@ const FilePicker = () => {
|
||||||
setPasswordMode(e.target.checked);
|
setPasswordMode(e.target.checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUseridModeChanged = async e => {
|
const onUseridModeChanged = async (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
if (progress !== null) {
|
if (progress !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -413,11 +440,11 @@ const FilePicker = () => {
|
||||||
setUseridMode(value);
|
setUseridMode(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDryRunModeChanged = ev => {
|
const onDryRunModeChanged = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (progress !== null) {
|
if (progress !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDryRun(ev.target.checked);
|
setDryRun(e.target.checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
// render individual small components
|
// render individual small components
|
||||||
|
@ -462,7 +489,7 @@ const FilePicker = () => {
|
||||||
<NativeSelect
|
<NativeSelect
|
||||||
onChange={onUseridModeChanged}
|
onChange={onUseridModeChanged}
|
||||||
value={useridMode}
|
value={useridMode}
|
||||||
enabled={(progress !== null).toString()}
|
disabled={progress !== null}
|
||||||
>
|
>
|
||||||
<TranslatableOption
|
<TranslatableOption
|
||||||
value="ignore"
|
value="ignore"
|
||||||
|
@ -496,7 +523,7 @@ const FilePicker = () => {
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={passwordMode}
|
checked={passwordMode}
|
||||||
enabled={(progress !== null).toString()}
|
disabled={progress !== null}
|
||||||
onChange={onPasswordModeChange}
|
onChange={onPasswordModeChange}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -510,7 +537,7 @@ const FilePicker = () => {
|
||||||
</Container>,
|
</Container>,
|
||||||
];
|
];
|
||||||
|
|
||||||
let conflictCards = stats && !importResults && (
|
const conflictCards = stats && !importResults && (
|
||||||
<Container>
|
<Container>
|
||||||
<CardHeader title={translate("import_users.cards.conflicts.header")} />
|
<CardHeader title={translate("import_users.cards.conflicts.header")} />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
@ -518,7 +545,7 @@ const FilePicker = () => {
|
||||||
<NativeSelect
|
<NativeSelect
|
||||||
onChange={onConflictModeChanged}
|
onChange={onConflictModeChanged}
|
||||||
value={conflictMode}
|
value={conflictMode}
|
||||||
enabled={(progress !== null).toString()}
|
disabled={progress !== null}
|
||||||
>
|
>
|
||||||
<TranslatableOption
|
<TranslatableOption
|
||||||
value="stop"
|
value="stop"
|
||||||
|
@ -534,7 +561,7 @@ const FilePicker = () => {
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
||||||
let errorCards = error && (
|
const errorCards = error && (
|
||||||
<Container>
|
<Container>
|
||||||
<CardHeader title={translate("import_users.error.error")} />
|
<CardHeader title={translate("import_users.error.error")} />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
@ -545,7 +572,7 @@ const FilePicker = () => {
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
||||||
let uploadCard = !importResults && (
|
const uploadCard = !importResults && (
|
||||||
<Container>
|
<Container>
|
||||||
<CardHeader title={translate("import_users.cards.upload.header")} />
|
<CardHeader title={translate("import_users.cards.upload.header")} />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
@ -556,13 +583,13 @@ const FilePicker = () => {
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
onChange={onFileChange}
|
onChange={onFileChange}
|
||||||
enabled={(progress !== null).toString()}
|
disabled={progress !== null}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
||||||
let resultsCard = importResults && (
|
const resultsCard = importResults && (
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<CardHeader title={translate("import_users.cards.results.header")} />
|
<CardHeader title={translate("import_users.cards.results.header")} />
|
||||||
<div>
|
<div>
|
||||||
|
@ -608,7 +635,7 @@ const FilePicker = () => {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
);
|
);
|
||||||
|
|
||||||
let startImportCard =
|
const startImportCard =
|
||||||
!values || values.length === 0 || importResults ? undefined : (
|
!values || values.length === 0 || importResults ? undefined : (
|
||||||
<CardActions>
|
<CardActions>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
@ -616,16 +643,12 @@ const FilePicker = () => {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={dryRun}
|
checked={dryRun}
|
||||||
onChange={onDryRunModeChanged}
|
onChange={onDryRunModeChanged}
|
||||||
enabled={(progress !== null).toString()}
|
disabled={progress !== null}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={translate("import_users.cards.startImport.simulate_only")}
|
label={translate("import_users.cards.startImport.simulate_only")}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button size="large" onClick={runImport} disabled={progress !== null}>
|
||||||
size="large"
|
|
||||||
onClick={runImport}
|
|
||||||
enabled={(progress !== null).toString()}
|
|
||||||
>
|
|
||||||
{translate("import_users.cards.startImport.run_import")}
|
{translate("import_users.cards.startImport.run_import")}
|
||||||
</Button>
|
</Button>
|
||||||
{progress !== null ? (
|
{progress !== null ? (
|
||||||
|
@ -636,7 +659,7 @@ const FilePicker = () => {
|
||||||
</CardActions>
|
</CardActions>
|
||||||
);
|
);
|
||||||
|
|
||||||
let allCards = [];
|
const allCards: JSX.Element[] = [];
|
||||||
if (uploadCard) allCards.push(uploadCard);
|
if (uploadCard) allCards.push(uploadCard);
|
||||||
if (errorCards) allCards.push(errorCards);
|
if (errorCards) allCards.push(errorCards);
|
||||||
if (conflictCards) allCards.push(conflictCards);
|
if (conflictCards) allCards.push(conflictCards);
|
||||||
|
@ -644,7 +667,7 @@ const FilePicker = () => {
|
||||||
if (startImportCard) allCards.push(startImportCard);
|
if (startImportCard) allCards.push(startImportCard);
|
||||||
if (resultsCard) allCards.push(resultsCard);
|
if (resultsCard) allCards.push(resultsCard);
|
||||||
|
|
||||||
let cardContainer = <Card>{allCards}</Card>;
|
const cardContainer = <Card>{allCards}</Card>;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<Title defaultTitle={translate("import_users.title")} />,
|
<Title defaultTitle={translate("import_users.title")} />,
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from "@testing-library/react";
|
||||||
import { AdminContext } from "react-admin";
|
import { AdminContext } from "react-admin";
|
||||||
import polyglotI18nProvider from "ra-i18n-polyglot";
|
import polyglotI18nProvider from "ra-i18n-polyglot";
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormDataConsumer,
|
FormDataConsumer,
|
||||||
|
@ -183,7 +183,7 @@ const LoginPage = () => {
|
||||||
const [serverVersion, setServerVersion] = useState("");
|
const [serverVersion, setServerVersion] = useState("");
|
||||||
const [matrixVersions, setMatrixVersions] = useState("");
|
const [matrixVersions, setMatrixVersions] = useState("");
|
||||||
|
|
||||||
const handleUsernameChange = _ => {
|
const handleUsernameChange = () => {
|
||||||
if (formData.base_url || allowSingleBaseUrl) return;
|
if (formData.base_url || allowSingleBaseUrl) return;
|
||||||
// check if username is a full qualified userId then set base_url accordingly
|
// check if username is a full qualified userId then set base_url accordingly
|
||||||
const domain = splitMxid(formData.username)?.domain;
|
const domain = splitMxid(formData.username)?.domain;
|
||||||
|
@ -238,7 +238,7 @@ const LoginPage = () => {
|
||||||
<Box>
|
<Box>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoFocus
|
autoFocus
|
||||||
name="username"
|
source="username"
|
||||||
label="ra.auth.username"
|
label="ra.auth.username"
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
disabled={loading || !supportPassAuth}
|
disabled={loading || !supportPassAuth}
|
||||||
|
@ -250,7 +250,7 @@ const LoginPage = () => {
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
name="password"
|
source="password"
|
||||||
label="ra.auth.password"
|
label="ra.auth.password"
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
|
@ -262,7 +262,7 @@ const LoginPage = () => {
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<TextInput
|
<TextInput
|
||||||
name="base_url"
|
source="base_url"
|
||||||
label="synapseadmin.auth.base_url"
|
label="synapseadmin.auth.base_url"
|
||||||
select={allowMultipleBaseUrls}
|
select={allowMultipleBaseUrls}
|
||||||
autoComplete="url"
|
autoComplete="url"
|
|
@ -1,17 +1,20 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
BooleanInput,
|
BooleanInput,
|
||||||
Create,
|
Create,
|
||||||
|
CreateProps,
|
||||||
Datagrid,
|
Datagrid,
|
||||||
DateField,
|
DateField,
|
||||||
DateTimeInput,
|
DateTimeInput,
|
||||||
Edit,
|
Edit,
|
||||||
|
EditProps,
|
||||||
List,
|
List,
|
||||||
|
ListProps,
|
||||||
maxValue,
|
maxValue,
|
||||||
number,
|
number,
|
||||||
NumberField,
|
NumberField,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
regex,
|
regex,
|
||||||
|
ResourceProps,
|
||||||
SaveButton,
|
SaveButton,
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
TextInput,
|
TextInput,
|
||||||
|
@ -56,7 +59,7 @@ const dateFormatter = v => {
|
||||||
|
|
||||||
const registrationTokenFilters = [<BooleanInput source="valid" alwaysOn />];
|
const registrationTokenFilters = [<BooleanInput source="valid" alwaysOn />];
|
||||||
|
|
||||||
export const RegistrationTokenList = props => (
|
export const RegistrationTokenList = (props: ListProps) => (
|
||||||
<List
|
<List
|
||||||
{...props}
|
{...props}
|
||||||
filters={registrationTokenFilters}
|
filters={registrationTokenFilters}
|
||||||
|
@ -79,7 +82,7 @@ export const RegistrationTokenList = props => (
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RegistrationTokenCreate = props => (
|
export const RegistrationTokenCreate = (props: CreateProps) => (
|
||||||
<Create {...props} redirect="list">
|
<Create {...props} redirect="list">
|
||||||
<SimpleForm
|
<SimpleForm
|
||||||
toolbar={
|
toolbar={
|
||||||
|
@ -111,7 +114,7 @@ export const RegistrationTokenCreate = props => (
|
||||||
</Create>
|
</Create>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RegistrationTokenEdit = props => (
|
export const RegistrationTokenEdit = (props: EditProps) => (
|
||||||
<Edit {...props}>
|
<Edit {...props}>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="token" disabled />
|
<TextInput source="token" disabled />
|
||||||
|
@ -131,7 +134,7 @@ export const RegistrationTokenEdit = props => (
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "registration_tokens",
|
name: "registration_tokens",
|
||||||
icon: RegistrationTokenIcon,
|
icon: RegistrationTokenIcon,
|
||||||
list: RegistrationTokenList,
|
list: RegistrationTokenList,
|
|
@ -1,14 +1,17 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
BooleanField,
|
BooleanField,
|
||||||
BulkDeleteButton,
|
BulkDeleteButton,
|
||||||
|
BulkDeleteButtonProps,
|
||||||
Button,
|
Button,
|
||||||
|
ButtonProps,
|
||||||
DatagridConfigurable,
|
DatagridConfigurable,
|
||||||
|
DeleteButtonProps,
|
||||||
ExportButton,
|
ExportButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
List,
|
List,
|
||||||
NumberField,
|
NumberField,
|
||||||
Pagination,
|
Pagination,
|
||||||
|
ResourceProps,
|
||||||
SelectColumnsButton,
|
SelectColumnsButton,
|
||||||
TextField,
|
TextField,
|
||||||
TopToolbar,
|
TopToolbar,
|
||||||
|
@ -29,7 +32,7 @@ const RoomDirectoryPagination = () => (
|
||||||
<Pagination rowsPerPageOptions={[100, 500, 1000, 2000]} />
|
<Pagination rowsPerPageOptions={[100, 500, 1000, 2000]} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RoomDirectoryUnpublishButton = props => {
|
export const RoomDirectoryUnpublishButton = (props: DeleteButtonProps) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -50,7 +53,9 @@ export const RoomDirectoryUnpublishButton = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RoomDirectoryBulkUnpublishButton = props => (
|
export const RoomDirectoryBulkUnpublishButton = (
|
||||||
|
props: BulkDeleteButtonProps
|
||||||
|
) => (
|
||||||
<BulkDeleteButton
|
<BulkDeleteButton
|
||||||
{...props}
|
{...props}
|
||||||
label="resources.room_directory.action.erase"
|
label="resources.room_directory.action.erase"
|
||||||
|
@ -62,7 +67,7 @@ export const RoomDirectoryBulkUnpublishButton = props => (
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RoomDirectoryBulkPublishButton = props => {
|
export const RoomDirectoryBulkPublishButton = (props: ButtonProps) => {
|
||||||
const { selectedIds } = useListContext();
|
const { selectedIds } = useListContext();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
|
@ -99,7 +104,7 @@ export const RoomDirectoryBulkPublishButton = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RoomDirectoryPublishButton = props => {
|
export const RoomDirectoryPublishButton = (props: ButtonProps) => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
|
@ -148,7 +153,7 @@ export const RoomDirectoryList = () => (
|
||||||
actions={<RoomDirectoryListActions />}
|
actions={<RoomDirectoryListActions />}
|
||||||
>
|
>
|
||||||
<DatagridConfigurable
|
<DatagridConfigurable
|
||||||
rowClick={(id, _resource, _record) => "/rooms/" + id + "/show"}
|
rowClick={id => "/rooms/" + id + "/show"}
|
||||||
bulkActionButtons={<RoomDirectoryBulkUnpublishButton />}
|
bulkActionButtons={<RoomDirectoryBulkUnpublishButton />}
|
||||||
omit={["room_id", "canonical_alias", "topic"]}
|
omit={["room_id", "canonical_alias", "topic"]}
|
||||||
>
|
>
|
||||||
|
@ -197,7 +202,7 @@ export const RoomDirectoryList = () => (
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "room_directory",
|
name: "room_directory",
|
||||||
icon: RoomDirectoryIcon,
|
icon: RoomDirectoryIcon,
|
||||||
list: RoomDirectoryList,
|
list: RoomDirectoryList,
|
|
@ -1,10 +1,12 @@
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
RaRecord,
|
||||||
SaveButton,
|
SaveButton,
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
TextInput,
|
TextInput,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
|
ToolbarProps,
|
||||||
required,
|
required,
|
||||||
useCreate,
|
useCreate,
|
||||||
useDataProvider,
|
useDataProvider,
|
||||||
|
@ -24,10 +26,12 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
const ServerNoticeDialog = ({ open, loading, onClose, onSubmit }) => {
|
const ServerNoticeDialog = ({ open, onClose, onSubmit }) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
|
|
||||||
const ServerNoticeToolbar = props => (
|
const ServerNoticeToolbar = (
|
||||||
|
props: ToolbarProps & { pristine?: boolean }
|
||||||
|
) => (
|
||||||
<Toolbar {...props}>
|
<Toolbar {...props}>
|
||||||
<SaveButton
|
<SaveButton
|
||||||
label="resources.servernotices.action.send"
|
label="resources.servernotices.action.send"
|
||||||
|
@ -40,7 +44,7 @@ const ServerNoticeDialog = ({ open, loading, onClose, onSubmit }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onClose} loading={loading}>
|
<Dialog open={open} onClose={onClose}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{translate("resources.servernotices.action.send")}
|
{translate("resources.servernotices.action.send")}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
@ -68,12 +72,12 @@ export const ServerNoticeButton = () => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const [create, { isloading }] = useCreate();
|
const [create, { isLoading }] = useCreate();
|
||||||
|
|
||||||
const handleDialogOpen = () => setOpen(true);
|
const handleDialogOpen = () => setOpen(true);
|
||||||
const handleDialogClose = () => setOpen(false);
|
const handleDialogClose = () => setOpen(false);
|
||||||
|
|
||||||
const handleSend = values => {
|
const handleSend = (values: Partial<RaRecord>) => {
|
||||||
create(
|
create(
|
||||||
"servernotices",
|
"servernotices",
|
||||||
{ data: { id: record.id, ...values } },
|
{ data: { id: record.id, ...values } },
|
||||||
|
@ -95,7 +99,7 @@ export const ServerNoticeButton = () => {
|
||||||
<Button
|
<Button
|
||||||
label="resources.servernotices.send"
|
label="resources.servernotices.send"
|
||||||
onClick={handleDialogOpen}
|
onClick={handleDialogOpen}
|
||||||
disabled={isloading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
<MessageIcon />
|
<MessageIcon />
|
||||||
</Button>
|
</Button>
|
30
src/components/date.ts
Normal file
30
src/components/date.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
export const DATE_FORMAT: Intl.DateTimeFormatOptions = {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dateParser = (v: string | number | Date): number => {
|
||||||
|
const d = new Date(v);
|
||||||
|
return d.getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dateFormatter = (
|
||||||
|
v: string | number | Date | undefined | null
|
||||||
|
): string => {
|
||||||
|
if (v === undefined || v === null) return "";
|
||||||
|
const d = new Date(v);
|
||||||
|
|
||||||
|
const pad = "00";
|
||||||
|
const year = d.getFullYear().toString();
|
||||||
|
const month = (pad + (d.getMonth() + 1).toString()).slice(-2);
|
||||||
|
const day = (pad + d.getDate().toString()).slice(-2);
|
||||||
|
const hour = (pad + d.getHours().toString()).slice(-2);
|
||||||
|
const minute = (pad + d.getMinutes().toString()).slice(-2);
|
||||||
|
|
||||||
|
// target format yyyy-MM-ddThh:mm
|
||||||
|
return `${year}-${month}-${day}T${hour}:${minute}`;
|
||||||
|
};
|
|
@ -1,14 +1,23 @@
|
||||||
import React from "react";
|
import { MouseEvent } from "react";
|
||||||
|
|
||||||
|
import AutorenewIcon from "@mui/icons-material/Autorenew";
|
||||||
|
import DestinationsIcon from "@mui/icons-material/CloudQueue";
|
||||||
|
import FolderSharedIcon from "@mui/icons-material/FolderShared";
|
||||||
|
import ViewListIcon from "@mui/icons-material/ViewList";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Datagrid,
|
Datagrid,
|
||||||
DateField,
|
DateField,
|
||||||
List,
|
List,
|
||||||
|
ListProps,
|
||||||
Pagination,
|
Pagination,
|
||||||
|
RaRecord,
|
||||||
ReferenceField,
|
ReferenceField,
|
||||||
ReferenceManyField,
|
ReferenceManyField,
|
||||||
|
ResourceProps,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
Show,
|
Show,
|
||||||
|
ShowProps,
|
||||||
Tab,
|
Tab,
|
||||||
TabbedShowLayout,
|
TabbedShowLayout,
|
||||||
TextField,
|
TextField,
|
||||||
|
@ -19,25 +28,14 @@ import {
|
||||||
useRefresh,
|
useRefresh,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import AutorenewIcon from "@mui/icons-material/Autorenew";
|
|
||||||
import DestinationsIcon from "@mui/icons-material/CloudQueue";
|
import { DATE_FORMAT } from "./date";
|
||||||
import FolderSharedIcon from "@mui/icons-material/FolderShared";
|
|
||||||
import ViewListIcon from "@mui/icons-material/ViewList";
|
|
||||||
|
|
||||||
const DestinationPagination = () => (
|
const DestinationPagination = () => (
|
||||||
<Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
<Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const date_format = {
|
const destinationRowSx = (record: RaRecord) => ({
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
};
|
|
||||||
|
|
||||||
const destinationRowSx = (record, _index) => ({
|
|
||||||
backgroundColor: record.retry_last_ts > 0 ? "#ffcccc" : "white",
|
backgroundColor: record.retry_last_ts > 0 ? "#ffcccc" : "white",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,7 +50,7 @@ export const DestinationReconnectButton = () => {
|
||||||
// Reconnect is not required if no error has occurred. (`failure_ts`)
|
// Reconnect is not required if no error has occurred. (`failure_ts`)
|
||||||
if (!record || !record.failure_ts) return null;
|
if (!record || !record.failure_ts) return null;
|
||||||
|
|
||||||
const handleClick = e => {
|
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
// Prevents redirection to the detail page when clicking in the list
|
// Prevents redirection to the detail page when clicking in the list
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -100,7 +98,7 @@ const DestinationTitle = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DestinationList = props => {
|
export const DestinationList = (props: ListProps) => {
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -110,12 +108,12 @@ export const DestinationList = props => {
|
||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
rowSx={destinationRowSx}
|
rowSx={destinationRowSx}
|
||||||
rowClick={(id, _resource, _record) => `${id}/show/rooms`}
|
rowClick={id => `${id}/show/rooms`}
|
||||||
bulkActionButtons={false}
|
bulkActionButtons={false}
|
||||||
>
|
>
|
||||||
<TextField source="destination" />
|
<TextField source="destination" />
|
||||||
<DateField source="failure_ts" showTime options={date_format} />
|
<DateField source="failure_ts" showTime options={DATE_FORMAT} />
|
||||||
<DateField source="retry_last_ts" showTime options={date_format} />
|
<DateField source="retry_last_ts" showTime options={DATE_FORMAT} />
|
||||||
<TextField source="retry_interval" />
|
<TextField source="retry_interval" />
|
||||||
<TextField source="last_successful_stream_ordering" />
|
<TextField source="last_successful_stream_ordering" />
|
||||||
<DestinationReconnectButton />
|
<DestinationReconnectButton />
|
||||||
|
@ -124,7 +122,7 @@ export const DestinationList = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DestinationShow = props => {
|
export const DestinationShow = (props: ShowProps) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
|
@ -135,8 +133,8 @@ export const DestinationShow = props => {
|
||||||
<TabbedShowLayout>
|
<TabbedShowLayout>
|
||||||
<Tab label="status" icon={<ViewListIcon />}>
|
<Tab label="status" icon={<ViewListIcon />}>
|
||||||
<TextField source="destination" />
|
<TextField source="destination" />
|
||||||
<DateField source="failure_ts" showTime options={date_format} />
|
<DateField source="failure_ts" showTime options={DATE_FORMAT} />
|
||||||
<DateField source="retry_last_ts" showTime options={date_format} />
|
<DateField source="retry_last_ts" showTime options={DATE_FORMAT} />
|
||||||
<TextField source="retry_interval" />
|
<TextField source="retry_interval" />
|
||||||
<TextField source="last_successful_stream_ordering" />
|
<TextField source="last_successful_stream_ordering" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
@ -149,13 +147,13 @@ export const DestinationShow = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="destination_rooms"
|
reference="destination_rooms"
|
||||||
target="destination"
|
target="destination"
|
||||||
addLabel={false}
|
label={false}
|
||||||
pagination={<DestinationPagination />}
|
pagination={<DestinationPagination />}
|
||||||
perPage={50}
|
perPage={50}
|
||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
rowClick={(id, resource, record) => `/rooms/${id}/show`}
|
rowClick={id => `/rooms/${id}/show`}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
source="room_id"
|
source="room_id"
|
||||||
|
@ -179,7 +177,7 @@ export const DestinationShow = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "destinations",
|
name: "destinations",
|
||||||
icon: DestinationsIcon,
|
icon: DestinationsIcon,
|
||||||
list: DestinationList,
|
list: DestinationList,
|
|
@ -1,51 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
DeleteButton,
|
|
||||||
useDelete,
|
|
||||||
useNotify,
|
|
||||||
useRecordContext,
|
|
||||||
useRefresh,
|
|
||||||
} from "react-admin";
|
|
||||||
|
|
||||||
export const DeviceRemoveButton = props => {
|
|
||||||
const record = useRecordContext();
|
|
||||||
const refresh = useRefresh();
|
|
||||||
const notify = useNotify();
|
|
||||||
|
|
||||||
const [removeDevice] = useDelete();
|
|
||||||
|
|
||||||
if (!record) return null;
|
|
||||||
|
|
||||||
const handleConfirm = () => {
|
|
||||||
removeDevice(
|
|
||||||
"devices",
|
|
||||||
// needs previousData for user_id
|
|
||||||
{ id: record.id, previousData: record },
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
notify("resources.devices.action.erase.success");
|
|
||||||
refresh();
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
notify("resources.devices.action.erase.failure", { type: "error" });
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DeleteButton
|
|
||||||
{...props}
|
|
||||||
label="ra.action.remove"
|
|
||||||
confirmTitle="resources.devices.action.erase.title"
|
|
||||||
confirmContent="resources.devices.action.erase.content"
|
|
||||||
onConfirm={handleConfirm}
|
|
||||||
mutationMode="pessimistic"
|
|
||||||
redirect={false}
|
|
||||||
translateOptions={{
|
|
||||||
id: record.id,
|
|
||||||
name: record.display_name ? record.display_name : record.id,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
25
src/components/devices.tsx
Normal file
25
src/components/devices.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import {
|
||||||
|
DeleteWithConfirmButton,
|
||||||
|
DeleteWithConfirmButtonProps,
|
||||||
|
useRecordContext,
|
||||||
|
} from "react-admin";
|
||||||
|
|
||||||
|
export const DeviceRemoveButton = (props: DeleteWithConfirmButtonProps) => {
|
||||||
|
const record = useRecordContext();
|
||||||
|
if (!record) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DeleteWithConfirmButton
|
||||||
|
{...props}
|
||||||
|
label="ra.action.remove"
|
||||||
|
confirmTitle="resources.devices.action.erase.title"
|
||||||
|
confirmContent="resources.devices.action.erase.content"
|
||||||
|
mutationMode="pessimistic"
|
||||||
|
redirect={false}
|
||||||
|
translateOptions={{
|
||||||
|
id: record.id,
|
||||||
|
name: record.display_name ? record.display_name : record.id,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,13 +1,15 @@
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import get from "lodash/get";
|
import { get } from "lodash";
|
||||||
import {
|
import {
|
||||||
BooleanInput,
|
BooleanInput,
|
||||||
Button,
|
Button,
|
||||||
|
ButtonProps,
|
||||||
DateTimeInput,
|
DateTimeInput,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
SaveButton,
|
SaveButton,
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
|
ToolbarProps,
|
||||||
useCreate,
|
useCreate,
|
||||||
useDelete,
|
useDelete,
|
||||||
useNotify,
|
useNotify,
|
||||||
|
@ -34,7 +36,7 @@ import FileOpenIcon from "@mui/icons-material/FileOpen";
|
||||||
import { alpha, useTheme } from "@mui/material/styles";
|
import { alpha, useTheme } from "@mui/material/styles";
|
||||||
import { getMediaUrl } from "../synapse/synapse";
|
import { getMediaUrl } from "../synapse/synapse";
|
||||||
|
|
||||||
const DeleteMediaDialog = ({ open, loading, onClose, onSubmit }) => {
|
const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
|
|
||||||
const dateParser = v => {
|
const dateParser = v => {
|
||||||
|
@ -43,7 +45,7 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSubmit }) => {
|
||||||
return d.getTime();
|
return d.getTime();
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeleteMediaToolbar = props => (
|
const DeleteMediaToolbar = (props: ToolbarProps) => (
|
||||||
<Toolbar {...props}>
|
<Toolbar {...props}>
|
||||||
<SaveButton
|
<SaveButton
|
||||||
label="resources.delete_media.action.send"
|
label="resources.delete_media.action.send"
|
||||||
|
@ -56,7 +58,7 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSubmit }) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onClose} loading={loading}>
|
<Dialog open={open} onClose={onClose}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{translate("resources.delete_media.action.send")}
|
{translate("resources.delete_media.action.send")}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
@ -92,7 +94,7 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSubmit }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteMediaButton = props => {
|
export const DeleteMediaButton = (props: ButtonProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
|
@ -101,7 +103,11 @@ export const DeleteMediaButton = props => {
|
||||||
const openDialog = () => setOpen(true);
|
const openDialog = () => setOpen(true);
|
||||||
const closeDialog = () => setOpen(false);
|
const closeDialog = () => setOpen(false);
|
||||||
|
|
||||||
const deleteMedia = values => {
|
const deleteMedia = (values: {
|
||||||
|
before_ts: string;
|
||||||
|
size_gt: number;
|
||||||
|
keep_profiles: boolean;
|
||||||
|
}) => {
|
||||||
deleteOne(
|
deleteOne(
|
||||||
"delete_media",
|
"delete_media",
|
||||||
// needs meta.before_ts, meta.size_gt and meta.keep_profiles
|
// needs meta.before_ts, meta.size_gt and meta.keep_profiles
|
||||||
|
@ -148,7 +154,7 @@ export const DeleteMediaButton = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProtectMediaButton = () => {
|
export const ProtectMediaButton = (props: ButtonProps) => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
|
@ -209,7 +215,7 @@ export const ProtectMediaButton = () => {
|
||||||
Button instead BooleanField for
|
Button instead BooleanField for
|
||||||
consistent appearance and position in the column
|
consistent appearance and position in the column
|
||||||
*/}
|
*/}
|
||||||
<Button disabled={true}>
|
<Button {...props} disabled={true}>
|
||||||
<ClearIcon />
|
<ClearIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -223,7 +229,7 @@ export const ProtectMediaButton = () => {
|
||||||
arrow
|
arrow
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleUnprotect} disabled={isLoading}>
|
<Button {...props} onClick={handleUnprotect} disabled={isLoading}>
|
||||||
<LockIcon />
|
<LockIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -236,7 +242,7 @@ export const ProtectMediaButton = () => {
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleProtect} disabled={isLoading}>
|
<Button {...props} onClick={handleProtect} disabled={isLoading}>
|
||||||
<LockOpenIcon />
|
<LockOpenIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -246,7 +252,7 @@ export const ProtectMediaButton = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QuarantineMediaButton = props => {
|
export const QuarantineMediaButton = (props: ButtonProps) => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
|
@ -329,7 +335,7 @@ export const QuarantineMediaButton = props => {
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleQuarantaine} disabled={isLoading}>
|
<Button {...props} onClick={handleQuarantaine} disabled={isLoading}>
|
||||||
<BlockIcon />
|
<BlockIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
BooleanField,
|
BooleanField,
|
||||||
BulkDeleteButton,
|
BulkDeleteButton,
|
||||||
|
@ -9,14 +8,17 @@ import {
|
||||||
ExportButton,
|
ExportButton,
|
||||||
FunctionField,
|
FunctionField,
|
||||||
List,
|
List,
|
||||||
|
ListProps,
|
||||||
NumberField,
|
NumberField,
|
||||||
Pagination,
|
Pagination,
|
||||||
ReferenceField,
|
ReferenceField,
|
||||||
ReferenceManyField,
|
ReferenceManyField,
|
||||||
|
ResourceProps,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
SelectColumnsButton,
|
SelectColumnsButton,
|
||||||
SelectField,
|
SelectField,
|
||||||
Show,
|
Show,
|
||||||
|
ShowProps,
|
||||||
Tab,
|
Tab,
|
||||||
TabbedShowLayout,
|
TabbedShowLayout,
|
||||||
TextField,
|
TextField,
|
||||||
|
@ -58,7 +60,7 @@ const RoomPagination = () => (
|
||||||
const RoomTitle = () => {
|
const RoomTitle = () => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
var name = "";
|
let name = "";
|
||||||
if (record) {
|
if (record) {
|
||||||
name = record.name !== "" ? record.name : record.id;
|
name = record.name !== "" ? record.name : record.id;
|
||||||
}
|
}
|
||||||
|
@ -72,15 +74,15 @@ const RoomTitle = () => {
|
||||||
|
|
||||||
const RoomShowActions = () => {
|
const RoomShowActions = () => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
var roomDirectoryStatus = "";
|
const publishButton = record.public ? (
|
||||||
if (record) {
|
<RoomDirectoryUnpublishButton />
|
||||||
roomDirectoryStatus = record.public;
|
) : (
|
||||||
}
|
<RoomDirectoryPublishButton />
|
||||||
|
);
|
||||||
|
// FIXME: refresh after (un)publish
|
||||||
return (
|
return (
|
||||||
<TopToolbar>
|
<TopToolbar>
|
||||||
{roomDirectoryStatus === false && <RoomDirectoryPublishButton />}
|
{publishButton}
|
||||||
{roomDirectoryStatus === true && <RoomDirectoryUnpublishButton />}
|
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
mutationMode="pessimistic"
|
mutationMode="pessimistic"
|
||||||
confirmTitle="resources.rooms.action.erase.title"
|
confirmTitle="resources.rooms.action.erase.title"
|
||||||
|
@ -90,7 +92,7 @@ const RoomShowActions = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RoomShow = props => {
|
export const RoomShow = (props: ShowProps) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
|
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
|
||||||
|
@ -129,11 +131,11 @@ export const RoomShow = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="room_members"
|
reference="room_members"
|
||||||
target="room_id"
|
target="room_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
rowClick={(id, resource, record) => "/users/" + id}
|
rowClick={id => "/users/" + id}
|
||||||
bulkActionButtons={false}
|
bulkActionButtons={false}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -217,7 +219,7 @@ export const RoomShow = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="room_state"
|
reference="room_state"
|
||||||
target="room_id"
|
target="room_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
>
|
>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<TextField source="type" sortable={false} />
|
<TextField source="type" sortable={false} />
|
||||||
|
@ -255,7 +257,7 @@ export const RoomShow = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="forward_extremities"
|
reference="forward_extremities"
|
||||||
target="room_id"
|
target="room_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
>
|
>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<TextField source="id" sortable={false} />
|
<TextField source="id" sortable={false} />
|
||||||
|
@ -296,7 +298,7 @@ const RoomListActions = () => (
|
||||||
</TopToolbar>
|
</TopToolbar>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RoomList = props => {
|
export const RoomList = (props: ListProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -345,7 +347,7 @@ export const RoomList = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "rooms",
|
name: "rooms",
|
||||||
icon: RoomIcon,
|
icon: RoomIcon,
|
||||||
list: RoomList,
|
list: RoomList,
|
|
@ -1,42 +1,26 @@
|
||||||
import React from "react";
|
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
||||||
import { cloneElement } from "react";
|
|
||||||
import {
|
import {
|
||||||
Datagrid,
|
Datagrid,
|
||||||
ExportButton,
|
ExportButton,
|
||||||
List,
|
List,
|
||||||
|
ListProps,
|
||||||
NumberField,
|
NumberField,
|
||||||
Pagination,
|
Pagination,
|
||||||
sanitizeListRestProps,
|
ResourceProps,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
TextField,
|
TextField,
|
||||||
TopToolbar,
|
TopToolbar,
|
||||||
useListContext,
|
useListContext,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
|
||||||
import { DeleteMediaButton } from "./media";
|
import { DeleteMediaButton } from "./media";
|
||||||
|
|
||||||
const ListActions = props => {
|
const ListActions = () => {
|
||||||
const { className, exporter, filters, maxResults, ...rest } = props;
|
const { isLoading, total } = useListContext();
|
||||||
const { sort, resource, displayedFilters, filterValues, showFilter, total } =
|
|
||||||
useListContext();
|
|
||||||
return (
|
return (
|
||||||
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
|
<TopToolbar>
|
||||||
{filters &&
|
|
||||||
cloneElement(filters, {
|
|
||||||
resource,
|
|
||||||
showFilter,
|
|
||||||
displayedFilters,
|
|
||||||
filterValues,
|
|
||||||
context: "button",
|
|
||||||
})}
|
|
||||||
<DeleteMediaButton />
|
<DeleteMediaButton />
|
||||||
<ExportButton
|
<ExportButton disabled={isLoading || total === 0} />
|
||||||
disabled={total === 0}
|
|
||||||
resource={resource}
|
|
||||||
sort={sort}
|
|
||||||
filterValues={filterValues}
|
|
||||||
maxResults={maxResults}
|
|
||||||
/>
|
|
||||||
</TopToolbar>
|
</TopToolbar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -47,7 +31,7 @@ const UserMediaStatsPagination = () => (
|
||||||
|
|
||||||
const userMediaStatsFilters = [<SearchInput source="search_term" alwaysOn />];
|
const userMediaStatsFilters = [<SearchInput source="search_term" alwaysOn />];
|
||||||
|
|
||||||
export const UserMediaStatsList = props => (
|
export const UserMediaStatsList = (props: ListProps) => (
|
||||||
<List
|
<List
|
||||||
{...props}
|
{...props}
|
||||||
actions={<ListActions />}
|
actions={<ListActions />}
|
||||||
|
@ -56,7 +40,7 @@ export const UserMediaStatsList = props => (
|
||||||
sort={{ field: "media_length", order: "DESC" }}
|
sort={{ field: "media_length", order: "DESC" }}
|
||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
rowClick={(id, resource, record) => "/users/" + id + "/media"}
|
rowClick={id => "/users/" + id + "/media"}
|
||||||
bulkActionButtons={false}
|
bulkActionButtons={false}
|
||||||
>
|
>
|
||||||
<TextField source="user_id" label="resources.users.fields.id" />
|
<TextField source="user_id" label="resources.users.fields.id" />
|
||||||
|
@ -70,7 +54,7 @@ export const UserMediaStatsList = props => (
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "user_media_statistics",
|
name: "user_media_statistics",
|
||||||
icon: EqualizerIcon,
|
icon: EqualizerIcon,
|
||||||
list: UserMediaStatsList,
|
list: UserMediaStatsList,
|
|
@ -1,4 +1,3 @@
|
||||||
import React, { cloneElement } from "react";
|
|
||||||
import AssignmentIndIcon from "@mui/icons-material/AssignmentInd";
|
import AssignmentIndIcon from "@mui/icons-material/AssignmentInd";
|
||||||
import ContactMailIcon from "@mui/icons-material/ContactMail";
|
import ContactMailIcon from "@mui/icons-material/ContactMail";
|
||||||
import DevicesIcon from "@mui/icons-material/Devices";
|
import DevicesIcon from "@mui/icons-material/Devices";
|
||||||
|
@ -16,9 +15,11 @@ import {
|
||||||
Datagrid,
|
Datagrid,
|
||||||
DateField,
|
DateField,
|
||||||
Create,
|
Create,
|
||||||
|
CreateProps,
|
||||||
Edit,
|
Edit,
|
||||||
|
EditProps,
|
||||||
List,
|
List,
|
||||||
Toolbar,
|
ListProps,
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
SimpleFormIterator,
|
SimpleFormIterator,
|
||||||
TabbedForm,
|
TabbedForm,
|
||||||
|
@ -30,11 +31,11 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
ReferenceField,
|
ReferenceField,
|
||||||
ReferenceManyField,
|
ReferenceManyField,
|
||||||
|
ResourceProps,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
SelectInput,
|
SelectInput,
|
||||||
BulkDeleteButton,
|
BulkDeleteButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
SaveButton,
|
|
||||||
maxLength,
|
maxLength,
|
||||||
regex,
|
regex,
|
||||||
required,
|
required,
|
||||||
|
@ -44,8 +45,8 @@ import {
|
||||||
CreateButton,
|
CreateButton,
|
||||||
ExportButton,
|
ExportButton,
|
||||||
TopToolbar,
|
TopToolbar,
|
||||||
sanitizeListRestProps,
|
|
||||||
NumberField,
|
NumberField,
|
||||||
|
useListContext,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import AvatarField from "./AvatarField";
|
import AvatarField from "./AvatarField";
|
||||||
|
@ -67,7 +68,7 @@ const choices_type = [
|
||||||
{ id: "support", name: "support" },
|
{ id: "support", name: "support" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const date_format = {
|
const date_format: Intl.DateTimeFormatOptions = {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
|
@ -76,43 +77,12 @@ const date_format = {
|
||||||
second: "2-digit",
|
second: "2-digit",
|
||||||
};
|
};
|
||||||
|
|
||||||
const UserListActions = ({
|
const UserListActions = () => {
|
||||||
sort,
|
const { isLoading, total } = useListContext();
|
||||||
className,
|
|
||||||
resource,
|
|
||||||
filters,
|
|
||||||
displayedFilters,
|
|
||||||
exporter, // you can hide ExportButton if exporter = (null || false)
|
|
||||||
filterValues,
|
|
||||||
permanentFilter,
|
|
||||||
hasCreate, // you can hide CreateButton if hasCreate = false
|
|
||||||
selectedIds,
|
|
||||||
onUnselectItems,
|
|
||||||
showFilter,
|
|
||||||
maxResults,
|
|
||||||
total,
|
|
||||||
...rest
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
|
<TopToolbar>
|
||||||
{filters &&
|
|
||||||
cloneElement(filters, {
|
|
||||||
resource,
|
|
||||||
showFilter,
|
|
||||||
displayedFilters,
|
|
||||||
filterValues,
|
|
||||||
context: "button",
|
|
||||||
})}
|
|
||||||
<CreateButton />
|
<CreateButton />
|
||||||
<ExportButton
|
<ExportButton disabled={isLoading || total === 0} maxResults={10000} />
|
||||||
disabled={total === 0}
|
|
||||||
resource={resource}
|
|
||||||
sort={sort}
|
|
||||||
filter={{ ...filterValues, ...permanentFilter }}
|
|
||||||
exporter={exporter}
|
|
||||||
maxResults={maxResults}
|
|
||||||
/>
|
|
||||||
{/* Add your custom actions */}
|
|
||||||
<Button component={Link} to="/import_users" label="CSV Import">
|
<Button component={Link} to="/import_users" label="CSV Import">
|
||||||
<GetAppIcon sx={{ transform: "rotate(180deg)", fontSize: "20px" }} />
|
<GetAppIcon sx={{ transform: "rotate(180deg)", fontSize: "20px" }} />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -150,13 +120,13 @@ const UserBulkActionButtons = () => (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const UserList = props => (
|
export const UserList = (props: ListProps) => (
|
||||||
<List
|
<List
|
||||||
{...props}
|
{...props}
|
||||||
filters={userFilters}
|
filters={userFilters}
|
||||||
filterDefaultValues={{ guests: true, deactivated: false }}
|
filterDefaultValues={{ guests: true, deactivated: false }}
|
||||||
sort={{ field: "name", order: "ASC" }}
|
sort={{ field: "name", order: "ASC" }}
|
||||||
actions={<UserListActions maxResults={10000} />}
|
actions={<UserListActions />}
|
||||||
pagination={<UserPagination />}
|
pagination={<UserPagination />}
|
||||||
>
|
>
|
||||||
<Datagrid rowClick="edit" bulkActionButtons={<UserBulkActionButtons />}>
|
<Datagrid rowClick="edit" bulkActionButtons={<UserBulkActionButtons />}>
|
||||||
|
@ -233,24 +203,14 @@ export function generateRandomUser() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserEditToolbar = props => (
|
const UserEditActions = () => {
|
||||||
<Toolbar {...props}>
|
const record = useRecordContext();
|
||||||
<SaveButton disabled={props.pristine} />
|
|
||||||
</Toolbar>
|
|
||||||
);
|
|
||||||
|
|
||||||
const UserEditActions = ({ data }) => {
|
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
var userStatus = "";
|
|
||||||
if (data) {
|
|
||||||
userStatus = data.deactivated;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TopToolbar>
|
<TopToolbar>
|
||||||
{!userStatus && <ServerNoticeButton record={data} />}
|
{!record.deactivated && <ServerNoticeButton />}
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
record={data}
|
|
||||||
label="resources.users.action.erase"
|
label="resources.users.action.erase"
|
||||||
confirmTitle={translate("resources.users.helper.erase", {
|
confirmTitle={translate("resources.users.helper.erase", {
|
||||||
smart_count: 1,
|
smart_count: 1,
|
||||||
|
@ -261,7 +221,7 @@ const UserEditActions = ({ data }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UserCreate = props => (
|
export const UserCreate = (props: CreateProps) => (
|
||||||
<Create {...props}>
|
<Create {...props}>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="id" autoComplete="off" validate={validateUser} />
|
<TextInput source="id" autoComplete="off" validate={validateUser} />
|
||||||
|
@ -315,11 +275,11 @@ const UserTitle = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UserEdit = props => {
|
export const UserEdit = (props: EditProps) => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Edit {...props} title={<UserTitle />} actions={<UserEditActions />}>
|
<Edit {...props} title={<UserTitle />} actions={<UserEditActions />}>
|
||||||
<TabbedForm toolbar={<UserEditToolbar />}>
|
<TabbedForm>
|
||||||
<FormTab
|
<FormTab
|
||||||
label={translate("resources.users.name", { smart_count: 1 })}
|
label={translate("resources.users.name", { smart_count: 1 })}
|
||||||
icon={<PersonPinIcon />}
|
icon={<PersonPinIcon />}
|
||||||
|
@ -389,7 +349,7 @@ export const UserEdit = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="devices"
|
reference="devices"
|
||||||
target="user_id"
|
target="user_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
>
|
>
|
||||||
<Datagrid style={{ width: "100%" }}>
|
<Datagrid style={{ width: "100%" }}>
|
||||||
<TextField source="device_id" sortable={false} />
|
<TextField source="device_id" sortable={false} />
|
||||||
|
@ -414,7 +374,7 @@ export const UserEdit = props => {
|
||||||
<ReferenceField
|
<ReferenceField
|
||||||
reference="connections"
|
reference="connections"
|
||||||
source="id"
|
source="id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
link={false}
|
link={false}
|
||||||
>
|
>
|
||||||
<ArrayField
|
<ArrayField
|
||||||
|
@ -447,7 +407,7 @@ export const UserEdit = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="users_media"
|
reference="users_media"
|
||||||
target="user_id"
|
target="user_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
pagination={<UserPagination />}
|
pagination={<UserPagination />}
|
||||||
perPage={50}
|
perPage={50}
|
||||||
sort={{ field: "created_ts", order: "DESC" }}
|
sort={{ field: "created_ts", order: "DESC" }}
|
||||||
|
@ -479,11 +439,11 @@ export const UserEdit = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="joined_rooms"
|
reference="joined_rooms"
|
||||||
target="user_id"
|
target="user_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
>
|
>
|
||||||
<Datagrid
|
<Datagrid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
rowClick={(id, resource, record) => "/rooms/" + id + "/show"}
|
rowClick={id => "/rooms/" + id + "/show"}
|
||||||
bulkActionButtons={false}
|
bulkActionButtons={false}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -512,7 +472,7 @@ export const UserEdit = props => {
|
||||||
<ReferenceManyField
|
<ReferenceManyField
|
||||||
reference="pushers"
|
reference="pushers"
|
||||||
target="user_id"
|
target="user_id"
|
||||||
addLabel={false}
|
label={false}
|
||||||
>
|
>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<TextField source="kind" sortable={false} />
|
<TextField source="kind" sortable={false} />
|
||||||
|
@ -531,7 +491,7 @@ export const UserEdit = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resource = {
|
const resource: ResourceProps = {
|
||||||
name: "users",
|
name: "users",
|
||||||
icon: UserIcon,
|
icon: UserIcon,
|
||||||
list: UserList,
|
list: UserList,
|
|
@ -1,6 +1,7 @@
|
||||||
import { formalGermanMessages } from "@haleos/ra-language-german";
|
import { formalGermanMessages } from "@haleos/ra-language-german";
|
||||||
|
import { SynapseTranslationMessages } from ".";
|
||||||
|
|
||||||
const de = {
|
const de: SynapseTranslationMessages = {
|
||||||
...formalGermanMessages,
|
...formalGermanMessages,
|
||||||
synapseadmin: {
|
synapseadmin: {
|
||||||
auth: {
|
auth: {
|
|
@ -1,6 +1,7 @@
|
||||||
import englishMessages from "ra-language-english";
|
import englishMessages from "ra-language-english";
|
||||||
|
import { SynapseTranslationMessages } from ".";
|
||||||
|
|
||||||
const en = {
|
const en: SynapseTranslationMessages = {
|
||||||
...englishMessages,
|
...englishMessages,
|
||||||
synapseadmin: {
|
synapseadmin: {
|
||||||
auth: {
|
auth: {
|
||||||
|
@ -18,6 +19,7 @@ const en = {
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
details: "Room details",
|
||||||
tabs: {
|
tabs: {
|
||||||
basic: "Basic",
|
basic: "Basic",
|
||||||
members: "Members",
|
members: "Members",
|
|
@ -1,6 +1,7 @@
|
||||||
import farsiMessages from "ra-language-farsi";
|
import farsiMessages from "ra-language-farsi";
|
||||||
|
import { SynapseTranslationMessages } from ".";
|
||||||
|
|
||||||
const fa = {
|
const fa: SynapseTranslationMessages = {
|
||||||
...farsiMessages,
|
...farsiMessages,
|
||||||
synapseadmin: {
|
synapseadmin: {
|
||||||
auth: {
|
auth: {
|
|
@ -1,6 +1,7 @@
|
||||||
import frenchMessages from "ra-language-french";
|
import frenchMessages from "ra-language-french";
|
||||||
|
import { SynapseTranslationMessages } from ".";
|
||||||
|
|
||||||
const fr = {
|
const fr: SynapseTranslationMessages = {
|
||||||
...frenchMessages,
|
...frenchMessages,
|
||||||
synapseadmin: {
|
synapseadmin: {
|
||||||
auth: {
|
auth: {
|
90
src/i18n/index.d.ts
vendored
Normal file
90
src/i18n/index.d.ts
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import { TranslationMessages } from "ra-core";
|
||||||
|
|
||||||
|
interface SynapseTranslationMessages extends TranslationMessages {
|
||||||
|
synapseadmin: {
|
||||||
|
auth: {
|
||||||
|
base_url: string;
|
||||||
|
welcome: string;
|
||||||
|
server_version: string;
|
||||||
|
supports_specs?: string; // TODO: fa, fr, it, zh
|
||||||
|
username_error: string;
|
||||||
|
protocol_error: string;
|
||||||
|
url_error: string;
|
||||||
|
sso_sign_in: string;
|
||||||
|
};
|
||||||
|
users: {
|
||||||
|
invalid_user_id: string;
|
||||||
|
tabs: { sso: string };
|
||||||
|
};
|
||||||
|
rooms: {
|
||||||
|
details?: string; // TODO: fa, fr, it, zh
|
||||||
|
tabs: {
|
||||||
|
basic: string;
|
||||||
|
members: string;
|
||||||
|
detail: string;
|
||||||
|
permission: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
reports: { tabs: { basic: string; detail: string } };
|
||||||
|
};
|
||||||
|
import_users: {
|
||||||
|
error: {
|
||||||
|
at_entry: string;
|
||||||
|
error: string;
|
||||||
|
required_field: string;
|
||||||
|
invalid_value: string;
|
||||||
|
unreasonably_big: string;
|
||||||
|
already_in_progress: string;
|
||||||
|
id_exits: string;
|
||||||
|
};
|
||||||
|
title: string;
|
||||||
|
goToPdf: string;
|
||||||
|
cards: {
|
||||||
|
importstats: {
|
||||||
|
header: string;
|
||||||
|
users_total: string;
|
||||||
|
guest_count: string;
|
||||||
|
admin_count: string;
|
||||||
|
};
|
||||||
|
conflicts: {
|
||||||
|
header: string;
|
||||||
|
mode: {
|
||||||
|
stop: string;
|
||||||
|
skip: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
ids: {
|
||||||
|
header: string;
|
||||||
|
all_ids_present: string;
|
||||||
|
count_ids_present: string;
|
||||||
|
mode: {
|
||||||
|
ignore: string;
|
||||||
|
update: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
passwords: {
|
||||||
|
header: string;
|
||||||
|
all_passwords_present: string;
|
||||||
|
count_passwords_present: string;
|
||||||
|
use_passwords: string;
|
||||||
|
};
|
||||||
|
upload: {
|
||||||
|
header: string;
|
||||||
|
explanation: string;
|
||||||
|
};
|
||||||
|
startImport: {
|
||||||
|
simulate_only: string;
|
||||||
|
run_import: string;
|
||||||
|
};
|
||||||
|
results: {
|
||||||
|
header: string;
|
||||||
|
total: string;
|
||||||
|
successful: string;
|
||||||
|
skipped: string;
|
||||||
|
download_skipped: string;
|
||||||
|
with_error: string;
|
||||||
|
simulated_only: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import italianMessages from "ra-language-italian";
|
import italianMessages from "ra-language-italian";
|
||||||
|
import { SynapseTranslationMessages } from ".";
|
||||||
|
|
||||||
const it = {
|
const it: SynapseTranslationMessages = {
|
||||||
...italianMessages,
|
...italianMessages,
|
||||||
synapseadmin: {
|
synapseadmin: {
|
||||||
auth: {
|
auth: {
|
|
@ -1,6 +1,7 @@
|
||||||
import chineseMessages from "@haxqer/ra-language-chinese";
|
import chineseMessages from "@haxqer/ra-language-chinese";
|
||||||
|
import { SynapseTranslationMessages } from ".";
|
||||||
|
|
||||||
const zh = {
|
const zh: SynapseTranslationMessages = {
|
||||||
...chineseMessages,
|
...chineseMessages,
|
||||||
synapseadmin: {
|
synapseadmin: {
|
||||||
auth: {
|
auth: {
|
||||||
|
@ -24,11 +25,6 @@ const zh = {
|
||||||
detail: "细节",
|
detail: "细节",
|
||||||
permission: "权限",
|
permission: "权限",
|
||||||
},
|
},
|
||||||
delete: {
|
|
||||||
title: "删除房间",
|
|
||||||
message:
|
|
||||||
"您确定要删除这个房间吗?该操作无法被撤销。这个房间里所有的消息和分享的媒体都将被从服务器上删除!",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
reports: { tabs: { basic: "基本", detail: "细节" } },
|
reports: { tabs: { basic: "基本", detail: "细节" } },
|
||||||
},
|
},
|
1
src/jest.setup.ts
Normal file
1
src/jest.setup.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import "@testing-library/jest-dom";
|
|
@ -1,3 +0,0 @@
|
||||||
import fetchMock from "jest-fetch-mock";
|
|
||||||
|
|
||||||
fetchMock.enableMocks();
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
import fetchMock from "jest-fetch-mock";
|
||||||
import authProvider from "./authProvider";
|
import authProvider from "./authProvider";
|
||||||
|
|
||||||
|
fetchMock.enableMocks();
|
||||||
|
|
||||||
describe("authProvider", () => {
|
describe("authProvider", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fetch.resetMocks();
|
fetchMock.resetMocks();
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("login", () => {
|
describe("login", () => {
|
||||||
it("should successfully login with username and password", async () => {
|
it("should successfully login with username and password", async () => {
|
||||||
fetch.once(
|
fetchMock.once(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
home_server: "example.com",
|
home_server: "example.com",
|
||||||
user_id: "@user:example.com",
|
user_id: "@user:example.com",
|
||||||
|
@ -17,7 +20,7 @@ describe("authProvider", () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const ret = await authProvider.login({
|
const ret: undefined = await authProvider.login({
|
||||||
base_url: "http://example.com",
|
base_url: "http://example.com",
|
||||||
username: "@user:example.com",
|
username: "@user:example.com",
|
||||||
password: "secret",
|
password: "secret",
|
||||||
|
@ -29,8 +32,8 @@ describe("authProvider", () => {
|
||||||
{
|
{
|
||||||
body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.password","user":"@user:example.com","password":"secret"}',
|
body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.password","user":"@user:example.com","password":"secret"}',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
Accept: ["application/json"],
|
Accept: "application/json",
|
||||||
"Content-Type": ["application/json"],
|
"Content-Type": "application/json",
|
||||||
}),
|
}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}
|
}
|
||||||
|
@ -43,7 +46,7 @@ describe("authProvider", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should successfully login with token", async () => {
|
it("should successfully login with token", async () => {
|
||||||
fetch.once(
|
fetchMock.once(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
home_server: "example.com",
|
home_server: "example.com",
|
||||||
user_id: "@user:example.com",
|
user_id: "@user:example.com",
|
||||||
|
@ -52,19 +55,19 @@ describe("authProvider", () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const ret = await authProvider.login({
|
const ret: undefined = await authProvider.login({
|
||||||
base_url: "https://example.com/",
|
base_url: "https://example.com/",
|
||||||
loginToken: "login_token",
|
loginToken: "login_token",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(ret).toBe(undefined);
|
expect(ret).toBe(undefined);
|
||||||
expect(fetch).toBeCalledWith(
|
expect(fetch).toHaveBeenCalledWith(
|
||||||
"https://example.com/_matrix/client/r0/login",
|
"https://example.com/_matrix/client/r0/login",
|
||||||
{
|
{
|
||||||
body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.token","token":"login_token"}',
|
body: '{"device_id":null,"initial_device_display_name":"Synapse Admin","type":"m.login.token","token":"login_token"}',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
Accept: ["application/json"],
|
Accept: "application/json",
|
||||||
"Content-Type": ["application/json"],
|
"Content-Type": "application/json",
|
||||||
}),
|
}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}
|
}
|
||||||
|
@ -79,14 +82,14 @@ describe("authProvider", () => {
|
||||||
it("should remove the access_token from localStorage", async () => {
|
it("should remove the access_token from localStorage", async () => {
|
||||||
localStorage.setItem("base_url", "example.com");
|
localStorage.setItem("base_url", "example.com");
|
||||||
localStorage.setItem("access_token", "foo");
|
localStorage.setItem("access_token", "foo");
|
||||||
fetch.mockResponse(JSON.stringify({}));
|
fetchMock.mockResponse(JSON.stringify({}));
|
||||||
|
|
||||||
await authProvider.logout();
|
await authProvider.logout(null);
|
||||||
|
|
||||||
expect(fetch).toBeCalledWith("example.com/_matrix/client/r0/logout", {
|
expect(fetch).toBeCalledWith("example.com/_matrix/client/r0/logout", {
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
Accept: ["application/json"],
|
Accept: "application/json",
|
||||||
Authorization: ["Bearer foo"],
|
Authorization: "Bearer foo",
|
||||||
}),
|
}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
user: { authenticated: true, token: "Bearer foo" },
|
user: { authenticated: true, token: "Bearer foo" },
|
||||||
|
@ -129,7 +132,7 @@ describe("authProvider", () => {
|
||||||
|
|
||||||
describe("getPermissions", () => {
|
describe("getPermissions", () => {
|
||||||
it("should do nothing", async () => {
|
it("should do nothing", async () => {
|
||||||
await expect(authProvider.getPermissions()).resolves.toBeUndefined();
|
await expect(authProvider.getPermissions(null)).resolves.toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -1,10 +1,20 @@
|
||||||
import { fetchUtils } from "react-admin";
|
import { AuthProvider, Options, fetchUtils } from "react-admin";
|
||||||
|
|
||||||
const authProvider = {
|
const authProvider: AuthProvider = {
|
||||||
// called when the user attempts to log in
|
// called when the user attempts to log in
|
||||||
login: async ({ base_url, username, password, loginToken }) => {
|
login: async ({
|
||||||
|
base_url,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
loginToken,
|
||||||
|
}: {
|
||||||
|
base_url: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
loginToken: string;
|
||||||
|
}) => {
|
||||||
console.log("login ");
|
console.log("login ");
|
||||||
const options = {
|
const options: Options = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(
|
body: JSON.stringify(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
|
@ -49,7 +59,7 @@ const authProvider = {
|
||||||
localStorage.getItem("base_url") + "/_matrix/client/r0/logout";
|
localStorage.getItem("base_url") + "/_matrix/client/r0/logout";
|
||||||
const access_token = localStorage.getItem("access_token");
|
const access_token = localStorage.getItem("access_token");
|
||||||
|
|
||||||
const options = {
|
const options: Options = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
user: {
|
user: {
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
|
@ -63,7 +73,7 @@ const authProvider = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// called when the API returns an error
|
// called when the API returns an error
|
||||||
checkError: ({ status }) => {
|
checkError: ({ status }: { status: number }) => {
|
||||||
console.log("checkError " + status);
|
console.log("checkError " + status);
|
||||||
if (status === 401 || status === 403) {
|
if (status === 401 || status === 403) {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
|
@ -1,7 +1,10 @@
|
||||||
|
import fetchMock from "jest-fetch-mock";
|
||||||
import dataProvider from "./dataProvider";
|
import dataProvider from "./dataProvider";
|
||||||
|
|
||||||
|
fetchMock.enableMocks();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fetch.resetMocks();
|
fetchMock.resetMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("dataProvider", () => {
|
describe("dataProvider", () => {
|
||||||
|
@ -9,7 +12,7 @@ describe("dataProvider", () => {
|
||||||
localStorage.setItem("access_token", "access_token");
|
localStorage.setItem("access_token", "access_token");
|
||||||
|
|
||||||
it("fetches all users", async () => {
|
it("fetches all users", async () => {
|
||||||
fetch.mockResponseOnce(
|
fetchMock.mockResponseOnce(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
users: [
|
users: [
|
||||||
{
|
{
|
||||||
|
@ -48,7 +51,7 @@ describe("dataProvider", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("fetches one user", async () => {
|
it("fetches one user", async () => {
|
||||||
fetch.mockResponseOnce(
|
fetchMock.mockResponseOnce(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
name: "user_id1",
|
name: "user_id1",
|
||||||
password: "user_password",
|
password: "user_password",
|
|
@ -1,8 +1,15 @@
|
||||||
import { fetchUtils } from "react-admin";
|
import {
|
||||||
|
DataProvider,
|
||||||
|
DeleteParams,
|
||||||
|
Identifier,
|
||||||
|
Options,
|
||||||
|
RaRecord,
|
||||||
|
fetchUtils,
|
||||||
|
} from "react-admin";
|
||||||
import { stringify } from "query-string";
|
import { stringify } from "query-string";
|
||||||
|
|
||||||
// Adds the access token to all requests
|
// Adds the access token to all requests
|
||||||
const jsonClient = (url, options = {}) => {
|
const jsonClient = (url: string, options: Options = {}) => {
|
||||||
const token = localStorage.getItem("access_token");
|
const token = localStorage.getItem("access_token");
|
||||||
console.log("httpClient " + url);
|
console.log("httpClient " + url);
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
|
@ -14,10 +21,10 @@ const jsonClient = (url, options = {}) => {
|
||||||
return fetchUtils.fetchJson(url, options);
|
return fetchUtils.fetchJson(url, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mxcUrlToHttp = mxcUrl => {
|
const mxcUrlToHttp = (mxcUrl: string) => {
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
const re = /^mxc:\/\/([^/]+)\/(\w+)/;
|
const re = /^mxc:\/\/([^/]+)\/(\w+)/;
|
||||||
var ret = re.exec(mxcUrl);
|
const ret = re.exec(mxcUrl);
|
||||||
console.log("mxcClient " + ret);
|
console.log("mxcClient " + ret);
|
||||||
if (ret == null) return null;
|
if (ret == null) return null;
|
||||||
const serverName = ret[1];
|
const serverName = ret[1];
|
||||||
|
@ -25,13 +32,188 @@ const mxcUrlToHttp = mxcUrl => {
|
||||||
return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`;
|
return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface Room {
|
||||||
|
room_id: string;
|
||||||
|
name?: string;
|
||||||
|
canonical_alias?: string;
|
||||||
|
avatar_url?: string;
|
||||||
|
joined_members: number;
|
||||||
|
joined_local_members: number;
|
||||||
|
version: number;
|
||||||
|
creator: string;
|
||||||
|
encryption?: string;
|
||||||
|
federatable: boolean;
|
||||||
|
public: boolean;
|
||||||
|
join_rules: "public" | "knock" | "invite" | "private";
|
||||||
|
guest_access?: "can_join" | "forbidden";
|
||||||
|
history_visibility: "invited" | "joined" | "shared" | "world_readable";
|
||||||
|
state_events: number;
|
||||||
|
room_type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RoomState {
|
||||||
|
age: number;
|
||||||
|
content: {
|
||||||
|
alias?: string;
|
||||||
|
};
|
||||||
|
event_id: string;
|
||||||
|
origin_server_ts: number;
|
||||||
|
room_id: string;
|
||||||
|
sender: string;
|
||||||
|
state_key: string;
|
||||||
|
type: string;
|
||||||
|
user_id: string;
|
||||||
|
unsigned: {
|
||||||
|
age?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ForwardExtremity {
|
||||||
|
event_id: string;
|
||||||
|
state_group: number;
|
||||||
|
depth: number;
|
||||||
|
received_ts: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventReport {
|
||||||
|
id: number;
|
||||||
|
received_ts: number;
|
||||||
|
room_id: string;
|
||||||
|
name: string;
|
||||||
|
event_id: string;
|
||||||
|
user_id: string;
|
||||||
|
reason?: string;
|
||||||
|
score?: number;
|
||||||
|
sender: string;
|
||||||
|
canonical_alias?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Threepid {
|
||||||
|
medium: string;
|
||||||
|
address: string;
|
||||||
|
added_at: number;
|
||||||
|
validated_at: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExternalId {
|
||||||
|
auth_provider: string;
|
||||||
|
external_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
name: string;
|
||||||
|
displayname?: string;
|
||||||
|
threepids: Threepid[];
|
||||||
|
avatar_url?: string;
|
||||||
|
is_guest: 0 | 1;
|
||||||
|
admin: 0 | 1;
|
||||||
|
deactivated: 0 | 1;
|
||||||
|
erased: boolean;
|
||||||
|
shadow_banned: 0 | 1;
|
||||||
|
creation_ts: number;
|
||||||
|
appservice_id?: string;
|
||||||
|
consent_server_notice_sent?: string;
|
||||||
|
consent_version?: string;
|
||||||
|
consent_ts?: number;
|
||||||
|
external_ids: ExternalId[];
|
||||||
|
user_type?: string;
|
||||||
|
locked: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Device {
|
||||||
|
device_id: string;
|
||||||
|
display_name?: string;
|
||||||
|
last_seen_ip?: string;
|
||||||
|
last_seen_user_agent?: string;
|
||||||
|
last_seen_ts?: number;
|
||||||
|
user_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Connection {
|
||||||
|
ip: string;
|
||||||
|
last_seen: number;
|
||||||
|
user_agent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Whois {
|
||||||
|
user_id: string;
|
||||||
|
devices: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
sessions: {
|
||||||
|
connections: Connection[];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Pusher {
|
||||||
|
app_display_name: string;
|
||||||
|
app_id: string;
|
||||||
|
data: {
|
||||||
|
url?: string;
|
||||||
|
format: string;
|
||||||
|
};
|
||||||
|
url: string;
|
||||||
|
format: string;
|
||||||
|
device_display_name: string;
|
||||||
|
profile_tag: string;
|
||||||
|
kind: string;
|
||||||
|
lang: string;
|
||||||
|
pushkey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserMedia {
|
||||||
|
created_ts: number;
|
||||||
|
last_access_ts?: number;
|
||||||
|
media_id: string;
|
||||||
|
media_length: number;
|
||||||
|
media_type: string;
|
||||||
|
quarantined_by?: string;
|
||||||
|
safe_from_quarantine: boolean;
|
||||||
|
upload_name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserMediaStatistic {
|
||||||
|
displayname: string;
|
||||||
|
media_count: number;
|
||||||
|
media_length: number;
|
||||||
|
user_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegistrationToken {
|
||||||
|
token: string;
|
||||||
|
uses_allowed: number;
|
||||||
|
pending: number;
|
||||||
|
completed: number;
|
||||||
|
expiry_time?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RaServerNotice {
|
||||||
|
id: string;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Destination {
|
||||||
|
destination: string;
|
||||||
|
retry_last_ts: number;
|
||||||
|
retry_interval: number;
|
||||||
|
failure_ts: number;
|
||||||
|
last_successful_stream_ordering?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DestinationRoom {
|
||||||
|
room_id: string;
|
||||||
|
stream_ordering: number;
|
||||||
|
}
|
||||||
|
|
||||||
const resourceMap = {
|
const resourceMap = {
|
||||||
users: {
|
users: {
|
||||||
path: "/_synapse/admin/v2/users",
|
path: "/_synapse/admin/v2/users",
|
||||||
map: u => ({
|
map: (u: User) => ({
|
||||||
...u,
|
...u,
|
||||||
id: u.name,
|
id: u.name,
|
||||||
avatar_src: mxcUrlToHttp(u.avatar_url),
|
avatar_src: u.avatar_url ? mxcUrlToHttp(u.avatar_url) : undefined,
|
||||||
is_guest: !!u.is_guest,
|
is_guest: !!u.is_guest,
|
||||||
admin: !!u.admin,
|
admin: !!u.admin,
|
||||||
deactivated: !!u.deactivated,
|
deactivated: !!u.deactivated,
|
||||||
|
@ -40,14 +222,14 @@ const resourceMap = {
|
||||||
}),
|
}),
|
||||||
data: "users",
|
data: "users",
|
||||||
total: json => json.total,
|
total: json => json.total,
|
||||||
create: data => ({
|
create: (data: RaRecord) => ({
|
||||||
endpoint: `/_synapse/admin/v2/users/@${encodeURIComponent(
|
endpoint: `/_synapse/admin/v2/users/@${encodeURIComponent(
|
||||||
data.id
|
data.id
|
||||||
)}:${localStorage.getItem("home_server")}`,
|
)}:${localStorage.getItem("home_server")}`,
|
||||||
body: data,
|
body: data,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
}),
|
}),
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_synapse/admin/v1/deactivate/${encodeURIComponent(
|
endpoint: `/_synapse/admin/v1/deactivate/${encodeURIComponent(
|
||||||
params.id
|
params.id
|
||||||
)}`,
|
)}`,
|
||||||
|
@ -57,7 +239,7 @@ const resourceMap = {
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
path: "/_synapse/admin/v1/rooms",
|
path: "/_synapse/admin/v1/rooms",
|
||||||
map: r => ({
|
map: (r: Room) => ({
|
||||||
...r,
|
...r,
|
||||||
id: r.room_id,
|
id: r.room_id,
|
||||||
alias: r.canonical_alias,
|
alias: r.canonical_alias,
|
||||||
|
@ -67,36 +249,29 @@ const resourceMap = {
|
||||||
public: !!r.public,
|
public: !!r.public,
|
||||||
}),
|
}),
|
||||||
data: "rooms",
|
data: "rooms",
|
||||||
total: json => {
|
total: json => json.total_rooms,
|
||||||
return json.total_rooms;
|
delete: (params: DeleteParams) => ({
|
||||||
},
|
|
||||||
delete: params => ({
|
|
||||||
endpoint: `/_synapse/admin/v2/rooms/${params.id}`,
|
endpoint: `/_synapse/admin/v2/rooms/${params.id}`,
|
||||||
body: { block: false },
|
body: { block: false },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
reports: {
|
reports: {
|
||||||
path: "/_synapse/admin/v1/event_reports",
|
path: "/_synapse/admin/v1/event_reports",
|
||||||
map: er => ({
|
map: (er: EventReport) => ({ ...er }),
|
||||||
...er,
|
|
||||||
id: er.id,
|
|
||||||
}),
|
|
||||||
data: "event_reports",
|
data: "event_reports",
|
||||||
total: json => json.total,
|
total: json => json.total,
|
||||||
},
|
},
|
||||||
devices: {
|
devices: {
|
||||||
map: d => ({
|
map: (d: Device) => ({
|
||||||
...d,
|
...d,
|
||||||
id: d.device_id,
|
id: d.device_id,
|
||||||
}),
|
}),
|
||||||
data: "devices",
|
data: "devices",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
reference: (id: Identifier) => ({
|
||||||
},
|
|
||||||
reference: id => ({
|
|
||||||
endpoint: `/_synapse/admin/v2/users/${encodeURIComponent(id)}/devices`,
|
endpoint: `/_synapse/admin/v2/users/${encodeURIComponent(id)}/devices`,
|
||||||
}),
|
}),
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_synapse/admin/v2/users/${encodeURIComponent(
|
endpoint: `/_synapse/admin/v2/users/${encodeURIComponent(
|
||||||
params.previousData.user_id
|
params.previousData.user_id
|
||||||
)}/devices/${params.id}`,
|
)}/devices/${params.id}`,
|
||||||
|
@ -104,84 +279,74 @@ const resourceMap = {
|
||||||
},
|
},
|
||||||
connections: {
|
connections: {
|
||||||
path: "/_synapse/admin/v1/whois",
|
path: "/_synapse/admin/v1/whois",
|
||||||
map: c => ({
|
map: (c: Whois) => ({
|
||||||
...c,
|
...c,
|
||||||
id: c.user_id,
|
id: c.user_id,
|
||||||
}),
|
}),
|
||||||
data: "connections",
|
data: "connections",
|
||||||
},
|
},
|
||||||
room_members: {
|
room_members: {
|
||||||
map: m => ({
|
map: (m: string) => ({
|
||||||
id: m,
|
id: m,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/rooms/${id}/members`,
|
endpoint: `/_synapse/admin/v1/rooms/${id}/members`,
|
||||||
}),
|
}),
|
||||||
data: "members",
|
data: "members",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
room_state: {
|
room_state: {
|
||||||
map: rs => ({
|
map: (rs: RoomState) => ({
|
||||||
...rs,
|
...rs,
|
||||||
id: rs.event_id,
|
id: rs.event_id,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/rooms/${id}/state`,
|
endpoint: `/_synapse/admin/v1/rooms/${id}/state`,
|
||||||
}),
|
}),
|
||||||
data: "state",
|
data: "state",
|
||||||
total: json => {
|
total: json => json.state.length,
|
||||||
return json.state.length;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
pushers: {
|
pushers: {
|
||||||
map: p => ({
|
map: (p: Pusher) => ({
|
||||||
...p,
|
...p,
|
||||||
id: p.pushkey,
|
id: p.pushkey,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(id)}/pushers`,
|
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(id)}/pushers`,
|
||||||
}),
|
}),
|
||||||
data: "pushers",
|
data: "pushers",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
joined_rooms: {
|
joined_rooms: {
|
||||||
map: jr => ({
|
map: (jr: string) => ({
|
||||||
id: jr,
|
id: jr,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(
|
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(
|
||||||
id
|
id
|
||||||
)}/joined_rooms`,
|
)}/joined_rooms`,
|
||||||
}),
|
}),
|
||||||
data: "joined_rooms",
|
data: "joined_rooms",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
users_media: {
|
users_media: {
|
||||||
map: um => ({
|
map: (um: UserMedia) => ({
|
||||||
...um,
|
...um,
|
||||||
id: um.media_id,
|
id: um.media_id,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(id)}/media`,
|
endpoint: `/_synapse/admin/v1/users/${encodeURIComponent(id)}/media`,
|
||||||
}),
|
}),
|
||||||
data: "media",
|
data: "media",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
delete: (params: DeleteParams) => ({
|
||||||
},
|
|
||||||
delete: params => ({
|
|
||||||
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
|
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
|
||||||
"home_server"
|
"home_server"
|
||||||
)}/${params.id}`,
|
)}/${params.id}`,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
delete_media: {
|
delete_media: {
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
|
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
|
||||||
"home_server"
|
"home_server"
|
||||||
)}/delete?before_ts=${params.meta.before_ts}&size_gt=${
|
)}/delete?before_ts=${params.meta.before_ts}&size_gt=${
|
||||||
|
@ -191,25 +356,25 @@ const resourceMap = {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
protect_media: {
|
protect_media: {
|
||||||
map: pm => ({ id: pm.media_id }),
|
map: (pm: UserMedia) => ({ id: pm.media_id }),
|
||||||
create: params => ({
|
create: (params: UserMedia) => ({
|
||||||
endpoint: `/_synapse/admin/v1/media/protect/${params.media_id}`,
|
endpoint: `/_synapse/admin/v1/media/protect/${params.media_id}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_synapse/admin/v1/media/unprotect/${params.id}`,
|
endpoint: `/_synapse/admin/v1/media/unprotect/${params.id}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
quarantine_media: {
|
quarantine_media: {
|
||||||
map: qm => ({ id: qm.media_id }),
|
map: (qm: UserMedia) => ({ id: qm.media_id }),
|
||||||
create: params => ({
|
create: (params: UserMedia) => ({
|
||||||
endpoint: `/_synapse/admin/v1/media/quarantine/${localStorage.getItem(
|
endpoint: `/_synapse/admin/v1/media/quarantine/${localStorage.getItem(
|
||||||
"home_server"
|
"home_server"
|
||||||
)}/${params.media_id}`,
|
)}/${params.media_id}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_synapse/admin/v1/media/unquarantine/${localStorage.getItem(
|
endpoint: `/_synapse/admin/v1/media/unquarantine/${localStorage.getItem(
|
||||||
"home_server"
|
"home_server"
|
||||||
)}/${params.id}`,
|
)}/${params.id}`,
|
||||||
|
@ -217,8 +382,8 @@ const resourceMap = {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
servernotices: {
|
servernotices: {
|
||||||
map: n => ({ id: n.event_id }),
|
map: (n: { event_id: string }) => ({ id: n.event_id }),
|
||||||
create: data => ({
|
create: (data: RaServerNotice) => ({
|
||||||
endpoint: "/_synapse/admin/v1/send_server_notice",
|
endpoint: "/_synapse/admin/v1/send_server_notice",
|
||||||
body: {
|
body: {
|
||||||
user_id: data.id,
|
user_id: data.id,
|
||||||
|
@ -232,50 +397,44 @@ const resourceMap = {
|
||||||
},
|
},
|
||||||
user_media_statistics: {
|
user_media_statistics: {
|
||||||
path: "/_synapse/admin/v1/statistics/users/media",
|
path: "/_synapse/admin/v1/statistics/users/media",
|
||||||
map: usms => ({
|
map: (usms: UserMediaStatistic) => ({
|
||||||
...usms,
|
...usms,
|
||||||
id: usms.user_id,
|
id: usms.user_id,
|
||||||
}),
|
}),
|
||||||
data: "users",
|
data: "users",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
forward_extremities: {
|
forward_extremities: {
|
||||||
map: fe => ({
|
map: (fe: ForwardExtremity) => ({
|
||||||
...fe,
|
...fe,
|
||||||
id: fe.event_id,
|
id: fe.event_id,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/rooms/${id}/forward_extremities`,
|
endpoint: `/_synapse/admin/v1/rooms/${id}/forward_extremities`,
|
||||||
}),
|
}),
|
||||||
data: "results",
|
data: "results",
|
||||||
total: json => {
|
total: json => json.count,
|
||||||
return json.count;
|
delete: (params: DeleteParams) => ({
|
||||||
},
|
|
||||||
delete: params => ({
|
|
||||||
endpoint: `/_synapse/admin/v1/rooms/${params.id}/forward_extremities`,
|
endpoint: `/_synapse/admin/v1/rooms/${params.id}/forward_extremities`,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
room_directory: {
|
room_directory: {
|
||||||
path: "/_matrix/client/r0/publicRooms",
|
path: "/_matrix/client/r0/publicRooms",
|
||||||
map: rd => ({
|
map: (rd: Room) => ({
|
||||||
...rd,
|
...rd,
|
||||||
id: rd.room_id,
|
id: rd.room_id,
|
||||||
public: !!rd.public,
|
public: !!rd.public,
|
||||||
guest_access: !!rd.guest_access,
|
guest_access: !!rd.guest_access,
|
||||||
avatar_src: mxcUrlToHttp(rd.avatar_url),
|
avatar_src: rd.avatar_url ? mxcUrlToHttp(rd.avatar_url) : undefined,
|
||||||
}),
|
}),
|
||||||
data: "chunk",
|
data: "chunk",
|
||||||
total: json => {
|
total: json => json.total_room_count_estimate,
|
||||||
return json.total_room_count_estimate;
|
create: (params: RaRecord) => ({
|
||||||
},
|
|
||||||
create: params => ({
|
|
||||||
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
|
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
|
||||||
body: { visibility: "public" },
|
body: { visibility: "public" },
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
}),
|
}),
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
|
endpoint: `/_matrix/client/r0/directory/list/room/${params.id}`,
|
||||||
body: { visibility: "private" },
|
body: { visibility: "private" },
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
|
@ -283,54 +442,49 @@ const resourceMap = {
|
||||||
},
|
},
|
||||||
destinations: {
|
destinations: {
|
||||||
path: "/_synapse/admin/v1/federation/destinations",
|
path: "/_synapse/admin/v1/federation/destinations",
|
||||||
map: dst => ({
|
map: (dst: Destination) => ({
|
||||||
...dst,
|
...dst,
|
||||||
id: dst.destination,
|
id: dst.destination,
|
||||||
}),
|
}),
|
||||||
data: "destinations",
|
data: "destinations",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
|
||||||
},
|
|
||||||
delete: params => ({
|
delete: params => ({
|
||||||
endpoint: `/_synapse/admin/v1/federation/destinations/${params.id}/reset_connection`,
|
endpoint: `/_synapse/admin/v1/federation/destinations/${params.id}/reset_connection`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
destination_rooms: {
|
destination_rooms: {
|
||||||
map: dstroom => ({
|
map: (dstroom: DestinationRoom) => ({
|
||||||
...dstroom,
|
...dstroom,
|
||||||
id: dstroom.room_id,
|
id: dstroom.room_id,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: (id: Identifier) => ({
|
||||||
endpoint: `/_synapse/admin/v1/federation/destinations/${id}/rooms`,
|
endpoint: `/_synapse/admin/v1/federation/destinations/${id}/rooms`,
|
||||||
}),
|
}),
|
||||||
data: "rooms",
|
data: "rooms",
|
||||||
total: json => {
|
total: json => json.total,
|
||||||
return json.total;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
registration_tokens: {
|
registration_tokens: {
|
||||||
path: "/_synapse/admin/v1/registration_tokens",
|
path: "/_synapse/admin/v1/registration_tokens",
|
||||||
map: rt => ({
|
map: (rt: RegistrationToken) => ({
|
||||||
...rt,
|
...rt,
|
||||||
id: rt.token,
|
id: rt.token,
|
||||||
}),
|
}),
|
||||||
data: "registration_tokens",
|
data: "registration_tokens",
|
||||||
total: json => {
|
total: json => json.registration_tokens.length,
|
||||||
return json.registration_tokens.length;
|
create: (params: RaRecord) => ({
|
||||||
},
|
|
||||||
create: params => ({
|
|
||||||
endpoint: "/_synapse/admin/v1/registration_tokens/new",
|
endpoint: "/_synapse/admin/v1/registration_tokens/new",
|
||||||
body: params,
|
body: params,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
delete: params => ({
|
delete: (params: DeleteParams) => ({
|
||||||
endpoint: `/_synapse/admin/v1/registration_tokens/${params.id}`,
|
endpoint: `/_synapse/admin/v1/registration_tokens/${params.id}`,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function filterNullValues(key, value) {
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
function filterNullValues(key: string, value: any) {
|
||||||
// Filtering out null properties
|
// Filtering out null properties
|
||||||
// to reset user_type from user, it must be null
|
// to reset user_type from user, it must be null
|
||||||
if (value === null && key !== "user_type") {
|
if (value === null && key !== "user_type") {
|
||||||
|
@ -339,7 +493,7 @@ function filterNullValues(key, value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSearchOrder(order) {
|
function getSearchOrder(order: "ASC" | "DESC") {
|
||||||
if (order === "DESC") {
|
if (order === "DESC") {
|
||||||
return "b";
|
return "b";
|
||||||
} else {
|
} else {
|
||||||
|
@ -347,7 +501,7 @@ function getSearchOrder(order) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataProvider = {
|
const dataProvider: DataProvider = {
|
||||||
getList: async (resource, params) => {
|
getList: async (resource, params) => {
|
||||||
console.log("getList " + resource);
|
console.log("getList " + resource);
|
||||||
const {
|
const {
|
||||||
|
@ -376,7 +530,8 @@ const dataProvider = {
|
||||||
dir: getSearchOrder(order),
|
dir: getSearchOrder(order),
|
||||||
};
|
};
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -393,7 +548,8 @@ const dataProvider = {
|
||||||
getOne: async (resource, params) => {
|
getOne: async (resource, params) => {
|
||||||
console.log("getOne " + resource);
|
console.log("getOne " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -407,7 +563,8 @@ const dataProvider = {
|
||||||
getMany: async (resource, params) => {
|
getMany: async (resource, params) => {
|
||||||
console.log("getMany " + resource);
|
console.log("getMany " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homerserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -436,7 +593,8 @@ const dataProvider = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -453,7 +611,8 @@ const dataProvider = {
|
||||||
update: async (resource, params) => {
|
update: async (resource, params) => {
|
||||||
console.log("update " + resource);
|
console.log("update " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -471,7 +630,8 @@ const dataProvider = {
|
||||||
updateMany: async (resource, params) => {
|
updateMany: async (resource, params) => {
|
||||||
console.log("updateMany " + resource);
|
console.log("updateMany " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -491,7 +651,8 @@ const dataProvider = {
|
||||||
create: async (resource, params) => {
|
create: async (resource, params) => {
|
||||||
console.log("create " + resource);
|
console.log("create " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
if (!("create" in res)) return Promise.reject();
|
if (!("create" in res)) return Promise.reject();
|
||||||
|
@ -505,13 +666,17 @@ const dataProvider = {
|
||||||
return { data: res.map(json) };
|
return { data: res.map(json) };
|
||||||
},
|
},
|
||||||
|
|
||||||
createMany: async (resource, params) => {
|
createMany: async (
|
||||||
|
resource: string,
|
||||||
|
params: { ids: Identifier[]; data: RaRecord }
|
||||||
|
) => {
|
||||||
console.log("createMany " + resource);
|
console.log("createMany " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
if (!("create" in res)) return Promise.reject();
|
if (!("create" in res)) throw Error(`Create ${resource} is not allowed`);
|
||||||
|
|
||||||
const responses = await Promise.all(
|
const responses = await Promise.all(
|
||||||
params.ids.map(id => {
|
params.ids.map(id => {
|
||||||
|
@ -530,7 +695,8 @@ const dataProvider = {
|
||||||
delete: async (resource, params) => {
|
delete: async (resource, params) => {
|
||||||
console.log("delete " + resource);
|
console.log("delete " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -555,7 +721,8 @@ const dataProvider = {
|
||||||
deleteMany: async (resource, params) => {
|
deleteMany: async (resource, params) => {
|
||||||
console.log("deleteMany " + resource);
|
console.log("deleteMany " + resource);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap))
|
||||||
|
throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
@ -579,7 +746,7 @@ const dataProvider = {
|
||||||
params.ids.map(id =>
|
params.ids.map(id =>
|
||||||
jsonClient(`${endpoint_url}/${id}`, {
|
jsonClient(`${endpoint_url}/${id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
body: JSON.stringify(params.data, filterNullValues),
|
// body: JSON.stringify(params.data, filterNullValues), @FIXME
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
4
tsconfig.eslint.json
Normal file
4
tsconfig.eslint.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["./**/*.ts", "./**/*.tsx"]
|
||||||
|
}
|
64
tsconfig.json
Normal file
64
tsconfig.json
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// prettier-ignore
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Basic Options */
|
||||||
|
"target": "ESNext" /* Specify ECMAScript target version */,
|
||||||
|
"module": "ESNext" /* Specify module code generation */,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"] /* Specify library files to be included in the compilation. */,
|
||||||
|
"allowJs": false /* Allow javascript files to be compiled. */,
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
"jsx": "react-jsx" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
|
||||||
|
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||||
|
"declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */,
|
||||||
|
"sourceMap": true /* Generates corresponding '.map' file. */,
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
// "outDir": "./lib", /* Redirect output structure to the directory. */
|
||||||
|
"rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "composite": true, /* Enable project compilation */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
"noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
|
"noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
"moduleResolution": "Bundler" /* Specify module resolution strategy */,
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
"types": ["vite/client"], /* Type declaration files to be included in compilation. */
|
||||||
|
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
|
||||||
|
"esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
"skipLibCheck": false
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.vite.json" }]
|
||||||
|
}
|
8
tsconfig.vite.json
Normal file
8
tsconfig.vite.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
260
yarn.lock
260
yarn.lock
|
@ -46,7 +46,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.16.0, @babel/core@npm:^7.23.5, @babel/core@npm:^7.23.9, @babel/core@npm:^7.24.4":
|
"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.16.0, @babel/core@npm:^7.23.5, @babel/core@npm:^7.23.9":
|
||||||
version: 7.24.4
|
version: 7.24.4
|
||||||
resolution: "@babel/core@npm:7.24.4"
|
resolution: "@babel/core@npm:7.24.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1456,7 +1456,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/preset-env@npm:^7.16.4, @babel/preset-env@npm:^7.24.4":
|
"@babel/preset-env@npm:^7.16.4":
|
||||||
version: 7.24.4
|
version: 7.24.4
|
||||||
resolution: "@babel/preset-env@npm:7.24.4"
|
resolution: "@babel/preset-env@npm:7.24.4"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1560,7 +1560,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@babel/preset-react@npm:^7.16.0, @babel/preset-react@npm:^7.24.1":
|
"@babel/preset-react@npm:^7.16.0":
|
||||||
version: 7.24.1
|
version: 7.24.1
|
||||||
resolution: "@babel/preset-react@npm:7.24.1"
|
resolution: "@babel/preset-react@npm:7.24.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1654,6 +1654,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@cspotcode/source-map-support@npm:^0.8.0":
|
||||||
|
version: 0.8.1
|
||||||
|
resolution: "@cspotcode/source-map-support@npm:0.8.1"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/trace-mapping": "npm:0.3.9"
|
||||||
|
checksum: 10c0/05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@emotion/babel-plugin@npm:^11.11.0":
|
"@emotion/babel-plugin@npm:^11.11.0":
|
||||||
version: 11.11.0
|
version: 11.11.0
|
||||||
resolution: "@emotion/babel-plugin@npm:11.11.0"
|
resolution: "@emotion/babel-plugin@npm:11.11.0"
|
||||||
|
@ -2363,7 +2372,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@jridgewell/resolve-uri@npm:^3.1.0":
|
"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0":
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
resolution: "@jridgewell/resolve-uri@npm:3.1.2"
|
resolution: "@jridgewell/resolve-uri@npm:3.1.2"
|
||||||
checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
|
checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
|
||||||
|
@ -2384,6 +2393,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@npm:0.3.9":
|
||||||
|
version: 0.3.9
|
||||||
|
resolution: "@jridgewell/trace-mapping@npm:0.3.9"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri": "npm:^3.0.3"
|
||||||
|
"@jridgewell/sourcemap-codec": "npm:^1.4.10"
|
||||||
|
checksum: 10c0/fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25":
|
"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25":
|
||||||
version: 0.3.25
|
version: 0.3.25
|
||||||
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
|
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
|
||||||
|
@ -2877,6 +2896,34 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node10@npm:^1.0.7":
|
||||||
|
version: 1.0.11
|
||||||
|
resolution: "@tsconfig/node10@npm:1.0.11"
|
||||||
|
checksum: 10c0/28a0710e5d039e0de484bdf85fee883bfd3f6a8980601f4d44066b0a6bcd821d31c4e231d1117731c4e24268bd4cf2a788a6787c12fc7f8d11014c07d582783c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node12@npm:^1.0.7":
|
||||||
|
version: 1.0.11
|
||||||
|
resolution: "@tsconfig/node12@npm:1.0.11"
|
||||||
|
checksum: 10c0/dddca2b553e2bee1308a056705103fc8304e42bb2d2cbd797b84403a223b25c78f2c683ec3e24a095e82cd435387c877239bffcb15a590ba817cd3f6b9a99fd9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node14@npm:^1.0.0":
|
||||||
|
version: 1.0.3
|
||||||
|
resolution: "@tsconfig/node14@npm:1.0.3"
|
||||||
|
checksum: 10c0/67c1316d065fdaa32525bc9449ff82c197c4c19092b9663b23213c8cbbf8d88b6ed6a17898e0cbc2711950fbfaf40388938c1c748a2ee89f7234fc9e7fe2bf44
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tsconfig/node16@npm:^1.0.2":
|
||||||
|
version: 1.0.4
|
||||||
|
resolution: "@tsconfig/node16@npm:1.0.4"
|
||||||
|
checksum: 10c0/05f8f2734e266fb1839eb1d57290df1664fe2aa3b0fdd685a9035806daa635f7519bf6d5d9b33f6e69dd545b8c46bd6e2b5c79acb2b1f146e885f7f11a42a5bb
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/aria-query@npm:^5.0.1":
|
"@types/aria-query@npm:^5.0.1":
|
||||||
version: 5.0.4
|
version: 5.0.4
|
||||||
resolution: "@types/aria-query@npm:5.0.4"
|
resolution: "@types/aria-query@npm:5.0.4"
|
||||||
|
@ -2966,6 +3013,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/jest@npm:^29.5.12":
|
||||||
|
version: 29.5.12
|
||||||
|
resolution: "@types/jest@npm:29.5.12"
|
||||||
|
dependencies:
|
||||||
|
expect: "npm:^29.0.0"
|
||||||
|
pretty-format: "npm:^29.0.0"
|
||||||
|
checksum: 10c0/25fc8e4c611fa6c4421e631432e9f0a6865a8cb07c9815ec9ac90d630271cad773b2ee5fe08066f7b95bebd18bb967f8ce05d018ee9ab0430f9dfd1d84665b6f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/jsdom@npm:^20.0.0":
|
"@types/jsdom@npm:^20.0.0":
|
||||||
version: 20.0.1
|
version: 20.0.1
|
||||||
resolution: "@types/jsdom@npm:20.0.1"
|
resolution: "@types/jsdom@npm:20.0.1"
|
||||||
|
@ -2991,7 +3048,14 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:*":
|
"@types/lodash@npm:^4.17.0":
|
||||||
|
version: 4.17.0
|
||||||
|
resolution: "@types/lodash@npm:4.17.0"
|
||||||
|
checksum: 10c0/4c5b41c9a6c41e2c05d08499e96f7940bcf194dcfa84356235b630da920c2a5e05f193618cea76006719bec61c76617dff02defa9d29934f9f6a76a49291bd8f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/node@npm:*, @types/node@npm:^20.12.7":
|
||||||
version: 20.12.7
|
version: 20.12.7
|
||||||
resolution: "@types/node@npm:20.12.7"
|
resolution: "@types/node@npm:20.12.7"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3000,6 +3064,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/papaparse@npm:^5.3.14":
|
||||||
|
version: 5.3.14
|
||||||
|
resolution: "@types/papaparse@npm:5.3.14"
|
||||||
|
dependencies:
|
||||||
|
"@types/node": "npm:*"
|
||||||
|
checksum: 10c0/feb4d215903b67442feaa9836a6a5771e78dc6a9da24781e399c6f891622fa82245cd783ab2613c5be43e4a2d6a94da52325538e4485af258166864576ecd0d8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/parse-json@npm:^4.0.0":
|
"@types/parse-json@npm:^4.0.0":
|
||||||
version: 4.0.2
|
version: 4.0.2
|
||||||
resolution: "@types/parse-json@npm:4.0.2"
|
resolution: "@types/parse-json@npm:4.0.2"
|
||||||
|
@ -3032,7 +3105,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/react@npm:*":
|
"@types/react@npm:*, @types/react@npm:^18.2.79":
|
||||||
version: 18.2.79
|
version: 18.2.79
|
||||||
resolution: "@types/react@npm:18.2.79"
|
resolution: "@types/react@npm:18.2.79"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -3266,14 +3339,14 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"acorn-walk@npm:^8.0.2":
|
"acorn-walk@npm:^8.0.2, acorn-walk@npm:^8.1.1":
|
||||||
version: 8.3.2
|
version: 8.3.2
|
||||||
resolution: "acorn-walk@npm:8.3.2"
|
resolution: "acorn-walk@npm:8.3.2"
|
||||||
checksum: 10c0/7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52
|
checksum: 10c0/7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"acorn@npm:^8.1.0, acorn@npm:^8.8.1, acorn@npm:^8.9.0":
|
"acorn@npm:^8.1.0, acorn@npm:^8.4.1, acorn@npm:^8.8.1, acorn@npm:^8.9.0":
|
||||||
version: 8.11.3
|
version: 8.11.3
|
||||||
resolution: "acorn@npm:8.11.3"
|
resolution: "acorn@npm:8.11.3"
|
||||||
bin:
|
bin:
|
||||||
|
@ -3387,6 +3460,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"arg@npm:^4.1.0":
|
||||||
|
version: 4.1.3
|
||||||
|
resolution: "arg@npm:4.1.3"
|
||||||
|
checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"argparse@npm:^1.0.7":
|
"argparse@npm:^1.0.7":
|
||||||
version: 1.0.10
|
version: 1.0.10
|
||||||
resolution: "argparse@npm:1.0.10"
|
resolution: "argparse@npm:1.0.10"
|
||||||
|
@ -3832,6 +3912,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"bs-logger@npm:0.x":
|
||||||
|
version: 0.2.6
|
||||||
|
resolution: "bs-logger@npm:0.2.6"
|
||||||
|
dependencies:
|
||||||
|
fast-json-stable-stringify: "npm:2.x"
|
||||||
|
checksum: 10c0/80e89aaaed4b68e3374ce936f2eb097456a0dddbf11f75238dbd53140b1e39259f0d248a5089ed456f1158984f22191c3658d54a713982f676709fbe1a6fa5a0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"bser@npm:2.1.1":
|
"bser@npm:2.1.1":
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
resolution: "bser@npm:2.1.1"
|
resolution: "bser@npm:2.1.1"
|
||||||
|
@ -4122,6 +4211,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"create-require@npm:^1.1.0":
|
||||||
|
version: 1.1.1
|
||||||
|
resolution: "create-require@npm:1.1.1"
|
||||||
|
checksum: 10c0/157cbc59b2430ae9a90034a5f3a1b398b6738bf510f713edc4d4e45e169bc514d3d99dd34d8d01ca7ae7830b5b8b537e46ae8f3c8f932371b0875c0151d7ec91
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"cross-fetch@npm:^3.0.4":
|
"cross-fetch@npm:^3.0.4":
|
||||||
version: 3.1.8
|
version: 3.1.8
|
||||||
resolution: "cross-fetch@npm:3.1.8"
|
resolution: "cross-fetch@npm:3.1.8"
|
||||||
|
@ -4364,6 +4460,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"diff@npm:^4.0.1":
|
||||||
|
version: 4.0.2
|
||||||
|
resolution: "diff@npm:4.0.2"
|
||||||
|
checksum: 10c0/81b91f9d39c4eaca068eb0c1eb0e4afbdc5bb2941d197f513dd596b820b956fef43485876226d65d497bebc15666aa2aa82c679e84f65d5f2bfbf14ee46e32c1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"dir-glob@npm:^3.0.1":
|
"dir-glob@npm:^3.0.1":
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
resolution: "dir-glob@npm:3.0.1"
|
resolution: "dir-glob@npm:3.0.1"
|
||||||
|
@ -5153,7 +5256,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"expect@npm:^29.7.0":
|
"expect@npm:^29.0.0, expect@npm:^29.7.0":
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
resolution: "expect@npm:29.7.0"
|
resolution: "expect@npm:29.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5200,7 +5303,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0":
|
"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0":
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
resolution: "fast-json-stable-stringify@npm:2.1.0"
|
||||||
checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b
|
checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b
|
||||||
|
@ -6605,7 +6708,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"jest-util@npm:^29.7.0":
|
"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0":
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
resolution: "jest-util@npm:29.7.0"
|
resolution: "jest-util@npm:29.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6931,6 +7034,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"lodash.memoize@npm:4.x":
|
||||||
|
version: 4.1.2
|
||||||
|
resolution: "lodash.memoize@npm:4.1.2"
|
||||||
|
checksum: 10c0/c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"lodash.merge@npm:^4.6.2":
|
"lodash.merge@npm:^4.6.2":
|
||||||
version: 4.6.2
|
version: 4.6.2
|
||||||
resolution: "lodash.merge@npm:4.6.2"
|
resolution: "lodash.merge@npm:4.6.2"
|
||||||
|
@ -6999,6 +7109,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"make-error@npm:1.x, make-error@npm:^1.1.1":
|
||||||
|
version: 1.3.6
|
||||||
|
resolution: "make-error@npm:1.3.6"
|
||||||
|
checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"make-fetch-happen@npm:^13.0.0":
|
"make-fetch-happen@npm:^13.0.0":
|
||||||
version: 13.0.0
|
version: 13.0.0
|
||||||
resolution: "make-fetch-happen@npm:13.0.0"
|
resolution: "make-fetch-happen@npm:13.0.0"
|
||||||
|
@ -7710,7 +7827,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"pretty-format@npm:^29.7.0":
|
"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0":
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
resolution: "pretty-format@npm:29.7.0"
|
resolution: "pretty-format@npm:29.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -8857,9 +8974,6 @@ __metadata:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "synapse-admin@workspace:."
|
resolution: "synapse-admin@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core": "npm:^7.24.4"
|
|
||||||
"@babel/preset-env": "npm:^7.24.4"
|
|
||||||
"@babel/preset-react": "npm:^7.24.1"
|
|
||||||
"@emotion/react": "npm:^11.4.1"
|
"@emotion/react": "npm:^11.4.1"
|
||||||
"@emotion/styled": "npm:^11.3.0"
|
"@emotion/styled": "npm:^11.3.0"
|
||||||
"@haleos/ra-language-german": "npm:^1.0.0"
|
"@haleos/ra-language-german": "npm:^1.0.0"
|
||||||
|
@ -8870,8 +8984,12 @@ __metadata:
|
||||||
"@testing-library/jest-dom": "npm:^6.0.0"
|
"@testing-library/jest-dom": "npm:^6.0.0"
|
||||||
"@testing-library/react": "npm:^15.0.2"
|
"@testing-library/react": "npm:^15.0.2"
|
||||||
"@testing-library/user-event": "npm:^14.5.2"
|
"@testing-library/user-event": "npm:^14.5.2"
|
||||||
|
"@types/jest": "npm:^29.5.12"
|
||||||
|
"@types/lodash": "npm:^4.17.0"
|
||||||
|
"@types/node": "npm:^20.12.7"
|
||||||
|
"@types/papaparse": "npm:^5.3.14"
|
||||||
|
"@types/react": "npm:^18.2.79"
|
||||||
"@vitejs/plugin-react": "npm:^4.0.0"
|
"@vitejs/plugin-react": "npm:^4.0.0"
|
||||||
babel-jest: "npm:^29.7.0"
|
|
||||||
eslint: "npm:^8.57.0"
|
eslint: "npm:^8.57.0"
|
||||||
eslint-config-prettier: "npm:^9.1.0"
|
eslint-config-prettier: "npm:^9.1.0"
|
||||||
eslint-config-react-app: "npm:^7.0.1"
|
eslint-config-react-app: "npm:^7.0.1"
|
||||||
|
@ -8899,6 +9017,9 @@ __metadata:
|
||||||
react-router: "npm:^6.1.0"
|
react-router: "npm:^6.1.0"
|
||||||
react-router-dom: "npm:^6.1.0"
|
react-router-dom: "npm:^6.1.0"
|
||||||
react-test-renderer: "npm:^18.2.0"
|
react-test-renderer: "npm:^18.2.0"
|
||||||
|
ts-jest: "npm:^29.1.2"
|
||||||
|
ts-node: "npm:^10.9.2"
|
||||||
|
typescript: "npm:^5.4.5"
|
||||||
vite: "npm:^5.0.0"
|
vite: "npm:^5.0.0"
|
||||||
vite-plugin-version-mark: "npm:^0.0.13"
|
vite-plugin-version-mark: "npm:^0.0.13"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
|
@ -8997,6 +9118,77 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ts-jest@npm:^29.1.2":
|
||||||
|
version: 29.1.2
|
||||||
|
resolution: "ts-jest@npm:29.1.2"
|
||||||
|
dependencies:
|
||||||
|
bs-logger: "npm:0.x"
|
||||||
|
fast-json-stable-stringify: "npm:2.x"
|
||||||
|
jest-util: "npm:^29.0.0"
|
||||||
|
json5: "npm:^2.2.3"
|
||||||
|
lodash.memoize: "npm:4.x"
|
||||||
|
make-error: "npm:1.x"
|
||||||
|
semver: "npm:^7.5.3"
|
||||||
|
yargs-parser: "npm:^21.0.1"
|
||||||
|
peerDependencies:
|
||||||
|
"@babel/core": ">=7.0.0-beta.0 <8"
|
||||||
|
"@jest/types": ^29.0.0
|
||||||
|
babel-jest: ^29.0.0
|
||||||
|
jest: ^29.0.0
|
||||||
|
typescript: ">=4.3 <6"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
"@babel/core":
|
||||||
|
optional: true
|
||||||
|
"@jest/types":
|
||||||
|
optional: true
|
||||||
|
babel-jest:
|
||||||
|
optional: true
|
||||||
|
esbuild:
|
||||||
|
optional: true
|
||||||
|
bin:
|
||||||
|
ts-jest: cli.js
|
||||||
|
checksum: 10c0/c2f51f0241f89d127d41392decbcb83b5dfd5e57ab9d50220aa7b7e2f9b3f3b07ccdbba33311284df1c41941879e4ddfad44b15a9d0da4b74bd1b98702b729df
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"ts-node@npm:^10.9.2":
|
||||||
|
version: 10.9.2
|
||||||
|
resolution: "ts-node@npm:10.9.2"
|
||||||
|
dependencies:
|
||||||
|
"@cspotcode/source-map-support": "npm:^0.8.0"
|
||||||
|
"@tsconfig/node10": "npm:^1.0.7"
|
||||||
|
"@tsconfig/node12": "npm:^1.0.7"
|
||||||
|
"@tsconfig/node14": "npm:^1.0.0"
|
||||||
|
"@tsconfig/node16": "npm:^1.0.2"
|
||||||
|
acorn: "npm:^8.4.1"
|
||||||
|
acorn-walk: "npm:^8.1.1"
|
||||||
|
arg: "npm:^4.1.0"
|
||||||
|
create-require: "npm:^1.1.0"
|
||||||
|
diff: "npm:^4.0.1"
|
||||||
|
make-error: "npm:^1.1.1"
|
||||||
|
v8-compile-cache-lib: "npm:^3.0.1"
|
||||||
|
yn: "npm:3.1.1"
|
||||||
|
peerDependencies:
|
||||||
|
"@swc/core": ">=1.2.50"
|
||||||
|
"@swc/wasm": ">=1.2.50"
|
||||||
|
"@types/node": "*"
|
||||||
|
typescript: ">=2.7"
|
||||||
|
peerDependenciesMeta:
|
||||||
|
"@swc/core":
|
||||||
|
optional: true
|
||||||
|
"@swc/wasm":
|
||||||
|
optional: true
|
||||||
|
bin:
|
||||||
|
ts-node: dist/bin.js
|
||||||
|
ts-node-cwd: dist/bin-cwd.js
|
||||||
|
ts-node-esm: dist/bin-esm.js
|
||||||
|
ts-node-script: dist/bin-script.js
|
||||||
|
ts-node-transpile-only: dist/bin-transpile.js
|
||||||
|
ts-script: dist/bin-script-deprecated.js
|
||||||
|
checksum: 10c0/5f29938489f96982a25ba650b64218e83a3357d76f7bede80195c65ab44ad279c8357264639b7abdd5d7e75fc269a83daa0e9c62fd8637a3def67254ecc9ddc2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"tsconfig-paths@npm:^3.15.0":
|
"tsconfig-paths@npm:^3.15.0":
|
||||||
version: 3.15.0
|
version: 3.15.0
|
||||||
resolution: "tsconfig-paths@npm:3.15.0"
|
resolution: "tsconfig-paths@npm:3.15.0"
|
||||||
|
@ -9123,6 +9315,26 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"typescript@npm:^5.4.5":
|
||||||
|
version: 5.4.5
|
||||||
|
resolution: "typescript@npm:5.4.5"
|
||||||
|
bin:
|
||||||
|
tsc: bin/tsc
|
||||||
|
tsserver: bin/tsserver
|
||||||
|
checksum: 10c0/2954022ada340fd3d6a9e2b8e534f65d57c92d5f3989a263754a78aba549f7e6529acc1921913560a4b816c46dce7df4a4d29f9f11a3dc0d4213bb76d043251e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"typescript@patch:typescript@npm%3A^5.4.5#optional!builtin<compat/typescript>":
|
||||||
|
version: 5.4.5
|
||||||
|
resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c"
|
||||||
|
bin:
|
||||||
|
tsc: bin/tsc
|
||||||
|
tsserver: bin/tsserver
|
||||||
|
checksum: 10c0/db2ad2a16ca829f50427eeb1da155e7a45e598eec7b086d8b4e8ba44e5a235f758e606d681c66992230d3fc3b8995865e5fd0b22a2c95486d0b3200f83072ec9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"unbox-primitive@npm:^1.0.2":
|
"unbox-primitive@npm:^1.0.2":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "unbox-primitive@npm:1.0.2"
|
resolution: "unbox-primitive@npm:1.0.2"
|
||||||
|
@ -9241,6 +9453,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"v8-compile-cache-lib@npm:^3.0.1":
|
||||||
|
version: 3.0.1
|
||||||
|
resolution: "v8-compile-cache-lib@npm:3.0.1"
|
||||||
|
checksum: 10c0/bdc36fb8095d3b41df197f5fb6f11e3a26adf4059df3213e3baa93810d8f0cc76f9a74aaefc18b73e91fe7e19154ed6f134eda6fded2e0f1c8d2272ed2d2d391
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"v8-to-istanbul@npm:^9.0.1":
|
"v8-to-istanbul@npm:^9.0.1":
|
||||||
version: 9.2.0
|
version: 9.2.0
|
||||||
resolution: "v8-to-istanbul@npm:9.2.0"
|
resolution: "v8-to-istanbul@npm:9.2.0"
|
||||||
|
@ -9552,7 +9771,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"yargs-parser@npm:^21.1.1":
|
"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1":
|
||||||
version: 21.1.1
|
version: 21.1.1
|
||||||
resolution: "yargs-parser@npm:21.1.1"
|
resolution: "yargs-parser@npm:21.1.1"
|
||||||
checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2
|
checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2
|
||||||
|
@ -9574,6 +9793,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"yn@npm:3.1.1":
|
||||||
|
version: 3.1.1
|
||||||
|
resolution: "yn@npm:3.1.1"
|
||||||
|
checksum: 10c0/0732468dd7622ed8a274f640f191f3eaf1f39d5349a1b72836df484998d7d9807fbea094e2f5486d6b0cd2414aad5775972df0e68f8604db89a239f0f4bf7443
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"yocto-queue@npm:^0.1.0":
|
"yocto-queue@npm:^0.1.0":
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
resolution: "yocto-queue@npm:0.1.0"
|
resolution: "yocto-queue@npm:0.1.0"
|
||||||
|
|
Loading…
Reference in a new issue