diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs
index a4eb35bc..de7292b1 100644
--- a/frontend/.eslintrc.cjs
+++ b/frontend/.eslintrc.cjs
@@ -28,6 +28,11 @@ module.exports = {
rules: {
'react/prop-types': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
+ "@typescript-eslint/no-misused-promises": [2, {
+ "checksVoidReturn": {
+ "attributes": false
+ }
+ }]
},
overrides: [
{
diff --git a/frontend/src/layout/AppBar.jsx b/frontend/src/layout/AppBar.jsx
deleted file mode 100644
index 63977c35..00000000
--- a/frontend/src/layout/AppBar.jsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import React from 'react';
-import { AppBar as RaAppBar, Link } from 'react-admin';
-import { Zoom, Box, Typography } from '@mui/material';
-import makeStyles from '@mui/styles/makeStyles';
-import { UserMenu } from "@semapps/auth-provider";
-import SearchForm from './SearchForm';
-
-const useStyles = makeStyles(theme => ({
- appBar: {
- [theme.breakpoints.down('sm')]: {
- '& .MuiToolbar-root a.MuiLink-root': {
- marginRight: 'auto'
- }
- }
- },
- menuButton: {
- [theme.breakpoints.up('sm')]: {
- display: 'none'
- }
- },
- toolbar: {
- height: 56,
- [theme.breakpoints.up('sm')]: {
- paddingLeft: '24px'
- }
- },
- spacer: {
- flex: 1
- },
- searchFormContainer: {
- minWidth: 240,
- flex: 2,
- margin: '0 5%',
- [theme.breakpoints.up('md')]: {
- minWidth: 360,
- marginRight: 100
- }
- },
- searchFormWrapper: {
- maxWidth: 880,
- margin: 'auto'
- },
- presContainer: {
- flex: 1,
- overflow: 'hidden',
- [theme.breakpoints.up('sm')]: {
- flex: 'unset',
- display: 'flex',
- justifyContent: 'flex-start',
- alignItems: 'center'
- }
- },
- logoContainer: {
- display: 'none',
- [theme.breakpoints.up('sm')]: {
- height: 48,
- marginLeft: '0.2em',
- marginRight: '0.2em',
- display: 'block'
- }
- },
- logo: {
- height: '100%'
- },
- title: {
- display: 'block',
- color: theme.palette.primary.contrastText,
- [theme.breakpoints.up('sm')]: {
- display: 'none'
- },
- [theme.breakpoints.up('md')]: {
- display: 'block'
- }
- }
-}));
-
-const AppBar = props => {
- const classes = useStyles();
- return (
-
-
-
-
-
-
-
-
-
- {props.title}
-
-
-
-
-
-
-
- );
-};
-
-AppBar.defaultProps = {
- userMenu:
-};
-
-export default AppBar;
diff --git a/frontend/src/layout/AppBar.tsx b/frontend/src/layout/AppBar.tsx
new file mode 100644
index 00000000..30ebecfe
--- /dev/null
+++ b/frontend/src/layout/AppBar.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { AppBar as RaAppBar, Link, AppBarProps, Logout } from 'react-admin';
+import { Zoom, Box, Typography } from '@mui/material';
+import { UserMenu } from '@semapps/auth-provider';
+import SearchForm from './SearchForm';
+
+const AppBar = (props: AppBarProps) => {
+ return (
+ } />} color="primary">
+
+
+
+
+
+
+
+
+ {props.title}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AppBar;
diff --git a/frontend/src/layout/BaseView.jsx b/frontend/src/layout/BaseView.jsx
deleted file mode 100644
index 231cd625..00000000
--- a/frontend/src/layout/BaseView.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React from 'react';
-import { Grid, Card, Typography, Box } from '@mui/material';
-
-import makeStyles from '@mui/styles/makeStyles';
-
-const useStyles = makeStyles(theme => ({
- title: {
- paddingTop: 20,
- paddingBottom: 10,
- [theme.breakpoints.down('sm')]: {
- fontSize: '1.8rem',
- paddingTop: 0,
- paddingBottom: 6
- }
- },
- actions: {
- [theme.breakpoints.down('sm')]: {
- '& .MuiToolbar-root': {
- backgroundColor: theme.palette.background.default,
- minHeight: 0,
- paddingTop: 0
- },
- '& .MuiButtonBase-root': {
- padding: 0
- },
- order: -1
- }
- },
- card: {
- marginTop: 0,
- transition: theme.transitions.create('margin-top'),
- position: 'relative',
- flex: '1 1 auto',
- [theme.breakpoints.down('sm')]: {
- '& > .MuiBox-root, .MuiCardContent-root': {
- paddingRight: theme.spacing(1.5),
- paddingLeft: theme.spacing(1.5)
- }
- },
- }
-}));
-
-const BaseView = ({ title, actions, aside, context, children }) => {
- const classes = useStyles();
- return(
-
-
-
- {title ?? context.defaultTitle}
-
-
-
- {actions}
-
-
-
-
- {children}
-
- {aside}
-
-
-
- )
-};
-
-export default BaseView;
diff --git a/frontend/src/layout/BaseView.tsx b/frontend/src/layout/BaseView.tsx
new file mode 100644
index 00000000..76fbb401
--- /dev/null
+++ b/frontend/src/layout/BaseView.tsx
@@ -0,0 +1,51 @@
+import React, { PropsWithChildren, ReactNode } from 'react';
+import { Grid, Card, Typography } from '@mui/material';
+import { styled } from '@mui/material/styles';
+
+const Title = styled(Typography)(({ theme }) => ({
+ [theme.breakpoints.down('sm')]: {
+ fontSize: '1.8rem',
+ },
+})) as typeof Typography;
+
+const ActionsGrid = styled(Grid)(({ theme }) => ({
+ [theme.breakpoints.down('sm')]: {
+ '& .MuiToolbar-root': {
+ backgroundColor: theme.palette.background.default,
+ minHeight: 0,
+ paddingTop: 0,
+ alignItems: 'center',
+ height: '100%',
+ },
+ '& .MuiButtonBase-root': {
+ padding: 0,
+ },
+ },
+}));
+
+type Props = {
+ title: string | ReactNode;
+ actions: JSX.Element;
+ aside?: JSX.Element;
+};
+
+const BaseView = ({ title, actions, aside, children }: PropsWithChildren) => {
+ return (
+
+
+
+ {title}
+
+
+
+ {actions}
+
+
+ {children}
+ {aside}
+
+
+ );
+};
+
+export default BaseView;
diff --git a/frontend/src/layout/Layout.jsx b/frontend/src/layout/Layout.jsx
deleted file mode 100644
index f621654b..00000000
--- a/frontend/src/layout/Layout.jsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
-import React from 'react';
-import { Layout as RaLayout } from 'react-admin';
-import makeStyles from '@mui/styles/makeStyles';
-import AppBar from './AppBar';
-import TreeMenu from './TreeMenu/TreeMenu';
-
-const useStyles = makeStyles(theme => ({
- layout: {
- '& .RaLayout-content': {
- paddingTop: theme.spacing(1),
- paddingRight: theme.spacing(2),
- paddingBottom: theme.spacing(2),
- [theme.breakpoints.down('sm')]: {
- padding: theme.spacing(1),
- },
- '& a:not(.MuiListItemButton-root):not(.MuiButtonBase-root)': {
- overflowWrap: 'break-word',
- color: theme.palette.primary.main
- }
- }
- }
-}));
-
-const Layout = ({ appBar, menu, children, ...otherProps }) => {
- const classes = useStyles();
- return (
-
- {children}
-
- );
-};
-
-export default Layout;
diff --git a/frontend/src/layout/Layout.tsx b/frontend/src/layout/Layout.tsx
new file mode 100644
index 00000000..e4ccbbca
--- /dev/null
+++ b/frontend/src/layout/Layout.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { LayoutProps, Layout as RaLayout } from 'react-admin';
+import { useTheme } from '@mui/material/styles';
+import AppBar from './AppBar';
+import TreeMenu from './TreeMenu/TreeMenu';
+
+const Layout = ({ children, ...otherProps }: LayoutProps) => {
+ const theme = useTheme();
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default Layout;
diff --git a/frontend/src/layout/SearchForm.jsx b/frontend/src/layout/SearchForm.jsx
deleted file mode 100644
index 76551a23..00000000
--- a/frontend/src/layout/SearchForm.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import React, { useEffect, useMemo } from 'react';
-import { useResourceDefinitions } from 'react-admin';
-import { Grid, Select, MenuItem, TextField, Button } from '@mui/material';
-import { alpha } from '@mui/material/styles';
-import { useForm, Controller } from 'react-hook-form';
-import { useNavigate, useLocation } from 'react-router-dom';
-import makeStyles from '@mui/styles/makeStyles';
-import SearchIcon from '@mui/icons-material/Search';
-
-const useStyles = makeStyles(theme => ({
- button: {
- color: theme.palette.primary.contrastText,
- borderColor: alpha(theme.palette.common.black, 0.42),
- '& .MuiButton-startIcon': {
- [theme.breakpoints.down('md')]: {
- margin: 0
- }
- },
- '&:hover': {
- borderColor: alpha(theme.palette.common.black, 0.8),
- }
- },
- buttonLabel: {
- [theme.breakpoints.down('md')]: {
- display: 'none'
- }
- },
- field: {
- color: theme.palette.primary.contrastText
- }
-}));
-
-const TypeSelect = (props) => {
- const resourceDefinitions = useResourceDefinitions();
- const resources = useMemo(() => Object.values(resourceDefinitions), [resourceDefinitions]);
- if (resources.length === 0) return null;
- return (
-
- );
-};
-
-const SearchForm = () => {
- const classes = useStyles();
- const navigate = useNavigate();
-
- const location = useLocation();
- const matches = location.pathname.match(/^\/([^/]+)/);
- const type = matches ? matches[1] : 'Organization';
-
- let search = new URLSearchParams(location.search);
- const filter = (search && JSON.parse(search.get('filter'))) || {};
-
- const { register, setValue, control, handleSubmit } = useForm({
- defaultValues: {
- type,
- filter: filter.q
- }
- });
-
- // Reinitialize the form on page change
- useEffect(() => {
- setValue('type', type);
- setValue('filter', filter.q);
- }, [location.pathname, type, filter.q, setValue]);
-
- const onSubmit = ({ filter, type }) => {
- if (filter) {
- navigate(`/${type}?filter=${encodeURIComponent(`{"q": "${filter}"}`)}`);
- } else {
- navigate(`/${type}?filter=${encodeURIComponent(`{}`)}`);
- }
- };
-
- return (
-
- );
-};
-
-export default SearchForm;
diff --git a/frontend/src/layout/SearchForm.tsx b/frontend/src/layout/SearchForm.tsx
new file mode 100644
index 00000000..e0abdba4
--- /dev/null
+++ b/frontend/src/layout/SearchForm.tsx
@@ -0,0 +1,119 @@
+import React, { useEffect, useMemo } from 'react';
+import { useResourceDefinitions } from 'react-admin';
+import { Select, MenuItem, TextField, Button, Box, Stack, IconButton, SelectProps } from '@mui/material';
+import { alpha } from '@mui/material/styles';
+import { useForm, Controller, SubmitHandler } from 'react-hook-form';
+import { useNavigate, useLocation } from 'react-router-dom';
+import SearchIcon from '@mui/icons-material/Search';
+import { ResourceOptions } from './TreeMenu/TreeMenu';
+
+type Fields = {
+ filter: string;
+ type: string;
+};
+
+const TypeSelect = (props: SelectProps) => {
+ const resourceDefinitions = useResourceDefinitions();
+ const resources = useMemo(() => Object.values(resourceDefinitions), [resourceDefinitions]);
+
+ if (resources.length === 0) return null;
+
+ return (
+
+ );
+};
+
+const SearchForm = () => {
+ const navigate = useNavigate();
+
+ const location = useLocation();
+ const matches = location.pathname.match(/^\/([^/]+)/);
+ const type = matches ? matches[1] : 'Organization';
+
+ const search = new URLSearchParams(location.search);
+ const filter = (search && (JSON.parse(search.get('filter') || '{}') as { q?: string })) || {};
+
+ const { register, setValue, control, handleSubmit } = useForm({
+ defaultValues: {
+ type,
+ filter: filter.q,
+ },
+ });
+
+ // Reinitialize the form on page change
+ useEffect(() => {
+ setValue('type', type);
+ setValue('filter', filter.q || '');
+ }, [location.pathname, type, filter.q, setValue]);
+
+ const onSubmit: SubmitHandler = ({ filter, type }) => {
+ if (filter) {
+ navigate(`/${type}?filter=${encodeURIComponent(`{"q": "${filter}"}`)}`);
+ } else {
+ navigate(`/${type}?filter=${encodeURIComponent(`{}`)}`);
+ }
+ };
+
+ return (
+
+
+
+ (
+
+ )}
+ />
+ }
+ sx={{
+ display: { xs: 'none', md: 'inline-flex' },
+ color: 'primary.contrastText',
+ borderColor: alpha('#000', 0.42),
+ '&:hover': {
+ borderColor: alpha('#000', 0.8),
+ },
+ }}
+ >
+ Rechercher
+
+
+
+
+
+
+ );
+};
+
+export default SearchForm;
diff --git a/frontend/src/layout/TreeMenu/ResourceMenuLink.jsx b/frontend/src/layout/TreeMenu/ResourceMenuLink.jsx
deleted file mode 100644
index 8b870536..00000000
--- a/frontend/src/layout/TreeMenu/ResourceMenuLink.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import { MenuItemLink } from 'react-admin';
-import DefaultIcon from '@mui/icons-material/ViewList';
-
-const ResourceMenuLink = ({ resource }) => (
- : }
- />
-);
-
-export default ResourceMenuLink;
diff --git a/frontend/src/layout/TreeMenu/ResourceMenuLink.tsx b/frontend/src/layout/TreeMenu/ResourceMenuLink.tsx
new file mode 100644
index 00000000..63283ff3
--- /dev/null
+++ b/frontend/src/layout/TreeMenu/ResourceMenuLink.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { MenuItemLink, useSidebarState } from 'react-admin';
+import DefaultIcon from '@mui/icons-material/ViewList';
+
+type Props = {
+ resource: {
+ name: string;
+ icon?: React.ElementType;
+ options?: {
+ label: string;
+ };
+ };
+ root?: boolean;
+};
+
+const ResourceMenuLink = ({ resource, root }: Props) => {
+ const [sidebarIsOpen] = useSidebarState();
+
+ return (
+ // @ts-expect-error Bad typing from react-admin
+ : }
+ sx={{
+ paddingLeft: root || !sidebarIsOpen ? 2 : 4,
+ transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms',
+ }}
+ />
+ );
+};
+
+export default ResourceMenuLink;
diff --git a/frontend/src/layout/TreeMenu/SubMenu.jsx b/frontend/src/layout/TreeMenu/SubMenu.jsx
deleted file mode 100644
index 2d219439..00000000
--- a/frontend/src/layout/TreeMenu/SubMenu.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react';
-import { MenuItemLink, useSidebarState } from 'react-admin';
-import { MenuList, Collapse, Tooltip } from '@mui/material';
-import makeStyles from '@mui/styles/makeStyles';
-import ExpandMore from '@mui/icons-material/ExpandMore';
-
-const useStyles = makeStyles(theme => ({
- sidebarIsOpen: {
- '& a': {
- paddingLeft: theme.spacing(4),
- transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms'
- }
- },
- sidebarIsClosed: {
- '& a': {
- paddingLeft: theme.spacing(2),
- transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms'
- }
- }
-}));
-
-const SubMenu = ({ handleToggle, isOpen, name, icon, children }) => {
- const classes = useStyles();
- const [sidebarIsOpen, setSidebarIsOpen] = useSidebarState();
-
- const header = (
- : icon}
- onClick={(e) => {
- e.preventDefault();
- setSidebarIsOpen(true);
- handleToggle();
- }}
- />
- );
-
- return (
- <>
- {sidebarIsOpen || isOpen ? (
- header
- ) : (
-
- {header}
-
- )}
-
-
- {children}
-
-
- >
- );
-};
-
-export default SubMenu;
diff --git a/frontend/src/layout/TreeMenu/SubMenu.tsx b/frontend/src/layout/TreeMenu/SubMenu.tsx
new file mode 100644
index 00000000..dc912904
--- /dev/null
+++ b/frontend/src/layout/TreeMenu/SubMenu.tsx
@@ -0,0 +1,45 @@
+import React, { PropsWithChildren } from 'react';
+import { MenuItemLink, useSidebarState } from 'react-admin';
+import { MenuList, Collapse, Tooltip } from '@mui/material';
+import ExpandMore from '@mui/icons-material/ExpandMore';
+
+type Props = {
+ handleToggle: () => void;
+ isOpen: boolean;
+ name: string;
+ icon: JSX.Element;
+};
+
+const SubMenu = ({ handleToggle, isOpen, name, icon, children }: PropsWithChildren) => {
+ const [sidebarIsOpen, setSidebarIsOpen] = useSidebarState();
+
+ const header = (
+ // @ts-expect-error Bad typing from react-admin
+ : icon}
+ onClick={(e) => {
+ e.preventDefault();
+ setSidebarIsOpen(true);
+ handleToggle();
+ }}
+ sx={{ paddingLeft: 2 }}
+ />
+ );
+
+ return (
+ <>
+
+ {header}
+
+
+
+ {children}
+
+
+ >
+ );
+};
+
+export default SubMenu;
diff --git a/frontend/src/layout/TreeMenu/TreeMenu.jsx b/frontend/src/layout/TreeMenu/TreeMenu.tsx
similarity index 56%
rename from frontend/src/layout/TreeMenu/TreeMenu.jsx
rename to frontend/src/layout/TreeMenu/TreeMenu.tsx
index 97799b69..f3537d74 100644
--- a/frontend/src/layout/TreeMenu/TreeMenu.jsx
+++ b/frontend/src/layout/TreeMenu/TreeMenu.tsx
@@ -1,22 +1,26 @@
-/* eslint-disable react/no-children-prop */
-import React, { useState, useEffect, useMemo } from "react";
-import { useResourceDefinitions, Logout, Menu, useGetIdentity, MenuItemLink, useTranslate } from "react-admin";
-import { useLocation } from "react-router";
-import { useMediaQuery, Divider } from "@mui/material";
-import DefaultIcon from "@mui/icons-material/ViewList";
+import React, { useState, useEffect, useMemo } from 'react';
+import { useResourceDefinitions, Logout, Menu, useGetIdentity, MenuItemLink, useTranslate } from 'react-admin';
+import { useLocation } from 'react-router';
+import { useMediaQuery, Divider } from '@mui/material';
+import { useTheme } from '@mui/material/styles';
+import DefaultIcon from '@mui/icons-material/ViewList';
import LockOpenIcon from '@mui/icons-material/LockOpen';
import LoginIcon from '@mui/icons-material/Login';
-import SubMenu from "./SubMenu";
-import ResourceMenuLink from "./ResourceMenuLink";
+import SubMenu from './SubMenu';
+import ResourceMenuLink from './ResourceMenuLink';
+import resources from '../../resources';
+
+export type ResourceOptions = {
+ label: string;
+ parent?: keyof typeof resources;
+}
const TreeMenu = () => {
- const isXSmall = useMediaQuery((theme) => theme.breakpoints.down("sm"));
+ const theme = useTheme();
+ const isXSmall = useMediaQuery(theme.breakpoints.down('sm'));
- const resourceDefinitions = useResourceDefinitions();
- const resources = useMemo(
- () => Object.values(resourceDefinitions),
- [resourceDefinitions]
- );
+ const resourceDefinitions = useResourceDefinitions();
+ const resources = useMemo(() => Object.values(resourceDefinitions), [resourceDefinitions]);
const location = useLocation();
const matches = location.pathname.match(/^\/([^/]+)/);
@@ -27,20 +31,17 @@ const TreeMenu = () => {
const translate = useTranslate();
- const [openSubMenus, setOpenSubMenus] = useState({});
- const handleToggle = (menu) => {
+ const [openSubMenus, setOpenSubMenus] = useState>({});
+ const handleToggle = (menu: string) => {
setOpenSubMenus((state) => ({ ...state, [menu]: !state[menu] }));
};
// Get menu root items
- const menuRootItems = useMemo(
- () => resources.filter((r) => !r.options?.parent),
- [resources]
- );
+ const menuRootItems = useMemo(() => resources.filter((r) => !r.options?.parent), [resources]);
// Calculate available categories
const categories = useMemo(() => {
- const names = resources.reduce((categories, resource) => {
+ const names = resources.reduce((categories, resource) => {
if (resource.options?.parent) {
categories.push(resource.options.parent);
}
@@ -51,15 +52,10 @@ const TreeMenu = () => {
// Open submenu of current page
useEffect(() => {
- const currentResource = resources.find(
- (resource) => resource.name === currentResourceName
- );
+ const currentResource = resources.find((resource) => resource.name === currentResourceName);
const currentCategory =
- currentResource &&
- categories.find(
- (category) => category.name === currentResource.options?.parent
- );
+ currentResource && categories.find((category) => category.name === currentResource.options?.parent);
if (currentCategory) {
setOpenSubMenus((state) => ({ ...state, [currentCategory.name]: true }));
@@ -76,15 +72,13 @@ const TreeMenu = () => {
icon={menuRootItem.icon ? : }
>
{resources
- .filter(resource => resource.hasList && resource.options.parent === menuRootItem.name)
- .map(resource => (
+ .filter((resource) => resource.hasList && resource.options?.parent === menuRootItem.name)
+ .map((resource) => (
))}
) : (
- menuRootItem.hasList && (
-
- )
+ menuRootItem.hasList &&
);
});
@@ -92,15 +86,17 @@ const TreeMenu = () => {
menuItems.push();
if (isLogged) {
- menuItems.push()
+ menuItems.push();
} else {
menuItems.push(
+ // @ts-expect-error Bad typing from react-admin
}
/>,
+ // @ts-expect-error Bad typing from react-admin
{
}
}
- return ;
+ return ;
};
export default TreeMenu;
diff --git a/frontend/src/layout/create/Create.jsx b/frontend/src/layout/create/Create.jsx
deleted file mode 100644
index 4aeaadad..00000000
--- a/frontend/src/layout/create/Create.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import { CreateActions, CreateBase } from 'react-admin';
-import CreateView from "./CreateView";
-
-const Create = ({ title, actions, children, ...rest }) => (
-
-
- {children}
-
-
-);
-
-Create.defaultProps = {
- actions:
-};
-
-export default Create;
diff --git a/frontend/src/layout/create/Create.tsx b/frontend/src/layout/create/Create.tsx
new file mode 100644
index 00000000..efdfa55a
--- /dev/null
+++ b/frontend/src/layout/create/Create.tsx
@@ -0,0 +1,13 @@
+import React, { PropsWithChildren } from 'react';
+import { CreateActions, CreateBase, CreateProps } from 'react-admin';
+import CreateView from "./CreateView";
+
+const Create = ({ title, actions, children, ...rest }: PropsWithChildren) => (
+
+ } title={title}>
+ {children}
+
+
+);
+
+export default Create;
diff --git a/frontend/src/layout/create/CreateView.jsx b/frontend/src/layout/create/CreateView.jsx
deleted file mode 100644
index 7ed774c0..00000000
--- a/frontend/src/layout/create/CreateView.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import { useCreateContext } from 'react-admin';
-import { useCheckPermissions } from '@semapps/auth-provider';
-import { useCreateContainer } from '@semapps/semantic-data-provider';
-import BaseView from "../BaseView";
-
-const CreateView = ({ title, actions, children }) => {
- const createContext = useCreateContext({ defaultTitle: title });
- const createContainerUri = useCreateContainer(createContext.resource);
- useCheckPermissions(createContainerUri, 'create');
- return(
-
- {children}
-
- )
-};
-
-export default CreateView;
diff --git a/frontend/src/layout/create/CreateView.tsx b/frontend/src/layout/create/CreateView.tsx
new file mode 100644
index 00000000..da7d5187
--- /dev/null
+++ b/frontend/src/layout/create/CreateView.tsx
@@ -0,0 +1,26 @@
+import React, { PropsWithChildren, ReactElement } from 'react';
+import { useCreateContext } from 'react-admin';
+import { useCheckPermissions } from '@semapps/auth-provider';
+import { useCreateContainerUri } from '@semapps/semantic-data-provider';
+import BaseView from "../BaseView";
+
+type Props = {
+ title?: string | ReactElement;
+ actions: JSX.Element;
+};
+
+const CreateView = ({ title, actions, children }: PropsWithChildren) => {
+ const createContext = useCreateContext();
+ const createContainerUri = useCreateContainerUri()(createContext.resource);
+
+ // @ts-expect-error Bad typing of Semapps
+ useCheckPermissions(createContainerUri || {}, 'create');
+
+ return(
+
+ {children}
+
+ )
+};
+
+export default CreateView;
diff --git a/frontend/src/layout/edit/Edit.jsx b/frontend/src/layout/edit/Edit.jsx
deleted file mode 100644
index 863da9fd..00000000
--- a/frontend/src/layout/edit/Edit.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import { EditBase } from 'react-admin';
-import { EditActionsWithPermissions } from "@semapps/auth-provider";
-import EditView from "./EditView";
-
-const Edit = ({ title, actions, children, ...rest }) => (
-
-
- {children}
-
-
-);
-
-Edit.defaultProps = {
- actions:
-};
-
-export default Edit;
diff --git a/frontend/src/layout/edit/Edit.tsx b/frontend/src/layout/edit/Edit.tsx
new file mode 100644
index 00000000..a37bf9b4
--- /dev/null
+++ b/frontend/src/layout/edit/Edit.tsx
@@ -0,0 +1,14 @@
+import React, { PropsWithChildren } from 'react';
+import { EditBase, EditProps } from 'react-admin';
+import { EditActionsWithPermissions } from "@semapps/auth-provider";
+import EditView from "./EditView";
+
+const Edit = ({ title, actions, children, ...rest }: PropsWithChildren) => (
+
+ }>
+ {children}
+
+
+);
+
+export default Edit;
diff --git a/frontend/src/layout/edit/EditView.jsx b/frontend/src/layout/edit/EditView.jsx
deleted file mode 100644
index d78dafec..00000000
--- a/frontend/src/layout/edit/EditView.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import { useEditContext, useGetRecordRepresentation, useResourceContext } from 'react-admin';
-import { useCheckPermissions } from '@semapps/auth-provider';
-import { EditToolbarWithPermissions } from "@semapps/auth-provider";
-import BaseView from "../BaseView";
-
-const EditView = ({ title, actions, children }) => {
- const editContext = useEditContext();
- useCheckPermissions(editContext?.record?.id, 'edit');
-
- const resource = useResourceContext();
- const getRecordRepresentation = useGetRecordRepresentation(resource);
-
- const recordTitle = getRecordRepresentation(editContext?.record);
-
- return(
-
- {React.cloneElement(children, {
- toolbar:
- })}
-
- )
-};
-
-export default EditView;
diff --git a/frontend/src/layout/edit/EditView.tsx b/frontend/src/layout/edit/EditView.tsx
new file mode 100644
index 00000000..8acc843e
--- /dev/null
+++ b/frontend/src/layout/edit/EditView.tsx
@@ -0,0 +1,32 @@
+import React, { PropsWithChildren, ReactElement } from 'react';
+import { RaRecord, useEditContext, useGetRecordRepresentation, useResourceContext } from 'react-admin';
+import { useCheckPermissions } from '@semapps/auth-provider';
+import { EditToolbarWithPermissions } from '@semapps/auth-provider';
+import BaseView from '../BaseView';
+
+type Props = {
+ title?: string | ReactElement;
+ actions: JSX.Element;
+};
+
+const EditView = ({ title, actions, children }: PropsWithChildren) => {
+ const editContext = useEditContext>();
+
+ // @ts-expect-error Bad typing of Semapps
+ useCheckPermissions(editContext?.record?.id || {}, 'edit');
+
+ const resource = useResourceContext();
+ const getRecordRepresentation = useGetRecordRepresentation(resource);
+
+ const recordTitle = getRecordRepresentation(editContext?.record);
+
+ return (
+
+ {React.cloneElement(children as ReactElement, {
+ toolbar:
+ })}
+
+ );
+};
+
+export default EditView;
diff --git a/frontend/src/layout/list/List.jsx b/frontend/src/layout/list/List.jsx
deleted file mode 100644
index e7a3cb9c..00000000
--- a/frontend/src/layout/list/List.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
-import React from 'react';
-import { ListBase } from 'react-admin';
-import ListView from "./ListView";
-import ListActionsWithViewsAndPermissions from "./ListActionsWithViewsAndPermissions";
-
-const List = ({ actions, aside, pagination, children, perPage, ...rest }) => (
-
- } pagination={pagination}>
- {children}
-
-
- );
-
-export default List;
diff --git a/frontend/src/layout/list/List.tsx b/frontend/src/layout/list/List.tsx
new file mode 100644
index 00000000..56a6e5c3
--- /dev/null
+++ b/frontend/src/layout/list/List.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { ListBase, ListProps, TopToolbar } from 'react-admin';
+import ListView from './ListView';
+import { ListActionsWithPermissions } from '@semapps/auth-provider';
+import { ViewsButtons } from '@semapps/list-components';
+import { Box } from '@mui/material';
+import { styled } from '@mui/material/styles';
+
+const ActionsBox = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'flex-end',
+ [theme.breakpoints.down('md')]: {
+ flexDirection: 'row-reverse',
+ },
+}));
+
+const List = ({ aside, pagination, children, ...rest }: ListProps) => (
+
+
+
+
+
+
+
+ }
+ pagination={pagination}
+ >
+ {children}
+
+
+);
+
+export default List;
diff --git a/frontend/src/layout/list/ListActionsWithViewsAndPermissions.jsx b/frontend/src/layout/list/ListActionsWithViewsAndPermissions.jsx
deleted file mode 100644
index 6b5d2c88..00000000
--- a/frontend/src/layout/list/ListActionsWithViewsAndPermissions.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react';
-import { CreateButton, ExportButton, useResourceDefinition, TopToolbar, usePermissions, useResourceContext } from 'react-admin';
-import { useMediaQuery } from '@mui/material';
-import { useCreateContainer } from "@semapps/semantic-data-provider";
-import { ViewsButtons } from "@semapps/list-components";
-import { PermissionsButton } from "@semapps/auth-provider";
-
-// Custom ListActions which include the PermissionButton and ViewsButtons
-const ListActionsWithViewsAndPermissions = ({
- bulkActions,
- sort,
- displayedFilters,
- exporter,
- filters,
- filterValues,
- onUnselectItems,
- selectedIds,
- showFilter,
- total,
- ...rest
-}) => {
- const resource = useResourceContext();
- const xs = useMediaQuery(theme => theme.breakpoints.down('sm'));
- const resourceDefinition = useResourceDefinition(rest);
- const createContainerUri = useCreateContainer(resource);
- const { permissions } = usePermissions(createContainerUri);
- return (
-
-
- {filters &&
- React.cloneElement(filters, {
- showFilter,
- displayedFilters,
- filterValues,
- context: 'button'
- })}
- {resourceDefinition.hasCreate && permissions && permissions.some(p => ['acl:Append', 'acl:Write', 'acl:Control'].includes(p['acl:mode'])) &&
- }
- {permissions && permissions.some(p => ['acl:Control'].includes(p['acl:mode'])) && (
-
- )}
- {!xs && exporter !== false && (
-
- )}
- {bulkActions &&
- React.cloneElement(bulkActions, {
- filterValues,
- selectedIds,
- onUnselectItems
- })}
-
- );
-};
-
-export default ListActionsWithViewsAndPermissions;
diff --git a/frontend/src/layout/list/ListView.jsx b/frontend/src/layout/list/ListView.jsx
deleted file mode 100644
index 3742ea09..00000000
--- a/frontend/src/layout/list/ListView.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import { useListContext, Pagination } from 'react-admin';
-import { Box } from '@mui/material';
-import BaseView from "../BaseView";
-
-const ListView = ({ title, children, aside, actions, pagination }) => {
- const listContext = useListContext();
- return(
-
-
- {children}
-
- {pagination === false ? null : pagination}
-
- )
-};
-
-ListView.defaultProps = {
- pagination:
-};
-
-export default ListView;
diff --git a/frontend/src/layout/list/ListView.tsx b/frontend/src/layout/list/ListView.tsx
new file mode 100644
index 00000000..b1c9dfae
--- /dev/null
+++ b/frontend/src/layout/list/ListView.tsx
@@ -0,0 +1,30 @@
+import React, { PropsWithChildren, ReactElement } from 'react';
+import { useListContext, Pagination } from 'react-admin';
+import { Box } from '@mui/material';
+import BaseView from '../BaseView';
+import { useCheckPermissions } from '@semapps/auth-provider';
+import { useCreateContainerUri } from '@semapps/semantic-data-provider';
+
+type Props = {
+ title?: string | ReactElement;
+ actions: JSX.Element;
+ aside?: JSX.Element;
+ pagination?: JSX.Element | boolean;
+};
+
+const ListView = ({ title, children, aside, actions, pagination }: PropsWithChildren) => {
+ const listContext = useListContext();
+ const createContainerUri = useCreateContainerUri()(listContext.resource);
+
+ // @ts-expect-error Bad typing of Semapps
+ useCheckPermissions(createContainerUri || {}, 'list');
+
+ return (
+
+ {children}
+ {pagination === false ? null : pagination || }
+
+ );
+};
+
+export default ListView;
diff --git a/frontend/src/layout/show/Show.jsx b/frontend/src/layout/show/Show.jsx
deleted file mode 100644
index 0f031710..00000000
--- a/frontend/src/layout/show/Show.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import { ShowBase } from 'react-admin';
-import { ShowActionsWithPermissions } from "@semapps/auth-provider";
-import ShowView from "./ShowView";
-
-const Show = ({ title, actions, children, ...rest }) => (
-
-
- {children}
-
-
-);
-
-Show.defaultProps = {
- actions:
-};
-
-export default Show;
diff --git a/frontend/src/layout/show/Show.tsx b/frontend/src/layout/show/Show.tsx
new file mode 100644
index 00000000..045393c2
--- /dev/null
+++ b/frontend/src/layout/show/Show.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { ShowBase, ShowProps } from 'react-admin';
+import { ShowActionsWithPermissions } from '@semapps/auth-provider';
+import ShowView from './ShowView';
+
+const Show = ({ title, actions, children, ...rest }: ShowProps) => (
+
+ }>
+ {children}
+
+
+);
+
+export default Show;
diff --git a/frontend/src/layout/show/ShowView.jsx b/frontend/src/layout/show/ShowView.jsx
deleted file mode 100644
index ef8639f8..00000000
--- a/frontend/src/layout/show/ShowView.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import { useGetRecordRepresentation, useResourceContext, useShowContext } from 'react-admin';
-import { Box } from '@mui/material';
-import { useCheckPermissions } from '@semapps/auth-provider';
-import BaseView from "../BaseView";
-
-const ShowView = ({ title, actions, children }) => {
- const showContext = useShowContext();
- useCheckPermissions(showContext?.record?.id, 'show');
-
- const resource = useResourceContext();
- const getRecordRepresentation = useGetRecordRepresentation(resource);
-
- const recordTitle = getRecordRepresentation(showContext?.record);
-
- return(
-
-
- {children}
-
-
- )
-};
-
-export default ShowView;
diff --git a/frontend/src/layout/show/ShowView.tsx b/frontend/src/layout/show/ShowView.tsx
new file mode 100644
index 00000000..f9127ff4
--- /dev/null
+++ b/frontend/src/layout/show/ShowView.tsx
@@ -0,0 +1,30 @@
+import React, { PropsWithChildren, ReactElement } from 'react';
+import { RaRecord, useGetRecordRepresentation, useResourceContext, useShowContext } from 'react-admin';
+import { Box } from '@mui/material';
+import { useCheckPermissions } from '@semapps/auth-provider';
+import BaseView from '../BaseView';
+
+type Props = {
+ title?: string | ReactElement;
+ actions: JSX.Element;
+};
+
+const ShowView = ({ title, actions, children }: PropsWithChildren) => {
+ const showContext = useShowContext>();
+
+ // @ts-expect-error Bad typing of Semapps
+ useCheckPermissions(showContext?.record?.id || {}, 'show');
+
+ const resource = useResourceContext();
+ const getRecordRepresentation = useGetRecordRepresentation(resource);
+
+ const recordTitle = getRecordRepresentation(showContext?.record);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default ShowView;