Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React admin csrf and other fixes #211

Merged
merged 7 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/Admin/Competition/CompetitionList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {FC} from 'react'
import {ArrayField, Datagrid, DateField, List, ListProps, TextField} from 'react-admin'
import {ArrayField, Datagrid, DateField, List, TextField} from 'react-admin'

// TODO: premysliet a prerobit rozhranie, mozno ako u postov - pri kliku na riadok ukazat Show, kde budu priklady
// (nie ako teraz v zanorenom Datagride)
export const CompetitionList: FC<ListProps> = (props) => (
<List {...props}>
export const CompetitionList: FC = () => (
<List>
<Datagrid>
<TextField source="id" />
<DateField source="deadline" />
Expand Down
5 changes: 3 additions & 2 deletions src/components/Admin/custom/MyArrayField.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {Chip} from '@mui/material'
import {FC} from 'react'
import {FieldProps} from 'react-admin'
import {FieldProps, useRecordContext} from 'react-admin'

type MyArrayFieldProps = FieldProps & {
formatNumber?: (v: number) => string
}

export const MyArrayField: FC<MyArrayFieldProps> = ({record, source, formatNumber}) => {
export const MyArrayField: FC<MyArrayFieldProps> = ({source, formatNumber}) => {
const record = useRecordContext()
if (!record || !source) return null

const array = record[source] as (string | number)[] | undefined
Expand Down
8 changes: 7 additions & 1 deletion src/components/Admin/custom/MyDateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import {DateTime} from 'luxon'
import {ComponentProps, FC} from 'react'
import {DateInput} from 'react-admin'

const backendFormat = 'dd.MM.yyyy HH:mm:ss'

// used to convert BE weird format of 01.01.2020 18:00:00 to 01-01-2020 input format
// https://moment.github.io/luxon/#/formatting
const dateFormatter = (v: string) => DateTime.fromISO(v).toISODate()
// TODO: ale blbost, upravi sa to aj tak nazad na ISO vs ISODate: https://github.com/ZdruzenieSTROM/webstrom-backend/issues/257
const dateFormatter = (v: string) => DateTime.fromFormat(v, backendFormat).toISODate()

// used to convert input format of 01-01-2020 to 01-01-2020TT00:00:00.000+02:00 BE-expected format
// https://moment.github.io/luxon/#/parsing
const dateParser = (v: string) => DateTime.fromISO(v).toISO()

Expand Down
6 changes: 4 additions & 2 deletions src/components/Admin/custom/MyEditActions.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {FC} from 'react'
import {EditActionsProps, ListButton, ShowButton, TopToolbar, useRecordContext, useResourceContext} from 'react-admin'
import {ListButton, ShowButton, TopToolbar, useRecordContext, useResourceContext} from 'react-admin'
// eslint-disable-next-line node/no-extraneous-import
import {useLocation} from 'react-router-dom'

export const MyEditActions: FC<EditActionsProps> = () => {
export const MyEditActions: FC = () => {
const {pathname} = useLocation() // bud '/cms/post/123' alebo '/cms/post/123/1' (prvy tab)

const resource = useResourceContext()
const record = useRecordContext()
// needed, undefined on first load
if (!record) return null

const currentPathWithoutTab = `/${resource}/${record.id}` // '/cms/post/123'
let to = `${currentPathWithoutTab}/show` // '/cms/post/123/show'
Expand Down
2 changes: 2 additions & 0 deletions src/components/Admin/custom/MyShowActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const MyShowActions: FC = () => {

const resource = useResourceContext()
const record = useRecordContext()
// needed, undefined on first load
if (!record) return null

const currentPathWithoutTab = `/${resource}/${record.id}/show` // '/cms/post/123/show'
let to = `/${resource}/${record.id}` // '/cms/post/123'
Expand Down
95 changes: 24 additions & 71 deletions src/components/Admin/dataProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios'
import {stringify} from 'querystring'
import {DataProvider, fetchUtils, RaRecord} from 'react-admin'
import {Cookies} from 'react-cookie'
import {DataProvider, RaRecord} from 'react-admin'

// potencialne TODO: ak BE bude mat pagination, filter alebo sort, upravime a pouzijeme tento kod.
// zatial je pagination aj sort rieseny client-side a len pre getList, filter/search nemame.
Expand All @@ -19,21 +19,6 @@ import {Cookies} from 'react-cookie'
// ordering: `${order === 'ASC' ? '' : '-'}${field}`,
// })

const cookies = new Cookies()

const authFetchJson = (url: string, options?: Record<string, unknown>) => {
const token = cookies.get('webstrom-token')
const authOptions = token
? {
user: {
authenticated: true,
token: 'Token ' + token,
},
}
: {}
return fetchUtils.fetchJson(url, Object.assign(authOptions, options))
}

const dynamicSort = (key: string, order: string) => {
const orderValue = order === 'ASC' ? 1 : -1
return (a: RaRecord, b: RaRecord) => {
Expand All @@ -54,99 +39,67 @@ export const dataProvider: DataProvider = {
// ...getPaginationQuery(params.pagination),
// ...getOrderingQuery(params.sort),
}
const {json} = await authFetchJson(`${apiUrl}/${resource}/?${stringify(query)}`)
const {data} = await axios.get(`${apiUrl}/${resource}/?${stringify(query)}`)

// client-side sort
const {field, order} = params.sort
json.sort(dynamicSort(field, order))

data.sort(dynamicSort(field, order))

// client-side pagination
const {page, perPage} = params.pagination
const pagedData = json.slice((page - 1) * perPage, page * perPage)
const pagedData = data.slice((page - 1) * perPage, page * perPage)

return {
data: pagedData,
total: json.length,
total: data.length,
}
},
getOne: async (resource, params) => {
const {json} = await authFetchJson(`${apiUrl}/${resource}/${params.id}`)
const {data} = await axios.get(`${apiUrl}/${resource}/${params.id}`)

return {
data: json,
}
return {data}
},
getMany: async (resource, params) => {
const data = await Promise.all(params.ids.map((id) => authFetchJson(`${apiUrl}/${resource}/${id}`)))
const data = await Promise.all(params.ids.map((id) => axios.get(`${apiUrl}/${resource}/${id}`)))

return {
data: data.map(({json}) => json),
}
return {data: data.map(({data}) => data)}
},
// TODO: ak budeme pouzivat tuto funkciu, upravime podla getList (pagination, sort, filter). uprimne este neviem, pri com sa pouziva
getManyReference: async (resource, params) => {
const query = {
[params.target]: params.id,
}
const {json} = await authFetchJson(`${apiUrl}/${resource}/?${stringify(query)}`)
const {data} = await axios.get(`${apiUrl}/${resource}/?${stringify(query)}`)

return {
data: json,
total: json.length,
data: data,
total: data.length,
}
},
update: async (resource, params) => {
const {json} = await authFetchJson(`${apiUrl}/${resource}/${params.id}`, {
method: 'PATCH',
body: JSON.stringify(params.data),
})
const {data} = await axios.patch(`${apiUrl}/${resource}/${params.id}`, params.data)

return {data: json}
return {data}
},
updateMany: async (resource, params) => {
const data = await Promise.all(
params.ids.map((id) =>
authFetchJson(`${apiUrl}/${resource}/${id}`, {
method: 'PATCH',
body: JSON.stringify(params.data),
}),
),
)
const data = await Promise.all(params.ids.map((id) => axios.patch(`${apiUrl}/${resource}/${id}`, params.data)))

return {
data: data.map(({json}) => json),
}
return {data: data.map(({data}) => data)}
},
create: async (resource, params) => {
const {json} = await authFetchJson(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
})
const {data} = await axios.post(`${apiUrl}/${resource}`, params.data)

return {
data: json,
}
return {data}
},
delete: async (resource, params) => {
const {json} = await authFetchJson(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
})
const {data} = await axios.delete(`${apiUrl}/${resource}/${params.id}`)

return {
data: json,
}
return {data}
},
deleteMany: async (resource, params) => {
const data = await Promise.all(
params.ids.map((id) =>
authFetchJson(`${apiUrl}/${resource}/${id}`, {
method: 'DELETE',
}),
),
)
const data = await Promise.all(params.ids.map((id) => axios.delete(`${apiUrl}/${resource}/${id}`)))

return {
data: data.map(({json}) => json.id),
}
return {data: data.map(({data}) => data.id)}
},
}
15 changes: 3 additions & 12 deletions src/components/Admin/resources/cms/post/PostCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import {FC} from 'react'
import {
ArrayInput,
Create,
CreateProps,
FormTab,
required,
SimpleFormIterator,
TabbedForm,
TextInput,
} from 'react-admin'
import {ArrayInput, Create, FormTab, required, SimpleFormIterator, TabbedForm, TextInput} from 'react-admin'

import {MyDateInput} from '@/components/Admin/custom/MyDateInput'
import {SitesCheckboxInput} from '@/components/Admin/custom/SitesCheckboxInput'

export const PostCreate: FC<CreateProps> = (props) => (
<Create redirect="show" {...props}>
export const PostCreate: FC = () => (
<Create redirect="show">
<TabbedForm>
<FormTab label="general">
{/* <NumberInput source="id" fullWidth disabled /> */}
Expand Down
16 changes: 3 additions & 13 deletions src/components/Admin/resources/cms/post/PostEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import {FC} from 'react'
import {
ArrayInput,
Edit,
EditProps,
FormTab,
NumberInput,
required,
SimpleFormIterator,
TabbedForm,
TextInput,
} from 'react-admin'
import {ArrayInput, Edit, FormTab, NumberInput, required, SimpleFormIterator, TabbedForm, TextInput} from 'react-admin'

import {MyDateInput} from '@/components/Admin/custom/MyDateInput'
import {MyEditActions} from '@/components/Admin/custom/MyEditActions'
import {SitesCheckboxInput} from '@/components/Admin/custom/SitesCheckboxInput'

export const PostEdit: FC<EditProps> = (props) => (
<Edit {...props} actions={<MyEditActions />} redirect="show" mutationMode="undoable">
export const PostEdit: FC = () => (
<Edit actions={<MyEditActions />} redirect="show" mutationMode="undoable">
<TabbedForm>
<FormTab label="general">
<NumberInput source="id" fullWidth disabled />
Expand Down
6 changes: 3 additions & 3 deletions src/components/Admin/resources/cms/post/PostList.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {FC} from 'react'
import {Datagrid, DateField, List, ListProps, NumberField, TextField} from 'react-admin'
import {Datagrid, DateField, List, NumberField, TextField} from 'react-admin'

export const PostList: FC<ListProps> = (props) => (
<List {...props}>
export const PostList: FC = () => (
<List>
<Datagrid rowClick="show">
<NumberField source="id" />
<TextField source="caption" />
Expand Down
5 changes: 2 additions & 3 deletions src/components/Admin/resources/cms/post/PostShow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
DateField,
NumberField,
Show,
ShowProps,
SimpleShowLayout,
Tab,
TabbedShowLayout,
Expand All @@ -15,8 +14,8 @@ import {
import {MyShowActions} from '@/components/Admin/custom/MyShowActions'
import {SitesArrayField} from '@/components/Admin/custom/SitesArrayField'

export const PostShow: FC<ShowProps> = (props) => (
<Show {...props} actions={<MyShowActions />}>
export const PostShow: FC = () => (
<Show actions={<MyShowActions />}>
<TabbedShowLayout>
<Tab label="general">
<SimpleShowLayout>
Expand Down
4 changes: 0 additions & 4 deletions src/components/PageLayout/MenuMain/MenuMain.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@
padding: 2.5px;
}

.menuItems {
margin-top: 176px;
}

.loading {
position: absolute;
top: 50%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export type Styles = {
menuButton: string
menuCloseButton: string
menuItem: string
menuItems: string
menuOpenButton: string
visible: string
}
Expand Down
23 changes: 16 additions & 7 deletions src/components/PageLayout/MenuMain/MenuMain.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Theme, Typography, useMediaQuery} from '@mui/material'
import {Stack, Theme, Typography, useMediaQuery} from '@mui/material'
import {useQuery} from '@tanstack/react-query'
import axios from 'axios'
import clsx from 'clsx'
Expand All @@ -9,6 +9,7 @@ import {FC, useState} from 'react'
import {CloseButton} from '@/components/CloseButton/CloseButton'
import {Loading} from '@/components/Loading/Loading'
import Menu from '@/svg/menu.svg'
import {useHasPermissions} from '@/utils/useHasPermissions'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

import {Authentication} from '../Authentication/Authentication'
Expand All @@ -23,6 +24,8 @@ interface MenuItemInterface {
export const MenuMain: FC = () => {
const {seminar, seminarId} = useSeminarInfo()

const {hasPermissions} = useHasPermissions()

const [isVisible, setIsVisible] = useState(true)
const toggleMenu = () => setIsVisible((currentIsVisible) => !currentIsVisible)

Expand All @@ -49,12 +52,18 @@ export const MenuMain: FC = () => {
<Loading />
</div>
)}
<div className={styles.menuItems}>
{menuItems.map((menuItem: MenuItemInterface) => {
// `menuItem.url` je vo formate `/vysledky/` alebo `/akcie/matboj/`
return <MenuMainItem key={menuItem.id} caption={menuItem.caption} url={`/${seminar}${menuItem.url}`} />
})}
</div>
<Stack mt="176px">
{menuItems.map(({id, caption, url}) => (
// `url` je vo formate `/vysledky/` alebo `/akcie/matboj/`
<MenuMainItem key={id} caption={caption} url={`/${seminar}${url}`} />
))}
</Stack>
{hasPermissions && (
<Stack sx={{mt: 4, mx: 2, borderTop: '8px dashed white', pt: 4}}>
<MenuMainItem caption="TODO: Opravovanie" url={`/${seminar}/admin/opravovanie`} />
<MenuMainItem caption="Admin" url="/admin" />
</Stack>
)}
<Authentication />
</div>
</>
Expand Down
Loading