Compare commits

...

3 Commits

Author SHA1 Message Date
012db596e8 add list and show to the Person entity 2025-01-13 21:51:17 +01:00
fdb8284b51 change api to the gateway - add some pkgs - and add eslint 2025-01-13 21:50:39 +01:00
000f46d583 disable CORS on nodejs server 2025-01-13 21:49:48 +01:00
21 changed files with 469 additions and 76 deletions

2
.env
View File

@@ -1 +1 @@
VITE_SIMPLE_REST_URL=http://localhost:8083/core/api/admin
VITE_SIMPLE_REST_URL=http://localhost:8081/core/api/admin

31
eslint.config.mjs Normal file
View File

@@ -0,0 +1,31 @@
import globals from 'globals';
import pluginJs from '@eslint/js';
import pluginReact from 'eslint-plugin-react';
export default [
{
files: ['**/*.{js,mjs,cjs,jsx}'],
rules: {
semi: 0,
'no-unused-vars': 1,
'comma-dangle': [1, {
arrays: 'only-multiline',
objects: 'only-multiline',
imports: 'only-multiline',
exports: 'only-multiline',
functions: 'only-multiline',
}],
yoda: ['error', 'always'],
'operator-linebreak': ['error', 'before'],
indent: ['error', 2, { SwitchCase: 1 }],
'space-before-function-paren': ['error', {
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
}],
},
},
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
pluginReact.configs.flat.recommended,
];

View File

@@ -15,6 +15,8 @@
"@mui/icons-material": "^5.16.14",
"@mui/material": "^5.16.14",
"@tanstack/react-query": "^5.63.0",
"axios": "^1.7.9",
"lodash": "^4.17.21",
"ra-data-simple-rest": "^5.4.0",
"react": "^18.3.0",
"react-admin": "^5.4.0",

76
pnpm-lock.yaml generated
View File

@@ -23,6 +23,12 @@ importers:
'@tanstack/react-query':
specifier: ^5.63.0
version: 5.63.0(react@18.3.1)
axios:
specifier: ^1.7.9
version: 1.7.9
lodash:
specifier: ^4.17.21
version: 4.17.21
ra-data-simple-rest:
specifier: ^5.4.0
version: 5.4.3(ra-core@5.4.3(react-dom@18.3.1(react@18.3.1))(react-hook-form@7.54.2(react@18.3.1))(react-router-dom@6.28.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@6.28.1(react@18.3.1))(react@18.3.1))
@@ -792,6 +798,9 @@ packages:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
attr-accept@2.2.5:
resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==}
engines: {node: '>=4'}
@@ -803,6 +812,9 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
axios@1.7.9:
resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
babel-plugin-macros@3.1.0:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
@@ -856,6 +868,10 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -918,6 +934,10 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@@ -1099,9 +1119,22 @@ packages:
flatted@3.3.2:
resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
form-data@4.0.1:
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
engines: {node: '>= 6'}
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@@ -1417,6 +1450,14 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -1542,6 +1583,9 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -2673,6 +2717,8 @@ snapshots:
get-intrinsic: 1.2.7
is-array-buffer: 3.0.5
asynckit@0.4.0: {}
attr-accept@2.2.5: {}
autosuggest-highlight@3.3.4:
@@ -2683,6 +2729,14 @@ snapshots:
dependencies:
possible-typed-array-names: 1.0.0
axios@1.7.9:
dependencies:
follow-redirects: 1.15.9
form-data: 4.0.1
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
babel-plugin-macros@3.1.0:
dependencies:
'@babel/runtime': 7.26.0
@@ -2741,6 +2795,10 @@ snapshots:
color-name@1.1.4: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
concat-map@0.0.1: {}
convert-source-map@1.9.0: {}
@@ -2805,6 +2863,8 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
delayed-stream@1.0.0: {}
dir-glob@3.0.1:
dependencies:
path-type: 4.0.0
@@ -3118,10 +3178,18 @@ snapshots:
flatted@3.3.2: {}
follow-redirects@1.15.9: {}
for-each@0.3.3:
dependencies:
is-callable: 1.2.7
form-data@4.0.1:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
fs.realpath@1.0.0: {}
fsevents@2.3.3:
@@ -3438,6 +3506,12 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
@@ -3563,6 +3637,8 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
proxy-from-env@1.1.0: {}
punycode@2.3.1: {}
query-string@7.1.3:

View File

@@ -1,14 +1,7 @@
import {
Admin,
EditGuesser,
ListGuesser,
Resource,
ShowGuesser,
} from "react-admin";
import { Admin, Resource } from "react-admin";
import { Layout } from "./Layout";
import { dataProvider } from "./dataProvider";
import { authProvider } from "./authProvider";
import { AdminDashboard } from '@admin';
import { AdminDashboard, PersonList, PersonShow } from "@admin";
import { authProvider, dataProvider } from "@core";
export const App = () => (
<Admin
@@ -19,9 +12,9 @@ export const App = () => (
>
<Resource
name="person"
list={ListGuesser}
edit={EditGuesser}
show={ShowGuesser}
options={{ label: "Users" }}
list={PersonList}
show={PersonShow}
/>
</Admin>
);

View File

@@ -1,20 +1,18 @@
import { Card, CardContent } from "@mui/material";
import { Title, useDataProvider } from "react-admin";
import { useQuery } from "@tanstack/react-query";
import { Card, CardContent, Typography } from "@mui/material";
import { Title } from "react-admin";
export const AdminDashboard = () => {
const dataProvider = useDataProvider();
const { data } = useQuery({
queryKey: ["dashboard"],
queryFn: () => dataProvider.person(),
});
console.log(data);
return (
<Card>
<Card sx={{ mt: 5 }}>
<Title title="Welcome to the administration" />
<CardContent>Lorem ipsum sic dolor amet...</CardContent>
<CardContent>
<Typography>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet
impedit, temporibus? Amet dolor fuga hic libero molestiae nemo
perferendis quas quis repellendus voluptas? Aliquid doloremque, est
labore odit optio similique.
</Typography>
</CardContent>
</Card>
);
};

View File

@@ -3,5 +3,6 @@ import { Menu } from "react-admin";
export const AdminMenu = () => (
<Menu>
<Menu.DashboardItem />
<Menu.ResourceItem name="person" />
</Menu>
);

View File

@@ -1,2 +1,3 @@
export { AdminMenu } from './admin-menu';
export { AdminDashboard } from './admin-dashboard';
export * from './resources';

View File

@@ -0,0 +1 @@
export * from './person';

View File

@@ -0,0 +1,31 @@
import React, { FC } from 'react';
import { isObject } from "lodash";
import { useRecordContext } from 'react-admin';
import { Chip, Stack } from '@mui/material';
interface FieldProps {
source: string;
}
export const ArrayField: FC<FieldProps> = ({ source }) => {
const record = useRecordContext();
const objects = record?.[source];
if (!objects) return "";
return (
<Stack direction="row" gap={1}>
{objects.map((currentObject: string, key: number) => {
if (isObject(currentObject)) {
return (
<Chip key={`${key}-people`} size="small" label={currentObject.name} />
);
}
return (
<Chip key={`${key}-people`} size="small" label={currentObject} />
);
})}
</Stack>
);
};

View File

@@ -0,0 +1 @@
export { ArrayField } from "./array-field.tsx";

View File

@@ -0,0 +1,3 @@
export { PersonList } from './list.tsx';
export { PersonShow } from './show.tsx';
export * from './components';

View File

@@ -0,0 +1,103 @@
import React, { Fragment, useEffect, useState } from "react";
import {
BooleanField,
Datagrid,
isEmpty,
ListContextProvider,
ListToolbar,
Pagination,
TextField,
Title,
TopToolbar,
useDataProvider,
WrapperField,
} from "react-admin";
import { useQuery } from "@tanstack/react-query";
import { ArrayField } from "./components";
import { TextField as MuiTextField } from "@mui/material";
export const PersonList = () => {
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(25);
const [firstName, setFirstName] = useState("");
const dataProvider = useDataProvider();
const { data, isPending, refetch } = useQuery({
queryKey: ["personList", page, perPage, firstName],
queryFn: () => {
const httpParams = new URLSearchParams({
page: `${page - 1}`,
size: `${perPage}`,
...(isEmpty(firstName)
? {}
: { filters: `firstName::like_ignore_case::${firstName}` }),
});
return dataProvider.personList(httpParams);
},
});
useEffect(() => {
(async () => {
await refetch();
})();
}, [page, perPage]);
return (
<Fragment>
<Title title="Users" />
<ListToolbar
actions={
<TopToolbar>
<FilterName setFirstName={setFirstName} />
</TopToolbar>
}
/>
<Datagrid
resource="person"
data={data?.content}
total={data?.totalElements}
isPending={isPending}
sort={undefined}
bulkActionButtons={false}
>
<TextField source="firstName" />
<TextField source="lastName" />
<TextField source="email" label="Email User ID" />
<TextField source="id" label="User ID" />
<TextField source="status" />
<BooleanField source="userAccess" label="Access to the Platform" />
<WrapperField label="Company">
<ArrayField source="parties" />
</WrapperField>
<WrapperField label="Programs assigned">
<ArrayField source="assignedPrograms" />
</WrapperField>
<WrapperField label="Functions assigned">
<ArrayField source="assignedFunctions" />
</WrapperField>
</Datagrid>
<ListContextProvider
value={{
page,
perPage,
total: data?.totalElements ?? 0,
setPage,
setPerPage,
}}
>
<Pagination />
</ListContextProvider>
</Fragment>
);
};
const FilterName = ({ setFirstName }) => {
return (
<MuiTextField
label="First name"
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setFirstName(event.target.value);
}}
/>
);
};

View File

@@ -0,0 +1,114 @@
import React from "react";
import {
BooleanField,
DateField,
Labeled,
Show,
SimpleShowLayout,
TextField,
} from "react-admin";
import {
Box,
Card,
CardContent,
CardHeader,
Stack,
Typography,
} from "@mui/material";
import { ArrayField } from "@admin";
export const PersonShow = () => {
return (
<Show>
<SimpleShowLayout>
<Card sx={{ border: 1 }}>
<CardHeader title="General info" />
<CardContent>
<Stack
sx={{
flexWrap: "wrap",
justifyContent: "flex-start",
alignItems: "center",
}}
direction="row"
columnGap={5}
rowGap={2}
>
<Labeled label="Name">
<Stack direction="row" gap={1}>
<TextField sx={{ textTransform: "" }} source="title" />
<TextField source="firstName" />
<TextField source="lastName" />
</Stack>
</Labeled>
<Labeled label="E-mail address">
<TextField source="email" />
</Labeled>
<Labeled label="User ID">
<TextField source="id" />
</Labeled>
<Labeled label="Access to the Platform">
<BooleanField source="userAccess" />
</Labeled>
<Box>
<Typography
sx={{ color: "rgba(255, 255, 255, 0.7)" }}
variant="caption"
>
Company
</Typography>
<ArrayField source="parties" />
</Box>
<Box sx={{ flexBasis: "100%" }} />
<Labeled label="Access to the Platform">
<BooleanField source="userAccess" />
</Labeled>
<Labeled label="Position">
<TextField source="jobPosition" emptyText={"\u2014"} />
</Labeled>
<Labeled label="Phone number">
<TextField source="phoneNumber" emptyText={"\u2014"} />
</Labeled>
<Labeled label="Mobile number">
<TextField source="mobileNumber" emptyText={"\u2014"} />
</Labeled>
<Box />
</Stack>
</CardContent>
</Card>
<Card sx={{ border: 1 }}>
<CardHeader title="Platform info" />
<CardContent>
<Stack
sx={{ justifyContent: "flex-start", alignItems: "center" }}
direction="row"
gap={5}
>
<Labeled label="Last changes date">
<DateField
source="updateDate"
locales="fr-CA"
emptyText={"\u2014"}
/>
</Labeled>
<Labeled label="User Access Active From">
<DateField
source="userAccessFrom"
locales="fr-CA"
emptyText={"\u2014"}
/>
</Labeled>
<Labeled label="User Access Active To">
<DateField
source="userAccessTo"
locales="fr-CA"
emptyText={"\u2014"}
/>
</Labeled>
</Stack>
</CardContent>
</Card>
</SimpleShowLayout>
</Show>
);
};

View File

@@ -10,6 +10,7 @@ export const authProvider: AuthProvider = {
response = await fetch(
new Request("http://localhost:8080/atsp-idp/token", {
method: "POST",
credentials: "include",
body: new URLSearchParams({
grant_type: "authorization_code",
code: "code",
@@ -36,9 +37,9 @@ export const authProvider: AuthProvider = {
);
}
const { refresh_token } = await response.json();
localStorage.setItem("token", refresh_token);
localStorage.setItem("user", refresh_token);
const { access_token } = await response.json();
localStorage.setItem("user", access_token);
localStorage.setItem("token", access_token);
return Promise.resolve();
},

View File

@@ -1,19 +1,10 @@
import { useQueryEngine } from "@core";
import { useQueryEngine } from './hooks';
export const dataProviderExtension = () => {
const { fetchCommon } = useQueryEngine();
const url = import.meta.env.VITE_SIMPLE_REST_URL;
return {
person: () =>
fetchCommon(
"http://localhost:8081/core/api/admin/person",
{
headers: new Headers({
Accept: "application/json",
'X-XSRF-TOKEN': 'c3aKjTfLsPSdmiMtTaj933RbsQMR6IWmf9C5ZImMh9pCmDGkEETougKpiZew-UcZe4XJ5xE4nDpwirWLR-SOXbm94-Im-QLB',
'Cookie': 'LAST_LOGIN_LOCATION=http://localhost:3000; SESSION=652f7c23-fe94-4727-a980-59052e75aab2; XSRF-TOKEN=c2b75b9c-cd46-48ec-9ab0-847901d8da3e',
}),
},
),
personList: (params: URLSearchParams) => fetchCommon(`${url}/person?${params}`),
};
};

View File

@@ -0,0 +1,31 @@
import { fetchUtils, withLifecycleCallbacks } from 'react-admin';
import simpleRestProvider from "ra-data-simple-rest";
import { dataProviderExtension } from "@core";
const fetchJson = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: "application/json" });
}
options.credentials = "include";
options.headers.set(
"Authorization",
`Bearer ${localStorage.getItem("user")}`,
);
options.headers.set("X-XSRF-TOKEN", localStorage.getItem("user"));
return fetchUtils.fetchJson(url, options);
};
export const dataProvider = withLifecycleCallbacks(
{
...simpleRestProvider(import.meta.env.VITE_SIMPLE_REST_URL, fetchJson),
...dataProviderExtension(),
},
[
{
resource: "person",
afterRead: async (data: object) => {
return data.original;
},
},
],
);

View File

@@ -1,16 +1,16 @@
export const useQueryEngine = () => {
const defaultHeaders = {
Accept: 'application/ld+json',
'Content-Type': 'application/ld+json',
Accept: "application/json",
"Content-Type": "application/json",
};
const response = async (request) => {
const response = async (request: Request) => {
let response;
try {
response = await fetch(request);
} catch (e) {
throw new Error('Network error');
throw new Error("Network error");
}
if (200 > response?.status || 300 <= response?.status) {
@@ -20,58 +20,76 @@ export const useQueryEngine = () => {
return response.json();
};
return ({
const decodeJwt = (token: string) => {
try {
return JSON.parse(window.atob(token.split(".")[1]));
} catch (e) {
return null;
}
};
return {
defaultHeaders,
fetchCommon: async (url, options = {}) => {
fetchCommon: async (url: string, options: object = {}) => {
const token = decodeJwt(localStorage.getItem("user"));
document.cookie = `XSRF-TOKEN=${token.sub}; SESSION=${token.jti}`;
return await response(
new Request(url, {
...(options?.method ? { method: options.method } : { method: 'GET' }),
credentials: "include",
...(options?.method ? { method: options.method } : { method: "GET" }),
...(options.replaceHeaders
? { headers: options.replaceHeaders }
? { headers: new Headers(options.replaceHeaders) }
: {
headers: {
...defaultHeaders,
Authorization: `Bearer ${localStorage.getItem('user')}`,
...(options?.headers ? { ...options.headers } : {}),
},
}),
headers: new Headers({
...defaultHeaders,
Authorization: `Bearer ${localStorage.getItem("user")}`,
"X-XSRF-TOKEN": localStorage.getItem("user"),
...(options?.headers ? { ...options.headers } : {}),
}),
}),
...(options?.body ? { body: options.body } : {}),
}),
);
},
fetchMultipart: async (url, options) => {
fetchMultipart: async (url: string, options: object = {}) => {
return await response(
new Request(url, {
method: 'POST',
method: "POST",
headers: {
Accept: 'application/ld+json',
Accept: "application/json",
/**
* DO NOT SPECIFY THIS - Because the boundary data in it
* @see https://stackoverflow.com/a/71392989/3111514
*/
// 'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${localStorage.getItem('user')}`,
Authorization: `Bearer ${localStorage.getItem("user")}`,
},
...options,
}),
);
},
fetchPlain: async (url, options = {}, withAuth = false) => {
fetchPlain: async (url: string, options: object = {}, withAuth = false) => {
return await response(
new Request(url, {
...(options?.method ? { method: options.method } : { method: 'GET' }),
...(options?.method ? { method: options.method } : { method: "GET" }),
...(options.replaceHeaders
? { headers: options.replaceHeaders }
: {
headers: {
'Content-Type': 'application/json',
...(options?.headers ? { headers: options.headers } : {}),
...(withAuth ? { Authorization: `Bearer ${localStorage.getItem('user')}` } : {}),
},
}),
headers: {
"Content-Type": "application/json",
...(options?.headers ? { headers: options.headers } : {}),
...(withAuth
? {
Authorization: `Bearer ${localStorage.getItem("user")}`,
}
: {}),
},
}),
...(options?.body ? { body: options.body } : {}),
}),
);
},
});
};
};

View File

@@ -1,2 +1,4 @@
export { dataProviderExtension } from './data-provider-extension';
export { dataProvider } from './dataProvider.ts';
export { authProvider } from './authProvider.ts';
export * from './hooks';

View File

@@ -1,7 +0,0 @@
import simpleRestProvider from "ra-data-simple-rest";
import { dataProviderExtension } from "@core";
export const dataProvider = {
...simpleRestProvider(import.meta.env.VITE_SIMPLE_REST_URL),
...dataProviderExtension(),
};

View File

@@ -7,6 +7,8 @@ export default defineConfig({
plugins: [react()],
server: {
host: true,
port: 3000,
cors: false,
},
base: "./",
resolve: {