Merge pull request 'Ajout de la page dashboard' (#1) from feat-dashboard into master
Reviewed-on: https://codeberg.org/OKI/konstitisyon.la/pulls/1
This commit is contained in:
+35
-1
@@ -1,5 +1,19 @@
|
||||
import {redirect} from 'next/navigation'
|
||||
import Box from '@mui/material/Box'
|
||||
import Container from '@mui/material/Container'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import HomeIcon from '@mui/icons-material/Home'
|
||||
import {auth} from '../../auth.js'
|
||||
import GetVersions from '@/components/versions/get-versions.js'
|
||||
import Footer from '@/components/footer.js'
|
||||
import Sign from '@/components/session/sign.js'
|
||||
|
||||
const navButton = {
|
||||
title: 'Accueil',
|
||||
path: '/',
|
||||
color: 'success',
|
||||
icon: <HomeIcon fontSize='large' />
|
||||
}
|
||||
|
||||
export default async function DashboardPage() {
|
||||
const session = await auth()
|
||||
@@ -9,6 +23,26 @@ export default async function DashboardPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>DashboardPage</div>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Typography
|
||||
textTransform='uppercase'
|
||||
mt={1}
|
||||
component='h1'
|
||||
textAlign='center'
|
||||
variant='h4'
|
||||
>
|
||||
Tableau de bord
|
||||
</Typography>
|
||||
<Sign session={session} navButton={navButton} />
|
||||
<GetVersions session={session} />
|
||||
</Container>
|
||||
<Footer />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
+9
-1
@@ -2,6 +2,7 @@ import {createDirectus, rest, readItems} from '@directus/sdk'
|
||||
import Container from '@mui/material/Container'
|
||||
import Box from '@mui/material/Box'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings'
|
||||
import {auth} from '../auth.js'
|
||||
import Konstitisyon from '@/components/konstitisyon/index.js'
|
||||
import Footer from '@/components/footer.js'
|
||||
@@ -11,6 +12,13 @@ import Create from '@/components/konstitisyon/create/index.js'
|
||||
const apiUrl = process.env.DIRECTUS_API_URL
|
||||
const appTitle = process.env.APP_TITLE
|
||||
|
||||
const navButton = {
|
||||
title: 'Tableau de bord',
|
||||
path: '/dashboard',
|
||||
color: 'success',
|
||||
icon: <AdminPanelSettingsIcon fontSize='large' />
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
if (!apiUrl) {
|
||||
throw new Error('DIRECTUS_API_URL is required')
|
||||
@@ -67,7 +75,7 @@ export default async function Page() {
|
||||
>
|
||||
<Container maxWidth='sm'>
|
||||
<Typography mt={1} component='h1' textAlign='center' variant='h4'>{appTitle.toUpperCase()}</Typography>
|
||||
<Sign session={session} />
|
||||
<Sign session={session} navButton={navButton} />
|
||||
{session && (
|
||||
<Create session={session} titres={titres} />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import Box from '@mui/material/Box'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
|
||||
export function Loading() {
|
||||
return (
|
||||
<Box
|
||||
display='flex'
|
||||
justifyContent='center'
|
||||
alignItems='center'
|
||||
height='100vh'
|
||||
>
|
||||
<CircularProgress color='warning' />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import Tooltip, {tooltipClasses} from '@mui/material/Tooltip'
|
||||
import LogoutIcon from '@mui/icons-material/Logout'
|
||||
import LoginIcon from '@mui/icons-material/Login'
|
||||
import PersonAddIcon from '@mui/icons-material/PersonAdd'
|
||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings'
|
||||
import ConfirmationAlert from './confirmation-alert.js'
|
||||
|
||||
const LightTooltip = styled(({className, ...props}) => (
|
||||
@@ -26,7 +25,7 @@ const LightTooltip = styled(({className, ...props}) => (
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Sign({session}) {
|
||||
export default function Sign({session, navButton}) {
|
||||
const router = useRouter()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
@@ -45,9 +44,9 @@ export default function Sign({session}) {
|
||||
<LogoutIcon fontSize='large' />
|
||||
</Fab>
|
||||
</LightTooltip>
|
||||
<LightTooltip title='Tableau de bord' placement='right'>
|
||||
<Fab sx={{mr: 3}} size='large' color='warning' onClick={() => router.push('/dashboard')}>
|
||||
<AdminPanelSettingsIcon fontSize='large' />
|
||||
<LightTooltip title={navButton.title} placement='right'>
|
||||
<Fab sx={{mr: 3}} size='large' color={navButton.color} onClick={() => router.push(navButton.path)}>
|
||||
{navButton.icon}
|
||||
</Fab>
|
||||
</LightTooltip>
|
||||
</Stack>
|
||||
@@ -78,5 +77,6 @@ export default function Sign({session}) {
|
||||
}
|
||||
|
||||
Sign.propTypes = {
|
||||
session: PropTypes.object
|
||||
session: PropTypes.object,
|
||||
navButton: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
'use client'
|
||||
|
||||
import {useState, useRef, useEffect} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Box from '@mui/material/Box'
|
||||
import AuthAlert from '../auth-form/auth-alert.js'
|
||||
import LogoutCountdown from '../session/logout-countdown.js'
|
||||
import {Loading} from '../loading.js'
|
||||
import ListVersions from './list-versions.js'
|
||||
import {listVersions} from '@/lib/directus.js'
|
||||
|
||||
export default function GetVersions({session}) {
|
||||
const {accessToken, userId} = session.user
|
||||
|
||||
const [versions, setVersions] = useState(null)
|
||||
const [isErrorAlertOpen, setIsErrorAlertOpen] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const countdownRef = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchVersions() {
|
||||
const data = await listVersions({
|
||||
accessToken, userId, countdownRef, setError, setIsErrorAlertOpen
|
||||
})
|
||||
setVersions(data)
|
||||
}
|
||||
|
||||
fetchVersions()
|
||||
}, [accessToken, userId])
|
||||
|
||||
if (!versions) {
|
||||
return (
|
||||
<>
|
||||
{error && <AuthAlert
|
||||
isOpen={isErrorAlertOpen}
|
||||
setIsOpen={setIsErrorAlertOpen}
|
||||
message={error}
|
||||
severity='error'
|
||||
/>}
|
||||
<Loading />
|
||||
<LogoutCountdown ref={countdownRef} setError={setError} setIsErrorAlertOpen={setIsErrorAlertOpen} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const titres = versions?.filter(({collection}) => collection === 'titres')
|
||||
const articles = versions?.filter(({collection}) => collection === 'articles')
|
||||
|
||||
return (
|
||||
<>
|
||||
{error && <AuthAlert
|
||||
isOpen={isErrorAlertOpen}
|
||||
setIsOpen={setIsErrorAlertOpen}
|
||||
message={error}
|
||||
severity='error'
|
||||
/>}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
border: '2px solid #ccc',
|
||||
borderRadius: '8px',
|
||||
padding: 1,
|
||||
my: 4
|
||||
}}
|
||||
>
|
||||
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant='h5'
|
||||
component='h2'
|
||||
textAlign='center'
|
||||
sx={{display: 'block', fontWeight: 'bold', textTransform: 'uppercase'}}
|
||||
>
|
||||
Liste des versions
|
||||
</Typography>
|
||||
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
justifyContent={
|
||||
(titres.length > 0 && articles.length === 0) || (articles.length > 0 && titres.length === 0)
|
||||
? 'center'
|
||||
: 'flex-start'
|
||||
}
|
||||
>
|
||||
{titres.length > 0 && (
|
||||
<Grid size={{xs: 12, md: articles.length > 0 ? 6 : 12}}>
|
||||
<ListVersions collection='Titres' data={titres} />
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{articles.length > 0 && (
|
||||
<Grid size={{xs: 12, md: titres.length > 0 ? 6 : 12}}>
|
||||
<ListVersions collection='Articles' data={articles} />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
<LogoutCountdown ref={countdownRef} setError={setError} setIsErrorAlertOpen={setIsErrorAlertOpen} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
GetVersions.propTypes = {
|
||||
session: PropTypes.object.isRequired
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Table from '@mui/material/Table'
|
||||
import TableBody from '@mui/material/TableBody'
|
||||
import TableCell from '@mui/material/TableCell'
|
||||
import TableContainer from '@mui/material/TableContainer'
|
||||
import TableHead from '@mui/material/TableHead'
|
||||
import TableRow from '@mui/material/TableRow'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import Button from '@mui/material/Button'
|
||||
import {TableVirtuoso} from 'react-virtuoso'
|
||||
import {Box, Typography} from '@mui/material'
|
||||
import {formatDate} from '@/lib/format.js'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
width: 200,
|
||||
label: 'Version',
|
||||
dataKey: 'name',
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
label: 'Créée le',
|
||||
dataKey: 'date_created',
|
||||
numeric: true,
|
||||
}
|
||||
]
|
||||
|
||||
const VirtuosoTableComponents = {
|
||||
Scroller: React.forwardRef((props, ref) => (
|
||||
<TableContainer component={Paper} {...props} ref={ref} />
|
||||
)),
|
||||
Table: props => (
|
||||
<Table {...props} sx={{borderCollapse: 'separate', tableLayout: 'fixed'}} />
|
||||
),
|
||||
TableHead: React.forwardRef((props, ref) => <TableHead {...props} ref={ref} />),
|
||||
TableRow,
|
||||
TableBody: React.forwardRef((props, ref) => <TableBody {...props} ref={ref} />),
|
||||
}
|
||||
|
||||
function fixedHeaderContent() {
|
||||
return (
|
||||
<TableRow>
|
||||
{columns.map(column => (
|
||||
<TableCell
|
||||
key={column.dataKey}
|
||||
variant='head'
|
||||
align={column.numeric || false ? 'right' : 'left'}
|
||||
style={{width: column.width}}
|
||||
sx={{backgroundColor: 'background.paper'}}
|
||||
>
|
||||
{column.label}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
function rowContent(_index, row) {
|
||||
return (
|
||||
<>
|
||||
{columns.map(column => (
|
||||
<TableCell
|
||||
key={column.dataKey}
|
||||
align={column.numeric || false ? 'right' : 'left'}
|
||||
>
|
||||
{column.dataKey === 'date_created' ? formatDate(row[column.dataKey], 'Pp') : <Button variant='outlined' color='success'>{row[column.dataKey]}</Button>}
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ListVersions({collection, data}) {
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant='button' textAlign='center' sx={{display: 'block', fontWeight: 'bold'}}>
|
||||
{collection}
|
||||
</Typography>
|
||||
<Paper style={{height: 350, width: '100%', marginBlock: 5}}>
|
||||
<TableVirtuoso
|
||||
data={data}
|
||||
components={VirtuosoTableComponents}
|
||||
fixedHeaderContent={fixedHeaderContent}
|
||||
itemContent={rowContent}
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
ListVersions.propTypes = {
|
||||
collection: PropTypes.oneOf(['titres', 'articles']).isRequired,
|
||||
data: PropTypes.array.isRequired
|
||||
}
|
||||
@@ -69,6 +69,39 @@ export async function handleSubmit({
|
||||
}
|
||||
}
|
||||
|
||||
export async function listVersions({
|
||||
accessToken,
|
||||
userId,
|
||||
countdownRef,
|
||||
setError,
|
||||
setIsErrorAlertOpen
|
||||
}) {
|
||||
try {
|
||||
await handleUserStatus(accessToken, userId)
|
||||
|
||||
const versions = await directusClient.request(
|
||||
withToken(
|
||||
accessToken,
|
||||
readContentVersions({
|
||||
sort: '-date_created'
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
return versions
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
|
||||
if (error?.errors[0]?.message === 'Token expired.') {
|
||||
countdownRef.current.startCountdown()
|
||||
} else {
|
||||
console.log(error?.errors[0]?.message)
|
||||
setError(error?.errors[0]?.message)
|
||||
setIsErrorAlertOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function createVersion({
|
||||
accessToken,
|
||||
userId,
|
||||
|
||||
+2
-1
@@ -18,7 +18,8 @@
|
||||
"next": "^14.2.3",
|
||||
"next-auth": "^5.0.0-beta.18",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
"react-dom": "^18.3.1",
|
||||
"react-virtuoso": "^4.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-config-xo-nextjs": "^6.0.0",
|
||||
|
||||
@@ -3115,6 +3115,11 @@ react-transition-group@^4.4.5:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-virtuoso@^4.10.2:
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.10.2.tgz#a27308a3c4cfeb24722032acc0b6a46055c26967"
|
||||
integrity sha512-os6n9QKeKRF+8mnQR/vGy/xrFf6vXIzuaAVL54q5k2st2d5QIEwI+KDKaflMUmMvnDbPxf68bs+CF5bY3YI7qA==
|
||||
|
||||
react@^18.3.1:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
|
||||
|
||||
Reference in New Issue
Block a user