Audit qualité : corrections de bugs, nettoyage, lint 0 erreur, tests #4
@@ -4,7 +4,6 @@ NEXT_PUBLIC_API_URL=$API_URL
|
|||||||
NEXT_PUBLIC_API_URL_ROOT=http://localhost:1337
|
NEXT_PUBLIC_API_URL_ROOT=http://localhost:1337
|
||||||
SITE_URL=http://localhost:3001
|
SITE_URL=http://localhost:3001
|
||||||
NEXT_PUBLIC_READ_TOKEN=
|
NEXT_PUBLIC_READ_TOKEN=
|
||||||
NEXT_PUBLIC_ADMIN_JWT_SECRET=
|
|
||||||
|
|
||||||
# IDENTITÉ DU SITE (branding)
|
# IDENTITÉ DU SITE (branding)
|
||||||
NEXT_PUBLIC_SITE_NAME=PAWÒL-NU. Paroles et traductions.
|
NEXT_PUBLIC_SITE_NAME=PAWÒL-NU. Paroles et traductions.
|
||||||
@@ -77,7 +76,6 @@ NEXT_PUBLIC_LIBERAPAY_DONATE=
|
|||||||
NEXT_PUBLIC_PALE_USERNAME=
|
NEXT_PUBLIC_PALE_USERNAME=
|
||||||
NEXT_PUBLIC_GADE_USERNAME=
|
NEXT_PUBLIC_GADE_USERNAME=
|
||||||
NEXT_PUBLIC_YOUTUBE_USERNAME=
|
NEXT_PUBLIC_YOUTUBE_USERNAME=
|
||||||
NEXT_PUBLIC_TELEGRAM_GROUP=
|
|
||||||
NEXT_PUBLIC_XMPP=
|
NEXT_PUBLIC_XMPP=
|
||||||
NEXT_PUBLIC_GIT=
|
NEXT_PUBLIC_GIT=
|
||||||
NEXT_PUBLIC_CODEBERG=
|
NEXT_PUBLIC_CODEBERG=
|
||||||
@@ -86,9 +84,6 @@ NEXT_PUBLIC_BLUESKY_URL=
|
|||||||
# DOMAIN IMAGE
|
# DOMAIN IMAGE
|
||||||
NEXT_PUBLIC_DOMAINS_IMAGE="localhost:1337 strapi.mondomaine.com"
|
NEXT_PUBLIC_DOMAINS_IMAGE="localhost:1337 strapi.mondomaine.com"
|
||||||
|
|
||||||
# JWT SECRET
|
|
||||||
NEXT_PUBLIC_JWT_SECRET=
|
|
||||||
|
|
||||||
# STRIPE
|
# STRIPE
|
||||||
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=
|
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import Proptypes from 'prop-types'
|
|
||||||
import {SessionProvider} from 'next-auth/react'
|
|
||||||
|
|
||||||
export default function AuthProvider({children, session}) {
|
|
||||||
return (
|
|
||||||
<SessionProvider session={session}>
|
|
||||||
{children}
|
|
||||||
</SessionProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthProvider.propTypes = {
|
|
||||||
children: Proptypes.node,
|
|
||||||
session: Proptypes.object
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import {useRef, useEffect} from 'react'
|
|
||||||
import {styled, useTheme} from '@mui/material/styles'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography} from '@mui/material'
|
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
|
||||||
import Cgu from '.'
|
|
||||||
|
|
||||||
const PREFIX = 'cgu-dialog'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
dialog: `${PREFIX}-dialog`
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = styled('div')(() => ({
|
|
||||||
[`& .${classes.dialog}`]: {
|
|
||||||
zIndex: 1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
const CGU_DOWNLOAD_LINK = process.env.NEXT_PUBLIC_CGU_DOWNLOAD_LINK
|
|
||||||
|
|
||||||
export default function CGUDialog({open, setOpen}) {
|
|
||||||
const theme = useTheme()
|
|
||||||
const fullScreen = useMediaQuery(theme.breakpoints.down('md'))
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const descriptionElementRef = useRef(null)
|
|
||||||
useEffect(() => {
|
|
||||||
if (open) {
|
|
||||||
const {current: descriptionElement} = descriptionElementRef
|
|
||||||
if (descriptionElement !== null) {
|
|
||||||
descriptionElement.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [open])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Root>
|
|
||||||
<Dialog
|
|
||||||
fullScreen={fullScreen}
|
|
||||||
open={open}
|
|
||||||
scroll='paper'
|
|
||||||
aria-labelledby='scroll-dialog-title'
|
|
||||||
aria-describedby='scroll-dialog-description'
|
|
||||||
className={classes.dialog}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<DialogTitle className={classes.dialog} id='scroll-dialog-title'>CGU et politique de confidentialité</DialogTitle>
|
|
||||||
<DialogContent dividers>
|
|
||||||
<Typography
|
|
||||||
ref={descriptionElementRef}
|
|
||||||
component='div'
|
|
||||||
variant='inherit'
|
|
||||||
id='scroll-dialog-description'
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
{CGU_DOWNLOAD_LINK ? (
|
|
||||||
<div style={{textAlign: 'center', padding: '2rem'}}>
|
|
||||||
<Typography paragraph>
|
|
||||||
Consultez nos CGU et notre politique de confidentialité :
|
|
||||||
</Typography>
|
|
||||||
<Button
|
|
||||||
variant='contained'
|
|
||||||
component='a'
|
|
||||||
href={CGU_DOWNLOAD_LINK}
|
|
||||||
target='_blank'
|
|
||||||
rel='noopener noreferrer'
|
|
||||||
>
|
|
||||||
Consulter les CGU
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Cgu />
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button color='primary' onClick={handleClose}>
|
|
||||||
Fermer
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
CGUDialog.propTypes = {
|
|
||||||
open: PropTypes.bool.isRequired,
|
|
||||||
setOpen: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,592 +0,0 @@
|
|||||||
import {List, ListItem, ListItemText, Link, Typography, ListItemIcon} from '@mui/material'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
import ArrowRightAltIcon from '@mui/icons-material/ArrowRightAlt'
|
|
||||||
|
|
||||||
const PREFIX = 'index'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
root: `${PREFIX}-root`
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = styled('div')({
|
|
||||||
[`&.${classes.root}`]: {
|
|
||||||
textAlign: 'justify'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function Cgu() {
|
|
||||||
return (
|
|
||||||
<Root className={classes.root}>
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
Définitions
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Prestations et Services </strong>: <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> met à disposition :
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Contenu </strong>: Ensemble des éléments constituants l’information présente sur le site, notamment textes – images – vidéos.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Informations utilisateurs </strong>: Ci après dénommé « Information (s) »
|
|
||||||
qui correspondent à l’ensemble des données personnelles susceptibles d’être détenues par
|
|
||||||
<Link href='https://pawol.nu'> <strong>pawol.nu</strong></Link> pour la gestion de votre compte, et à des fins d’analyses et de statistiques.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Utilisateur </strong>: Internaute, utilisant le site susnommé.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Utilisateur enregistré</strong>: Internaute ayant compte utilisateur, utilisant le site susnommé.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography variant='body1'>
|
|
||||||
<strong>Informations personnelles </strong>: « Les informations qui permettent, sous quelque forme que ce soit, directement ou non,
|
|
||||||
l’identification des personnes physiques auxquelles elles s’appliquent » (article 4 de la loi n° 78-17 du 6 janvier 1978).
|
|
||||||
</Typography>
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Les termes « données à caractère personnel », « personne concernée », « sous traitant » et « données sensibles » ont le sens défini par le Règlement
|
|
||||||
Général sur la Protection des Données (RGPD : n° 2016-679)
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
1. Présentation du site internet
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
En vertu de l’article 6 de la loi n° 2004-575 du 21 juin 2004 pour la confiance dans l’économie numérique, il est précisé aux utilisateurs du site internet
|
|
||||||
<Link href='https://pawol.nu'> <strong>pawol.nu</strong></Link> l’identité des différents intervenants dans le cadre de sa réalisation et de son suivi :
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Propriétaire</strong> : ORGANISATION KA INTERNATIONALE (Association loi 1901)
|
|
||||||
</Typography>
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<strong>Responsable publication</strong> : ORGANISATION KA INTERNATIONALE – <Link href='mailto:kontak@o-k-i.net'><strong>kontak@o-k-i.net</strong></Link>
|
|
||||||
</Typography>
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Le responsable publication est une personne morale
|
|
||||||
</Typography>
|
|
||||||
<Typography variant='body1'>
|
|
||||||
<strong>Réalisation</strong> : Cédric Pronzola
|
|
||||||
</Typography>
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<strong>Courriel</strong> : <Link href='mailto:contact@cedric-pronzola.dev'><strong>contact@cedric-pronzola.dev</strong></Link>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<strong>WEB</strong> : <Link target='_blank' rel='noreferrer' href='https://cedric-pronzola.dev'><strong>cedric-pronzola.dev</strong></Link>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<strong>Git</strong> : <Link target='_blank' rel='noreferrer' href='https://labola.o-k-i.net/ORGANISATION-KA-INTERNATIONALE'><strong>LABOLA - OKI</strong></Link>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
<Typography variant='body1'>
|
|
||||||
<strong>Hébergeur</strong> : OVH – 2 rue Kellermann - 59100 Roubaix - France
|
|
||||||
</Typography>
|
|
||||||
<Typography gutterBottom variant='body1'>
|
|
||||||
<strong>Délégué à la protection des données</strong> : ORGANISATION KA INTERNATIONALE – <Link href='mailto:kontak@o-k-i.net'><strong>kontak@o-k-i.net</strong></Link>
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Ces mentions légales RGPD sont issues du <Link target='_blank' rel='noreferrer' href='https://fr.orson.io/1371/generateur-mentions-legales'>générateur gratuit de mentions légales pour un site internet (orson.io)</Link>.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
2. Conditions générales d’utilisation du site et des services proposés.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
L’utilisation du site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> implique l’acceptation pleine et entière des conditions générales d’utilisation ci-après décrites.
|
|
||||||
Ces conditions d’utilisation sont susceptibles d’être modifiées ou complétées à tout moment, les utilisateurs du site <Link href='https://pawol.nu'><strong>pawol.nu </strong></Link>
|
|
||||||
sont donc invités à les consulter de manière régulière.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Ce site internet est normalement accessible à tout moment aux utilisateurs. Une interruption pour raison de maintenance technique peut être toutefois
|
|
||||||
décidée par <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>, qui s’efforcera alors de communiquer préalablement aux utilisateurs les dates et heures de l’intervention.
|
|
||||||
Le site web <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> est mis à jour régulièrement par <strong>ORGANISATION KA INTERNATIONALE</strong>. De la même façon, les mentions légales peuvent être
|
|
||||||
modifiées à tout moment : elles s’imposent néanmoins à l’utilisateur qui est invité à s’y référer le plus souvent possible afin d’en prendre connaissance.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
3. Description des services fournis
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> s’efforce de fournir sur le site, des informations
|
|
||||||
aussi précises que possible. Toutefois, il ne pourra être tenu responsable des oublis, des inexactitudes et des carences dans la mise à jour, qu’elles soient de son fait ou du fait
|
|
||||||
des tiers partenaires qui lui fournissent ces informations.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Toutes les informations indiquées sur le site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> sont données à titre indicatif,
|
|
||||||
et sont susceptibles d’évoluer. Par ailleurs, les renseignements figurant sur le site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> ne sont pas exhaustifs.
|
|
||||||
Ils sont donnés sous réserve de modifications ayant été apportées depuis leur mise en ligne.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
4. Limitations contractuelles sur les données techniques
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Le site utilise la technologie JavaScript.
|
|
||||||
Le site Internet ne pourra être tenu responsable de dommages matériels liés à l’utilisation du site. De plus, l’utilisateur du site s’engage à accéder au site en utilisant un matériel récent,
|
|
||||||
ne contenant pas de virus et avec un navigateur de dernière génération mis-à-jour.
|
|
||||||
Le site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> est hébergé chez un prestataire sur le territoire de l’Union Européenne conformément aux dispositions du
|
|
||||||
Règlement Général sur la Protection des Données (RGPD : n° 2016-679).
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
L’objectif est d’apporter une prestation qui assure le meilleur taux d’accessibilité.
|
|
||||||
L’hébergeur assure la continuité de son service 24 Heures sur 24, tous les jours de l’année. Il se réserve néanmoins la possibilité d’interrompre le service d’hébergement
|
|
||||||
pour les durées les plus courtes possibles notamment à des fins de maintenance, d’amélioration de ses infrastructures, de défaillance de ses infrastructures ou si les
|
|
||||||
Prestations et Services génèrent un trafic réputé anormal.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> et l’hébergeur ne pourront être tenus responsables en cas de dysfonctionnement du réseau Internet,
|
|
||||||
des lignes téléphoniques ou du matériel informatique et de téléphonie lié notamment à l’encombrement du réseau empêchant l’accès au serveur.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
5. Limitations de responsabilité
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> agit en tant qu’éditeur du site. <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> est responsable
|
|
||||||
de la qualité et de la véracité du Contenu qu’il publie.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> ne pourra être tenu responsable des dommages directs et indirects causés au matériel de l’utilisateur,
|
|
||||||
lors de l’accès au site internet <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>, et résultant soit de l’utilisation d’un matériel
|
|
||||||
ne répondant pas aux spécifications indiquées au point 4, soit de l’apparition d’un bug ou d’une incompatibilité.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
6. Gestion des données personnelles
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
6.1 Responsables de la collecte des données personnelles
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Pour les données personnelles collectées dans le cadre de la création du compte personnel de l’utilisateur et de sa navigation sur le site
|
|
||||||
le responsable du traitement des données personnelles est : ORGANISATION KA INTERNATIONALE. <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
En tant que responsable du traitement des données qu’il collecte, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> s’engage à respecter le cadre des dispositions légales en vigueur.
|
|
||||||
Chaque fois que <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> traite des Données Personnelles, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> prend toutes les mesures raisonnables
|
|
||||||
pour s’assurer de l’exactitude et de la pertinence des Données Personnelles au regard des finalités pour lesquelles <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> les traite.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
6.2 Finalité des données collectées
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> est susceptible de traiter tout ou partie des données :
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
pour permettre la navigation sur le site et la gestion et la traçabilité des prestations et services commandés par l’utilisateur : données de connexion et d’utilisation du site
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
pour prévenir et lutter contre la fraude informatique (spamming, hacking…) : matériel informatique utilisé pour la navigation, l’adresse IP, le mot de passe (hashé)
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
pour améliorer la navigation sur le site : données de connexion et d’utilisation
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
6.3 Droit d’accès, de rectification et d’opposition
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography variant='body1'>
|
|
||||||
Conformément à la réglementation européenne en vigueur, les Utilisateurs de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> disposent des droits suivants :
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
droit d’accès (article 15 RGPD) et de rectification (article 16 RGPD), de mise à jour, de complétude des données des Utilisateurs droit de verrouillage ou d’effacement
|
|
||||||
des données des Utilisateurs à caractère personnel (article 17 du RGPD), lorsqu’elles sont inexactes, incomplètes, équivoques, périmées, ou dont la collecte, l’utilisation,
|
|
||||||
la communication ou la conservation est interdite
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
droit de retirer à tout moment un consentement (article 13-2c RGPD)
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
droit à la limitation du traitement des données des Utilisateurs (article 18 RGPD)
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
droit à la portabilité des données que les Utilisateurs auront fournies, lorsque ces données font l’objet de traitements automatisés fondés sur leur consentement ou sur un contrat (article 20 RGPD)
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
droit de définir le sort des données des Utilisateurs après leur mort et de choisir à qui <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> devra communiquer (ou non)
|
|
||||||
ses données à un tiers qu’ils aura préalablement désigné
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Dès que <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> a connaissance du décès d’un Utilisateur et à défaut d’instructions de sa part, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> s’engage à détruire
|
|
||||||
ses données, sauf si leur conservation s’avère nécessaire à des fins probatoires ou pour répondre à une obligation légale.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Si l’Utilisateur souhaite savoir comment <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> utilise ses Données Personnelles, demander à les rectifier ou s’oppose à leur traitement,
|
|
||||||
l’Utilisateur peut contacter <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> par courriel à l’adresse suivante, <Link href='mailto:kontak@o-k-i.net'><strong>kontak@o-k-i.net</strong></Link> ou via XMPP à <Link href='xmpp:oki@xmpp.cz'><strong>oki@xmpp.cz</strong></Link>.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Dans ce cas, l’Utilisateur doit indiquer les Données Personnelles qu’il souhaiterait que <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> corrige,
|
|
||||||
mette à jour ou supprime, en s’identifiant précisément avec une copie d’une pièce d’identité (carte d’identité ou passeport).
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Les demandes de suppression de Données Personnelles seront soumises aux obligations qui sont imposées à <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> par la loi,
|
|
||||||
notamment en matière de conservation ou d’archivage des documents. Enfin, les Utilisateurs de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> peuvent déposer une réclamation
|
|
||||||
auprès des autorités de contrôle, et notamment de la CNIL (https://www.cnil.fr/fr/plaintes).
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
6.4 Non-communication des données personnelles
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> s’interdit de traiter, héberger ou transférer les Informations collectées sur ses utilisateurs vers un pays situé en dehors de
|
|
||||||
l’Union européenne ou reconnu comme « non adéquat » par la Commission européenne sans en informer préalablement l’utilisateur. Pour autant, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> reste
|
|
||||||
libre du choix de ses sous-traitants techniques et commerciaux à la condition qu’il présentent les garanties suffisantes au regard des exigences du Règlement Général sur la Protection des
|
|
||||||
Données (RGPD : n° 2016-679).
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> s’engage à prendre toutes les précautions nécessaires afin de préserver la sécurité des Informations et notamment qu’elles ne soient pas communiquées
|
|
||||||
à des personnes non autorisées. Cependant, si un incident impactant l’intégrité ou la confidentialité des Informations de l’utilisateur est portée
|
|
||||||
à la connaissance de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>, celle-ci devra dans les meilleurs délais informer l’utilisateur
|
|
||||||
et lui communiquer les mesures de corrections prises. Par ailleurs <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> ne collecte aucune « données sensibles ».
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
6.5 Inscription et connexion
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Les utilisateurs peuvent s’inscrire sur <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> en renseignant leur adresse e-mail, un nom d’utilisateur ainsi qu’un mot de passe.
|
|
||||||
À la suite de l’inscription, un lien de vérification est envoyé à l’adresse utilisée. Ce lien permet d’activer le compte. Sans cette activation, l’utilisateur n’aura pas la possibilité de se connecter au site.
|
|
||||||
Si un utilisateur n’a pas reçu le lien d’activation, il a la possibilité de demander son renvoi.
|
|
||||||
L’utilisateur ayant oublié son mot de passe peut faire la demande de renouvellement de celui-ci.
|
|
||||||
Avant toute inscription, il est important de noté que le nom d’utilisateur est public et donc accessible à tous. En effet, le site fait appel à une API qui permet la consultation de plusieurs éléments, dont l’identifiant de l’utilisateur ainsi que le nom qu’il aura choisi au moment de l’inscription.
|
|
||||||
Cependant, l’adresse e-mail ne figure pas dans les informations accessibles. Seul <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> en à connaissance.
|
|
||||||
Les utilisateurs peuvent également se connecter en utilisant un compte Twitter. Dans ce cas, c’est l’identifiant de cette plateforme qui sera affiché en cas de contribution.
|
|
||||||
Il est également possible de se connecter grâce à un compte Google.
|
|
||||||
En acceptant d’utiliser la fonctionnalité de connexion via Google ou Twitter, vous autorisez <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> à récupérer les informations liées à votre profil, que vous avez transmises à ces plateformes lors de votre inscription.
|
|
||||||
Un compte GitHub permet désormais de se connecter au site. Nous utilisons uniquement le nom d’utilisateur, ainsi que l’adresse e-mail.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
6.6 Proposition de paroles
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Les utilisateurs connectés ont la possibilité de proposer des paroles depuis un espace dédié.
|
|
||||||
Il se trouve à l’adresse <Link href='https://pawol.nu/pwopose'><strong>pawol.nu/pwopose</strong></Link>.
|
|
||||||
Les textes soumis ne sont pas directement publiés sur le site. Ils doivent être approuvé par l’équipe de vérification.
|
|
||||||
Après approbation, il apparaît sur le site avec le nom de l’utilisateur l’ayant proposé.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
7. Notification d’incident
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Quels que soient les efforts fournis, aucune méthode de transmission sur Internet et aucune méthode de stockage électronique n’est complètement sûre.
|
|
||||||
Nous ne pouvons en conséquence pas garantir une sécurité absolue.
|
|
||||||
Si nous prenions connaissance d’une brèche de la sécurité, nous avertirions les utilisateurs concernés afin qu’ils puissent prendre les mesures appropriées.
|
|
||||||
Nos procédures de notification d’incident tiennent compte de nos obligations légales, qu’elles se situent au niveau national ou européen. Nous nous engageons à informer pleinement
|
|
||||||
nos utilisateurs de toutes les questions relevant de la sécurité de leur compte et à leur fournir toutes les informations nécessaires pour les aider à respecter leurs propres
|
|
||||||
obligations réglementaires en matière de reporting.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Aucune information personnelle de l’utilisateur du site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> n’est publiée à l’insu de l’utilisateur, échangée, transférée,
|
|
||||||
cédée ou vendue sur un support quelconque à des tiers. Seule l’hypothèse du rachat de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> et de ses droits permettrait
|
|
||||||
la transmission des dites informations à l’éventuecodel acquéreur qui serait à son tour tenu de la même obligation de conservation et de modification des données vis à vis de
|
|
||||||
l’utilisateur du site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
Sécurité
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Pour assurer la sécurité et la confidentialité des Données Personnelles et des Données Personnelles de Santé, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> utilise des réseaux protégés
|
|
||||||
par des dispositifs standards tels que par pare-feu, la pseudonymisation, l’encryption et mot de passe.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Lors du traitement des Données Personnelles, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> prend toutes les mesures raisonnables visant à les protéger contre toute perte,
|
|
||||||
utilisation détournée, accès non autorisé, divulgation, altération ou destruction.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
8. Liens hypertextes « cookies » et balises (“tags”) internet
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Le site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> contient un certain nombre de liens hypertextes vers d’autres sites,
|
|
||||||
mis en place avec l’autorisation de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>. Cependant, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> n’a pas la possibilité de vérifier
|
|
||||||
le contenu des sites ainsi visités, et n’assumera en conséquence aucune responsabilité de ce fait.
|
|
||||||
Sauf si vous décidez de désactiver les cookies, vous acceptez que le site puisse les utiliser.
|
|
||||||
Vous pouvez à tout moment désactiver ces cookies et ce gratuitement à partir des possibilités de désactivation qui vous sont offertes et rappelées ci-après,
|
|
||||||
sachant que cela peut réduire ou empêcher l’accessibilité à tout ou partie des Services proposés par le site.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
8.1 « COOKIES »
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Un « cookie » est un petit fichier d’information envoyé sur le navigateur de l’Utilisateur et enregistré au sein du terminal de l’Utilisateur (ex : ordinateur, smartphone),
|
|
||||||
(ci-après « Cookies »). Ce fichier comprend des informations telles que le nom de domaine de l’Utilisateur, le fournisseur d’accès Internet de l’Utilisateur,
|
|
||||||
le système d’exploitation de l’Utilisateur, ainsi que la date et l’heure d’accès. Les Cookies ne risquent en aucun cas d’endommager le terminal de l’Utilisateur.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
<Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> est susceptible de traiter les informations de l’Utilisateur concernant sa visite du Site, telles que les pages consultées,
|
|
||||||
les recherches effectuées. Ces informations permettent à <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> d’améliorer le contenu du Site, de la navigation de l’Utilisateur.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Les Cookies facilitant la navigation et/ou la fourniture des services proposés par le Site, l’Utilisateur peut configurer son navigateur pour qu’il lui permette de décider s’il souhaite
|
|
||||||
ou non les accepter de manière à ce que des Cookies soient enregistrés dans le terminal ou, au contraire, qu’ils soient rejetés, soit systématiquement, soit selon leur émetteur.
|
|
||||||
L’Utilisateur peut également configurer son logiciel de navigation de manière à ce que l’acceptation ou le refus des Cookies lui soient proposés ponctuellement, avant qu’un Cookie soit
|
|
||||||
susceptible d’être enregistré dans son terminal. <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> informe l’Utilisateur que, dans ce cas, il se peut que les fonctionnalités de son logiciel
|
|
||||||
de navigation ne soient pas toutes disponibles.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Si l’Utilisateur refuse l’enregistrement de Cookies dans son terminal ou son navigateur, ou si l’Utilisateur supprime ceux qui y sont enregistrés,
|
|
||||||
l’Utilisateur est informé que sa navigation et son expérience sur le Site peuvent être limitées. Cela pourrait également être le cas lorsque <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> ne peut pas reconnaître,
|
|
||||||
à des fins de compatibilité technique, le type de navigateur utilisé par le terminal, les paramètres de langue et d’affichage ou le pays depuis lequel le terminal semble connecté à Internet.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Le cas échéant, <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> décline toute responsabilité pour les conséquences liées au fonctionnement dégradé du Site
|
|
||||||
et des services éventuellement proposés par <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>, résultant du refus de Cookies par l’Utilisateur de l’impossibilité
|
|
||||||
pour <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> d’enregistrer ou de consulter les Cookies nécessaires à leur fonctionnement du fait du choix de l’Utilisateur.
|
|
||||||
Pour la gestion des Cookies et des choix de l’Utilisateur, la configuration de chaque navigateur est différente. Elle est décrite dans le menu d’aide du navigateur,
|
|
||||||
qui permettra de savoir de quelle manière l’Utilisateur peut modifier ses souhaits en matière de Cookies.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Enfin, en cliquant sur l’icône dédiée au réseau social Twitter, figurant sur le Site de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> ou dans son application mobile
|
|
||||||
et si l’Utilisateur a accepté le dépôt de cookies en poursuivant sa navigation sur le Site Internet ou l’application mobile de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>, Twitter,
|
|
||||||
peut également déposer des cookies sur vos terminaux (ordinateur, tablette, téléphone portable).
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Ces types de cookies ne sont déposés sur vos terminaux qu’à condition que vous y consentiez, en continuant votre navigation sur le Site Internet ou
|
|
||||||
l’application mobile de <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link>. À tout moment, l’Utilisateur peut néanmoins revenir sur son consentement à ce
|
|
||||||
que <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> dépose ce type de cookies.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
9. Les dons
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Le site dispose d’une page permettant de faire des dons à « ORGANISATION KA INTERNATIONALE » de manière sécurisée.
|
|
||||||
Pour se faire, il faut se rendre sur la page « <Link href='/soutyen'><strong>soutyen</strong></Link> ». L’utilisateur a 2 onglets disponibles :
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
LIBERAPAY / PAYPAL
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
CARTE BANCAIRE
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
9.1 LIBERAPAY / PAYPAL
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
La plateforme <Link target='_blank' rel='noreferrer' href='https://liberapay.com/OKi/donate'><strong>Liberapay</strong></Link> est mise à disposition. Elle permet non seulement de personnaliser le montant, mais aussi la récurrence des dons. C’est à dire qu’avec cet outil, l’utilisateur peut effectuer des dons hebdomadaires, mensuels ou annuels.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
L’utilisateur peut faire un don en passant par la plateforme <Link target='_blank' rel='noreferrer' href='https://www.paypal.com/donate/?hosted_button_id=5Q3KPR79CAZVW'><strong>PayPal</strong></Link>. En cliquant sur le bouton prévu à cet effet, il est redirigé vers la page de don associée à ORGANISATION KA INTERNATIONALE. Il peut ensuite choisir un montant et valider le don en utilisant son compte <Link target='_blank' rel='noreferrer' href='https://www.paypal.com/donate/?hosted_button_id=5Q3KPR79CAZVW'><strong>PayPal</strong></Link> ou une carte bancaire s’il n’est pas enregistré.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h6' component='h3'>
|
|
||||||
9.2 CARTE BANCAIRE
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
En cliquant sur le bouton « CARTE BANCAIRE », l’utilisateur se voit proposer plusieurs choix. Parmi ces derniers, deux options sont possibles :
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<strong>Ponctuel</strong> (1€, 5€, 10€, 20€, 50€, Montant personnalisé)
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<strong>Mensuel</strong> (1€ / mois, 5€ / mois, 10€ / mois, 20€ / mois, 50€ / mois)
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Les dons peuvent donc être fait une seule fois ou de manière récurrente.
|
|
||||||
Quelque soit le choix, l’utilisateur est redirigé vers le sous-domaine don.o-k-i.net suivi de l’ID du montant choisit. Cette URL est gérée par la plateforme de paiement sécurisée <Link href='https://stripe.com/' target='_blank' rel='noopener noreferrer'> <strong>Stripe</strong></Link>.
|
|
||||||
|
|
||||||
Il est important de noter qu’<strong>aucune information de paiement n’est stockée sur notre site internet</strong>.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
10. Propriété intellectuelle et licence
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' gutterBottom variant='body1'>
|
|
||||||
Le site internet <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> et les éléments qui y sont accessibles
|
|
||||||
(textes, images, graphismes, logos, vidéos, icônes, sons, etc.) sont, sauf mention contraire,
|
|
||||||
mis à disposition sous la licence <Link href='https://www.gnu.org/licenses/agpl-3.0.html' target='_blank' rel='noopener noreferrer'> <strong>GNU Affero General Public License Version 3 (AGPL-3.0)</strong></Link>. Cette licence garantit aux utilisateurs les libertés suivantes :
|
|
||||||
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
Liberté d’exécuter le programme pour tous les usages,
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
Liberté d’étudier le fonctionnement du programme et de l’adapter à vos besoins,
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
Liberté de redistribuer des copies du programme,
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ArrowRightAltIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
Liberté d’améliorer le programme et de publier vos modifications.
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
|
|
||||||
Conformément aux exigences de la licence AGPL-3.0, si vous redistribuez ou modifiez des éléments du site,
|
|
||||||
vous devez également fournir le code source complet, incluant vos modifications, sous la même licence.
|
|
||||||
Vous pouvez consulter les termes détaillés de la licence AGPL-3.0 sur le site officiel de la <Link href='https://www.gnu.org/licenses/agpl-3.0.html' target='_blank' rel='noopener noreferrer'> <strong>Free Software Foundation</strong></Link>.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='h5' component='h2'>
|
|
||||||
11. Droit applicable et attribution de juridiction
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography paragraph='true' variant='body1'>
|
|
||||||
Tout litige en relation avec l’utilisation du site <Link href='https://pawol.nu'><strong>pawol.nu</strong></Link> est soumis au droit français.
|
|
||||||
En dehors des cas où la loi ne le permet pas, il est fait attribution exclusive de juridiction aux tribunaux compétents.
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Typography gutterBottom variant='caption' >
|
|
||||||
Dernières modifications le 26/06/2026
|
|
||||||
</Typography>
|
|
||||||
</Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import {useCallback, useEffect, useState, forwardRef} from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import axios from 'axios'
|
|
||||||
import {
|
|
||||||
TextField,
|
|
||||||
Container,
|
|
||||||
Button,
|
|
||||||
Snackbar,
|
|
||||||
LinearProgress,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material'
|
|
||||||
import MuiAlert from '@mui/material/Alert'
|
|
||||||
|
|
||||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:1337/api'
|
|
||||||
|
|
||||||
const Alert = forwardRef(function Alert(props, ref) {
|
|
||||||
return <MuiAlert ref={ref} elevation={6} variant='filled' {...props} />
|
|
||||||
})
|
|
||||||
function EkriKomante({session, paroleId, meteEsKomanteOuve}) {
|
|
||||||
const {jwt, user} = session
|
|
||||||
const [komante, meteKomante] = useState({kontni: ''})
|
|
||||||
const [ere, meteEre] = useState('')
|
|
||||||
const [sikse, meteSikse] = useState('')
|
|
||||||
const [chaje, meteChaje] = useState(false)
|
|
||||||
const [esOuve, meteEsOuve] = useState(false)
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
meteChaje(true)
|
|
||||||
const {kontni} = komante
|
|
||||||
|
|
||||||
if (kontni === '') {
|
|
||||||
meteEre({error: {message: 'Champ obligatoire'}})
|
|
||||||
meteChaje(false)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
Authorization: `Bearer ${jwt}`
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await axios.post(`${API_URL}/commentaires`, {
|
|
||||||
data: {
|
|
||||||
contenu: kontni,
|
|
||||||
parole: paroleId,
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email
|
|
||||||
},
|
|
||||||
datePublication: new Date()
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
headers
|
|
||||||
})
|
|
||||||
|
|
||||||
meteSikse('Commentaire envoyé avec succès. Il apparaîtra sur le site après validation.')
|
|
||||||
meteChaje(false)
|
|
||||||
} catch (error) {
|
|
||||||
meteEre(error?.response?.data)
|
|
||||||
meteChaje(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleUpdate = useCallback(update => {
|
|
||||||
meteKomante({...komante, ...update})
|
|
||||||
}, [komante])
|
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
meteEsOuve(false)
|
|
||||||
meteSikse('')
|
|
||||||
meteEre('')
|
|
||||||
meteEsKomanteOuve(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
meteKomante({kontni: ''})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (sikse) {
|
|
||||||
meteEsOuve(true)
|
|
||||||
handleReset()
|
|
||||||
}
|
|
||||||
}, [sikse])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ere) {
|
|
||||||
meteEsOuve(true)
|
|
||||||
}
|
|
||||||
}, [ere])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container maxWidth='sm'>
|
|
||||||
<form noValidate autoComplete='off'>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
multiline
|
|
||||||
required
|
|
||||||
helperText='*Obligatoire. 500 caractères maximum'
|
|
||||||
style={{marginTop: '1em'}}
|
|
||||||
id='komante'
|
|
||||||
label='Commentaire'
|
|
||||||
value={komante.kontni}
|
|
||||||
rows={8}
|
|
||||||
variant='outlined'
|
|
||||||
onChange={event => handleUpdate({kontni: event.target.value})}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
disabled={chaje || komante.komante === ''}
|
|
||||||
style={{marginBlock: 20}}
|
|
||||||
variant='contained'
|
|
||||||
color='primary'
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Valider
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
{chaje && <LinearProgress size={24} style={{width: '100%', marginBlock: '1em'}} />}
|
|
||||||
</div>
|
|
||||||
{sikse && (
|
|
||||||
<Snackbar open={esOuve} autoHideDuration={10_000} onClose={handleClose}>
|
|
||||||
<Alert severity='success' onClose={handleClose}>
|
|
||||||
<strong>{sikse}</strong>
|
|
||||||
</Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
{ere && (
|
|
||||||
<Snackbar open={esOuve} autoHideDuration={10_000} onClose={handleClose}>
|
|
||||||
<Alert severity='error' onClose={handleClose}>
|
|
||||||
<strong>Une erreur s’est produite</strong> : <i>{ere?.error?.message}</i>
|
|
||||||
</Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
EkriKomante.propTypes = {
|
|
||||||
session: PropTypes.object.isRequired,
|
|
||||||
paroleId: PropTypes.number.isRequired,
|
|
||||||
meteEsKomanteOuve: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EkriKomante
|
|
||||||
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import {format} from 'date-fns'
|
|
||||||
import {fr} from 'date-fns/locale'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
|
|
||||||
import {
|
|
||||||
Typography,
|
|
||||||
Divider,
|
|
||||||
List,
|
|
||||||
ListItemText
|
|
||||||
} from '@mui/material'
|
|
||||||
|
|
||||||
import {formatJsonString} from '../../lib/utils/format'
|
|
||||||
|
|
||||||
const PREFIX = 'komante-list'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
root: `${PREFIX}-root`,
|
|
||||||
inline: `${PREFIX}-inline`
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledList = styled(List)((
|
|
||||||
{
|
|
||||||
theme
|
|
||||||
}
|
|
||||||
) => ({
|
|
||||||
[`&.${classes.root}`]: {
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '36ch',
|
|
||||||
backgroundColor: theme.palette.background.paper
|
|
||||||
},
|
|
||||||
|
|
||||||
[`& .${classes.inline}`]: {
|
|
||||||
display: 'inline'
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
export default function KomanteList({commentaires}) {
|
|
||||||
return (
|
|
||||||
<StyledList className={classes.root}>
|
|
||||||
{commentaires.map(({id, user, contenu, datePublication}) => (
|
|
||||||
<div key={id}>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography gutterBottom style={{textDecoration: 'underline'}} variant='body1'>
|
|
||||||
<small>{user.username}</small>
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ListItemText
|
|
||||||
primary={formatJsonString(contenu)}
|
|
||||||
secondary={
|
|
||||||
<Typography gutterBottom style={{marginBlock: 5, fontStyle: 'italic'}} variant='caption' display='block'>
|
|
||||||
{format(new Date(datePublication), 'Pp', {locale: fr})}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider style={{marginBottom: '1em'}} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</StyledList>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
KomanteList.propTypes = {
|
|
||||||
commentaires: PropTypes.array.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
import {useState, useEffect, useRef} from 'react'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {useSession} from 'next-auth/react'
|
|
||||||
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
DialogActions,
|
|
||||||
Button,
|
|
||||||
Typography,
|
|
||||||
Badge
|
|
||||||
} from '@mui/material'
|
|
||||||
import {useRouter} from 'next/navigation'
|
|
||||||
import Koneksyon from '../sesyon/koneksyon'
|
|
||||||
import KomanteList from './komante-list'
|
|
||||||
import EkriKomante from './ekri-komante'
|
|
||||||
|
|
||||||
const PREFIX = 'vwe-komante'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
tooltip: `${PREFIX}-tooltip`,
|
|
||||||
margin: `${PREFIX}-margin`,
|
|
||||||
extendedIcon: `${PREFIX}-extendedIcon`
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = styled('div')((
|
|
||||||
{
|
|
||||||
theme
|
|
||||||
}
|
|
||||||
) => ({
|
|
||||||
[`& .${classes.margin}`]: {
|
|
||||||
margin: theme.spacing(1)
|
|
||||||
},
|
|
||||||
|
|
||||||
[`& .${classes.extendedIcon}`]: {
|
|
||||||
marginRight: theme.spacing(1)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
export default function VweKomante({commentaires, parole, paroleId}) {
|
|
||||||
const [esOuve, meteEsOuve] = useState(false)
|
|
||||||
const [esKoneksyonOuve, meteEsKoneksyonOuve] = useState(false)
|
|
||||||
const [esKomenteOuve, meteEsKomanteOuve] = useState(false)
|
|
||||||
const {data: session} = useSession()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
meteEsOuve(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
meteEsOuve(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const descriptionElementRef = useRef(null)
|
|
||||||
useEffect(() => {
|
|
||||||
if (esOuve) {
|
|
||||||
const {current: descriptionElement} = descriptionElementRef
|
|
||||||
if (descriptionElement !== null) {
|
|
||||||
descriptionElement.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [esOuve])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Root>
|
|
||||||
<Badge badgeContent={commentaires.length} color='primary'>
|
|
||||||
<Button sx={{marginBlock: 3}} variant='outlined' onClick={handleClick}>
|
|
||||||
Voir les commentaires
|
|
||||||
</Button>
|
|
||||||
</Badge>
|
|
||||||
<Dialog
|
|
||||||
open={esOuve}
|
|
||||||
scroll='paper'
|
|
||||||
aria-labelledby='scroll-dialog-title'
|
|
||||||
aria-describedby='scroll-dialog-description'
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<DialogTitle align='center' id='scroll-dialog-title'>Commentaires</DialogTitle>
|
|
||||||
<DialogContent dividers>
|
|
||||||
<Typography
|
|
||||||
ref={descriptionElementRef}
|
|
||||||
variant='inherit'
|
|
||||||
id='scroll-dialog-description'
|
|
||||||
tabIndex={-1}
|
|
||||||
component='div'
|
|
||||||
>
|
|
||||||
{commentaires.length > 0 ? (
|
|
||||||
<KomanteList commentaires={commentaires} />
|
|
||||||
) : (
|
|
||||||
<Typography component='div' align='center'>
|
|
||||||
Aucun commentaire
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions align='center' >
|
|
||||||
{session && session.user && !esKomenteOuve && (
|
|
||||||
<Button fullWidth color='primary' onClick={() => meteEsKomanteOuve(true)}>
|
|
||||||
Ajouter un commentaire
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!session && !esKoneksyonOuve && (
|
|
||||||
<Button fullWidth color='primary' onClick={() => meteEsKoneksyonOuve(true)}>
|
|
||||||
Se connecter pour commenter
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!session && esKoneksyonOuve && (
|
|
||||||
<Koneksyon chimen={router.asPath} />
|
|
||||||
)}
|
|
||||||
</DialogActions>
|
|
||||||
{!session && esKoneksyonOuve && (
|
|
||||||
<Button style={{marginBlock: 10}} color='secondary' onClick={() => meteEsKoneksyonOuve(false)}>
|
|
||||||
<strong>Annuler</strong>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{session && session.user && esKomenteOuve && (
|
|
||||||
<>
|
|
||||||
<EkriKomante session={session} parole={parole} paroleId={paroleId} meteEsKomanteOuve={meteEsKomanteOuve} />
|
|
||||||
<Button fullWidth style={{marginBottom: 10}} color='primary' onClick={() => meteEsKomanteOuve(false)}>
|
|
||||||
Fermer
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Dialog>
|
|
||||||
</Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
VweKomante.propTypes = {
|
|
||||||
commentaires: PropTypes.array,
|
|
||||||
parole: PropTypes.object.isRequired,
|
|
||||||
paroleId: PropTypes.number.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
import {useState, forwardRef} from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import axios from 'axios'
|
|
||||||
import {useRouter} from 'next/navigation'
|
|
||||||
|
|
||||||
import {FormControl, Snackbar, IconButton, Button, Input, InputAdornment, InputLabel, Box, Container, Typography, LinearProgress} from '@mui/material'
|
|
||||||
import MuiAlert from '@mui/material/Alert'
|
|
||||||
import Visibility from '@mui/icons-material/Visibility'
|
|
||||||
import VisibilityOff from '@mui/icons-material/VisibilityOff'
|
|
||||||
import Link from '@mui/material/Link'
|
|
||||||
|
|
||||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:1337'
|
|
||||||
|
|
||||||
const Alert = forwardRef(function Alert(props, ref) {
|
|
||||||
return <MuiAlert ref={ref} elevation={6} variant='filled' {...props} />
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function NewPassword({code}) {
|
|
||||||
const router = useRouter()
|
|
||||||
const [showPassword, setShowPassword] = useState('')
|
|
||||||
const [showPasswordConfirmation, setShowPasswordConfirmation] = useState('')
|
|
||||||
const [password, setPassword] = useState('')
|
|
||||||
const [passwordConfirmation, setPasswordConfirmation] = useState('')
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [error, setError] = useState('')
|
|
||||||
const [open, setOpen] = useState(true)
|
|
||||||
const [passwordSuccess, setPasswordSuccess] = useState(false)
|
|
||||||
|
|
||||||
const resetForm = () => {
|
|
||||||
setPassword('')
|
|
||||||
setPasswordConfirmation('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const changePassword = async () => {
|
|
||||||
setLoading(true)
|
|
||||||
try {
|
|
||||||
await axios.post(`${API_URL}/auth/reset-password`, {
|
|
||||||
code,
|
|
||||||
password,
|
|
||||||
passwordConfirmation
|
|
||||||
})
|
|
||||||
setLoading(false)
|
|
||||||
setPasswordSuccess(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
router.push('/pwopose')
|
|
||||||
}, 10_000)
|
|
||||||
} catch {
|
|
||||||
setOpen(true)
|
|
||||||
setError('Une erreur s’est produite. Veuillez réessayer ultérieurement')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDownPassword = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDownPasswordConfirmation = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyUpPassword = event => {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
handleClick()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyUpPasswordConfirmation = event => {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
handleClick()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
if (password !== passwordConfirmation) {
|
|
||||||
setOpen(true)
|
|
||||||
setError('Les 2 mots de passe de correspondent pas')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 6) {
|
|
||||||
setOpen(true)
|
|
||||||
setError('Le mot de passe est trop court, 6 caratères minimum')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await changePassword()
|
|
||||||
setLoading(false)
|
|
||||||
resetForm()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(false)
|
|
||||||
setError('')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container sx={{marginTop: 2}} maxWidth='sm'>
|
|
||||||
<Box sx={{width: '100%'}}>
|
|
||||||
<Typography sx={{textAlign: 'center'}} variant='h5' component='div'>Nouveau mot de passe</Typography>
|
|
||||||
<FormControl fullWidth style={{marginTop: '1em'}}>
|
|
||||||
<InputLabel htmlFor='password'>Mot de passe</InputLabel>
|
|
||||||
<Input
|
|
||||||
value={password}
|
|
||||||
name='register-password'
|
|
||||||
type={showPassword ? 'text' : 'password'}
|
|
||||||
id='register-password'
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end'>
|
|
||||||
<IconButton
|
|
||||||
aria-label='password visibility'
|
|
||||||
size='large'
|
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
|
||||||
onMouseDown={handleMouseDownPassword}
|
|
||||||
>
|
|
||||||
{showPassword ? <Visibility /> : <VisibilityOff />}
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onChange={event => setPassword(event.target.value)}
|
|
||||||
onKeyUp={handleKeyUpPassword}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth style={{marginTop: '1em'}}>
|
|
||||||
<InputLabel htmlFor='password'>Vérification du mot de passe</InputLabel>
|
|
||||||
<Input
|
|
||||||
value={passwordConfirmation}
|
|
||||||
name='verification-password'
|
|
||||||
type={showPasswordConfirmation ? 'text' : 'password'}
|
|
||||||
id='verification-password'
|
|
||||||
error={password !== passwordConfirmation}
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end'>
|
|
||||||
<IconButton
|
|
||||||
aria-label='password visibility'
|
|
||||||
size='large'
|
|
||||||
onClick={() => setShowPasswordConfirmation(!showPassword)}
|
|
||||||
onMouseDown={handleMouseDownPasswordConfirmation}
|
|
||||||
>
|
|
||||||
{showPasswordConfirmation ? <Visibility /> : <VisibilityOff />}
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onChange={event => setPasswordConfirmation(event.target.value)}
|
|
||||||
onKeyUp={handleKeyUpPasswordConfirmation}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='contained'
|
|
||||||
color='primary'
|
|
||||||
disabled={loading || password !== passwordConfirmation || password === ''}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Changer de mot de passe
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='outlined'
|
|
||||||
color='primary'
|
|
||||||
onClick={() => router.push('/pwopose')}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Retour à la page de connexion
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{loading && <LinearProgress size={24} style={{width: '100%', marginTop: '1em'}} />}
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<Snackbar
|
|
||||||
open={open}
|
|
||||||
autoHideDuration={6000}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='error' onClose={handleClose}><strong>{error}</strong></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{passwordSuccess && (
|
|
||||||
<Snackbar
|
|
||||||
open={passwordSuccess}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='info' onClose={handleClose}>Votre changement de mot de passe a été pris en compte. Vous serez redirigé vers la page de connexion. <Link underline='hover' color='inherit' href='/pwopose'><strong>Cliquez ici si vous n’êtes pas redirigé vers la page de connexion</strong></Link></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
NewPassword.propTypes = {
|
|
||||||
code: PropTypes.string.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
import {useState} from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import TextField from '@mui/material/TextField'
|
|
||||||
import Dialog from '@mui/material/Dialog'
|
|
||||||
import DialogActions from '@mui/material/DialogActions'
|
|
||||||
import DialogContent from '@mui/material/DialogContent'
|
|
||||||
import DialogContentText from '@mui/material/DialogContentText'
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle'
|
|
||||||
|
|
||||||
import {validateEmail} from '../../lib/utils/emails'
|
|
||||||
import {jwennUserEpiEmail, passwordRequest} from '../../lib/oki-api'
|
|
||||||
|
|
||||||
export default function ResetDialog({lyen, title, activation, content, open, setOpen, setLoading, setError, setSuccess}) {
|
|
||||||
const [email, setEmail] = useState('')
|
|
||||||
|
|
||||||
const forgotPasswordRequest = async () => {
|
|
||||||
setLoading(true)
|
|
||||||
try {
|
|
||||||
await passwordRequest(lyen, email)
|
|
||||||
|
|
||||||
if (activation) {
|
|
||||||
const user = await jwennUserEpiEmail(email)
|
|
||||||
localStorage.setItem('user-id', user?.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false)
|
|
||||||
setSuccess(true)
|
|
||||||
} catch {
|
|
||||||
setError('Une erreur s’est produite. Veuillez réessayer ultérieurement')
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetFrom = () => {
|
|
||||||
setEmail('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
forgotPasswordRequest()
|
|
||||||
setOpen(false)
|
|
||||||
resetFrom()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Dialog open={open} onClose={handleClose}>
|
|
||||||
<DialogTitle>{title}</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogContentText>
|
|
||||||
{content}
|
|
||||||
</DialogContentText>
|
|
||||||
<TextField
|
|
||||||
autoFocus
|
|
||||||
fullWidth
|
|
||||||
value={email}
|
|
||||||
margin='dense'
|
|
||||||
id='email'
|
|
||||||
label='Adresse e-mail'
|
|
||||||
type='email'
|
|
||||||
variant='standard'
|
|
||||||
onChange={event => setEmail(event.target.value)}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button variant='contained' color='secondary' onClick={handleClose}>Annuler</Button>
|
|
||||||
<Button disabled={!validateEmail(email)} variant='contained' color='primary' onClick={handleClick}>Valider</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetDialog.propTypes = {
|
|
||||||
lyen: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
activation: PropTypes.bool,
|
|
||||||
content: PropTypes.string.isRequired,
|
|
||||||
open: PropTypes.bool.isRequired,
|
|
||||||
setOpen: PropTypes.func.isRequired,
|
|
||||||
setLoading: PropTypes.func.isRequired,
|
|
||||||
setError: PropTypes.func.isRequired,
|
|
||||||
setSuccess: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
import {useState, forwardRef} from 'react'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
import Snackbar from '@mui/material/Snackbar'
|
|
||||||
import {LinearProgress} from '@mui/material'
|
|
||||||
import MuiAlert from '@mui/material/Alert'
|
|
||||||
import ResetDialog from './reset-dialog'
|
|
||||||
|
|
||||||
const Alert = forwardRef(function Alert(props, ref) {
|
|
||||||
return <MuiAlert ref={ref} elevation={6} variant='filled' {...props} />
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function ResetPassword() {
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [error, setError] = useState(false)
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [success, setSuccess] = useState(false)
|
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setError('')
|
|
||||||
setSuccess(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box style={{marginTop: 20}}>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
disabled={loading}
|
|
||||||
variant='outlined'
|
|
||||||
color='warning'
|
|
||||||
onClick={() => setOpen(true)}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Mot de passe oublié
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
{loading && <LinearProgress size={24} style={{width: '100%', marginTop: '1em'}} />}
|
|
||||||
{error && (
|
|
||||||
<Snackbar
|
|
||||||
open={Boolean(error)}
|
|
||||||
autoHideDuration={6000}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='error' onClose={handleClose}><strong>{error}</strong></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
{success && (
|
|
||||||
<Snackbar
|
|
||||||
open={success}
|
|
||||||
autoHideDuration={15_000}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='success' onClose={handleClose}><strong>Email envoyé avec succès. Vous pouvez changer votre mot de passe via le lien qui vous a été envoyé.</strong></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
<ResetDialog
|
|
||||||
lyen='forgot-password'
|
|
||||||
title='Mot de passe oublié'
|
|
||||||
content='Indiquez l’adresse e-mail utilisée lors de votre inscription. Vous receverez des instructions par e-mail.'
|
|
||||||
open={open}
|
|
||||||
setOpen={setOpen}
|
|
||||||
setLoading={setLoading}
|
|
||||||
setError={setError}
|
|
||||||
setSuccess={setSuccess}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import {useState, useEffect, forwardRef} from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {useSession} from 'next-auth/react'
|
|
||||||
import MuiAlert from '@mui/material/Alert'
|
|
||||||
import Snackbar from '@mui/material/Snackbar'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
|
|
||||||
import Koneksyon from '../../components/sesyon/koneksyon'
|
|
||||||
import Dekoneksyon from '../../components/sesyon/dekoneksyon'
|
|
||||||
import EkriTeks from '../../components/soumet/ekri-teks'
|
|
||||||
import Footer from '../../components/footer'
|
|
||||||
|
|
||||||
import {jwennUserEpiToken, jwennUserEpiUsername} from '../../lib/oki-api'
|
|
||||||
import NewPassword from '../../components/password/new-password'
|
|
||||||
import ChwaTeks from '../../components/soumet/chwa-teks'
|
|
||||||
|
|
||||||
const Alert = forwardRef(function Alert(props, ref) {
|
|
||||||
return <MuiAlert ref={ref} elevation={6} variant='filled' {...props} />
|
|
||||||
})
|
|
||||||
|
|
||||||
export default function Pwopose({code}) {
|
|
||||||
const {data: session} = useSession()
|
|
||||||
const [localUsername, setLocalUsername] = useState(null)
|
|
||||||
const [username, setUsername] = useState(null)
|
|
||||||
const [open, setOpen] = useState(true)
|
|
||||||
const [selectedTeks, setSelectedTeks] = useState(null)
|
|
||||||
const [canAutoTranslate, setCanAutoTranslate] = useState(false)
|
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (session?.jwt) {
|
|
||||||
const getUser = async token => {
|
|
||||||
const user = await jwennUserEpiToken(token)
|
|
||||||
setCanAutoTranslate(user.canAutoTranslate)
|
|
||||||
}
|
|
||||||
|
|
||||||
getUser(session.jwt)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (localStorage.getItem('username')) {
|
|
||||||
const username = localStorage.getItem('username')
|
|
||||||
setLocalUsername(username)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (localUsername) {
|
|
||||||
const getUser = async username => {
|
|
||||||
const user = await jwennUserEpiUsername(username)
|
|
||||||
setUsername(user?.username)
|
|
||||||
}
|
|
||||||
|
|
||||||
getUser(localUsername)
|
|
||||||
}
|
|
||||||
}, [localUsername])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (username && localStorage.getItem('username')) {
|
|
||||||
localStorage.removeItem('username')
|
|
||||||
}
|
|
||||||
}, [username])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{display: 'flex', flexDirection: 'column', minHeight: '100vh'}}>
|
|
||||||
<Box sx={{flexGrow: 1, marginTop: 1, marginBottom: 10}}>
|
|
||||||
{!session && !code && (
|
|
||||||
<Koneksyon
|
|
||||||
chimen='/pwopose'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!session && code && (
|
|
||||||
<NewPassword code={code} />
|
|
||||||
)}
|
|
||||||
{session && session.user && (
|
|
||||||
<>
|
|
||||||
<Dekoneksyon position='absolute' top={95} left={5} chimen='/pwopose' />
|
|
||||||
<ChwaTeks selectedTeks={selectedTeks} setSelectedTeks={setSelectedTeks} />
|
|
||||||
<EkriTeks canAutoTranslate={canAutoTranslate} selectedTeks={selectedTeks} setSelectedTeks={setSelectedTeks} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{session && !session.user && (
|
|
||||||
<Dekoneksyon position='absolute' top={95} left={5} chimen='/pwopose' />
|
|
||||||
)}
|
|
||||||
{username && (
|
|
||||||
<Snackbar
|
|
||||||
open={open}
|
|
||||||
autoHideDuration={10_000}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='success' onClose={handleClose}><strong>Bonjour {username}, votre compte a été activé avec succès. Vous pouvez vous connecter.</strong></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Footer />
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Pwopose.propTypes = {
|
|
||||||
code: PropTypes.string
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import {useState} from 'react'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {signOut} from 'next-auth/react'
|
|
||||||
import {Tooltip, Fab, Zoom} from '@mui/material'
|
|
||||||
import ExitToAppIcon from '@mui/icons-material/ExitToApp'
|
|
||||||
|
|
||||||
import Alet from '../alet'
|
|
||||||
|
|
||||||
const PREFIX = 'Dekoneksyon'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
tooltip: `${PREFIX}-tooltip`,
|
|
||||||
dekoneksyon: `${PREFIX}-dekoneksyon`
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = styled('div')(() => ({
|
|
||||||
[`& .${classes.dekoneksyon}`]: {
|
|
||||||
position: props => props.position,
|
|
||||||
top: props => props.top,
|
|
||||||
left: props => props.left,
|
|
||||||
zIndex: 0
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
const DekoneksonTooltip = Tooltip
|
|
||||||
|
|
||||||
function Dekoneksyon({tooltipPlacement}) {
|
|
||||||
const [esOuve, meteEsOuve] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
(
|
|
||||||
<Root>
|
|
||||||
<DekoneksonTooltip
|
|
||||||
title='Se déconnecter'
|
|
||||||
placement={tooltipPlacement}
|
|
||||||
TransitionComponent={Zoom}
|
|
||||||
classes={{
|
|
||||||
tooltip: classes.tooltip
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Fab
|
|
||||||
className={classes.dekoneksyon}
|
|
||||||
color='error'
|
|
||||||
aria-label='logout'
|
|
||||||
size='small'
|
|
||||||
onClick={() => meteEsOuve(true)}
|
|
||||||
>
|
|
||||||
<ExitToAppIcon />
|
|
||||||
</Fab>
|
|
||||||
</DekoneksonTooltip>
|
|
||||||
<Alet esOuve={esOuve} meteEsOuve={meteEsOuve} handleKonfime={() => signOut()} />
|
|
||||||
</Root>
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Dekoneksyon.propTypes = {
|
|
||||||
tooltipPlacement: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Dekoneksyon
|
|
||||||
@@ -1,459 +0,0 @@
|
|||||||
import {useEffect, useState, forwardRef} from 'react'
|
|
||||||
import {signIn} from 'next-auth/react'
|
|
||||||
import {useRouter} from 'next/navigation'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import Container from '@mui/material/Container'
|
|
||||||
import FormControl from '@mui/material/FormControl'
|
|
||||||
import IconButton from '@mui/material/IconButton'
|
|
||||||
import Input from '@mui/material/Input'
|
|
||||||
import InputAdornment from '@mui/material/InputAdornment'
|
|
||||||
import InputLabel from '@mui/material/InputLabel'
|
|
||||||
import LinearProgress from '@mui/material/LinearProgress'
|
|
||||||
import Snackbar from '@mui/material/Snackbar'
|
|
||||||
import Tab from '@mui/material/Tab'
|
|
||||||
import Tabs from '@mui/material/Tabs'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import Grid from '@mui/material/Grid'
|
|
||||||
import Visibility from '@mui/icons-material/Visibility'
|
|
||||||
import VisibilityOff from '@mui/icons-material/VisibilityOff'
|
|
||||||
import MuiAlert from '@mui/material/Alert'
|
|
||||||
import LoginIcon from '@mui/icons-material/Login'
|
|
||||||
import AppRegistrationRoundedIcon from '@mui/icons-material/AppRegistrationRounded'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
import {validateEmail} from '../../lib/utils/emails'
|
|
||||||
import ResetPassword from '../password/reset-password'
|
|
||||||
import ResetDialog from '../password/reset-dialog'
|
|
||||||
import LoginProvider from './login-provider'
|
|
||||||
|
|
||||||
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3001'
|
|
||||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:1337'
|
|
||||||
|
|
||||||
const PROVIDERS = [
|
|
||||||
{
|
|
||||||
id: 'google',
|
|
||||||
title: 'Google',
|
|
||||||
width: 16,
|
|
||||||
height: 16
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'twitter',
|
|
||||||
title: 'Twitter',
|
|
||||||
width: 16,
|
|
||||||
height: 13
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
function TabPanel(props) {
|
|
||||||
const {children, value, index, ...other} = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role='tabpanel'
|
|
||||||
hidden={value !== index}
|
|
||||||
id={`simple-tabpanel-${index}`}
|
|
||||||
aria-labelledby={`simple-tab-${index}`}
|
|
||||||
{...other}
|
|
||||||
>
|
|
||||||
{value === index && (
|
|
||||||
<Box sx={{p: 3}}>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
TabPanel.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
index: PropTypes.number.isRequired,
|
|
||||||
value: PropTypes.number.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
function a11yProps(index) {
|
|
||||||
return {
|
|
||||||
id: `simple-tab-${index}`,
|
|
||||||
'aria-controls': `simple-tabpanel-${index}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Alert = forwardRef(function Alert(props, ref) {
|
|
||||||
return <MuiAlert ref={ref} elevation={6} variant='filled' {...props} />
|
|
||||||
})
|
|
||||||
|
|
||||||
function Koneksyon({chimen}) {
|
|
||||||
const [error, setError] = useState('')
|
|
||||||
const [loginCredentials, setLoginCredentials] = useState({username: '', password: ''})
|
|
||||||
const [registerCredentials, setRegisterCredentials] = useState({username: '', email: '', password: ''})
|
|
||||||
const [passwordVerification, setPasswordVerification] = useState('')
|
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
|
||||||
const [showRegisterPassword, setShowRegisterPassword] = useState(false)
|
|
||||||
const [showVerificationPassword, setShowVerificationPassword] = useState(false)
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [open, setOpen] = useState(true)
|
|
||||||
const [success, setSuccess] = useState(false)
|
|
||||||
const [openDialog, setOpenDialog] = useState(false)
|
|
||||||
const [value, setValue] = useState(0)
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const handleUpdate = update => {
|
|
||||||
setLoginCredentials({...loginCredentials, ...update})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRegisterUpdate = update => {
|
|
||||||
setRegisterCredentials({...registerCredentials, ...update})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChange = (event, newValue) => {
|
|
||||||
setValue(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
const register = async () => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${apiUrl}/auth/local/register`, {
|
|
||||||
...registerCredentials
|
|
||||||
}, {
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
localStorage.setItem('username', `${response?.data?.user?.username}`)
|
|
||||||
setSuccess(true)
|
|
||||||
resetRegisterForm()
|
|
||||||
} catch (error_) {
|
|
||||||
if (error_.message.endsWith(400)) {
|
|
||||||
setError('E-mail ou utilisateur déjà enregistré')
|
|
||||||
} else {
|
|
||||||
setError('Une erreur s’est produite')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetRegisterForm = () => {
|
|
||||||
setRegisterCredentials({username: '', email: '', password: ''})
|
|
||||||
setPasswordVerification('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClickLogin = async () => {
|
|
||||||
if (!validateEmail(loginCredentials.username) || loginCredentials.password === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true)
|
|
||||||
const response = await signIn('credentials', {
|
|
||||||
callbackUrl: `${siteUrl}${chimen}`,
|
|
||||||
redirect: false,
|
|
||||||
...loginCredentials
|
|
||||||
})
|
|
||||||
if (response.error) {
|
|
||||||
setError(response.error)
|
|
||||||
setLoading(false)
|
|
||||||
} else if (response.ok) {
|
|
||||||
setLoading(false)
|
|
||||||
router.push(chimen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClickRegister = async () => {
|
|
||||||
setLoading(true)
|
|
||||||
if (!validateEmail(registerCredentials.email) || registerCredentials.password === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (registerCredentials.password !== passwordVerification) {
|
|
||||||
setError('Les 2 mots de passe de correspondent pas')
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (registerCredentials.username.length < 3) {
|
|
||||||
setError('Le nom d’utilisateur est trop court, 3 caratères minimum')
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (registerCredentials.password.length < 6) {
|
|
||||||
setError('Le mot de passe est trop court, 6 caratères minimum')
|
|
||||||
setLoading(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await register()
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(false)
|
|
||||||
setSuccess(false)
|
|
||||||
setError('')
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
setOpen(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}, [error])
|
|
||||||
|
|
||||||
const handleClickShowPassword = () => {
|
|
||||||
setShowPassword(!showPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClickShowRegisterPassword = () => {
|
|
||||||
setShowRegisterPassword(!showRegisterPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClickShowVerificationPassword = () => {
|
|
||||||
setShowVerificationPassword(!showVerificationPassword)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDownPassword = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDownRegisterPassword = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDownVerificationPassword = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyUpLogin = event => {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
handleClickLogin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyUpRegister = event => {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
handleClickRegister()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container sx={{marginTop: 6}} maxWidth='sm'>
|
|
||||||
|
|
||||||
<Box sx={{width: '100%'}}>
|
|
||||||
<Tabs centered value={value} aria-label='basic tabs example' onChange={handleChange}>
|
|
||||||
<Tab icon={<LoginIcon />} label='Se connecter' {...a11yProps(0)} />
|
|
||||||
<Tab icon={<AppRegistrationRoundedIcon />} label='S’inscrire' {...a11yProps(1)} />
|
|
||||||
</Tabs>
|
|
||||||
<TabPanel value={value} index={0}>
|
|
||||||
|
|
||||||
<Box sx={{textAlign: 'center', marginBottom: 3}}>
|
|
||||||
<Typography gutterBottom textalign='center'>Connectez-vous avec</Typography>
|
|
||||||
<Grid sx={{justifyContent: 'center'}} container columnSpacing={{xs: 1, sm: 2, md: 3}}>
|
|
||||||
{PROVIDERS.map(({id, title, width, height}) => (
|
|
||||||
<Grid sx={{marginTop: 1}} key={id}>
|
|
||||||
<LoginProvider id={id} title={title} width={width} height={height} callbackUrl={`${siteUrl}${chimen}`} />
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box sx={{textAlign: 'center', marginBottom: 3}}>
|
|
||||||
<Typography>ou utilisez votre e-mail pour vous identifier</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<FormControl fullWidth autoComplete='off'>
|
|
||||||
<InputLabel htmlFor='username'>E-mail</InputLabel>
|
|
||||||
<Input
|
|
||||||
autoComplete='new-password'
|
|
||||||
value={loginCredentials.username}
|
|
||||||
name='username'
|
|
||||||
type='email'
|
|
||||||
id='email'
|
|
||||||
onChange={event => handleUpdate({username: event.target.value})}
|
|
||||||
onKeyUp={handleKeyUpLogin}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl fullWidth style={{marginTop: '1em'}}>
|
|
||||||
<InputLabel htmlFor='password'>Mot de passe</InputLabel>
|
|
||||||
<Input
|
|
||||||
autoComplete='new-password'
|
|
||||||
value={loginCredentials.password}
|
|
||||||
name='password'
|
|
||||||
type={showPassword ? 'text' : 'password'}
|
|
||||||
id='password'
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end'>
|
|
||||||
<IconButton
|
|
||||||
aria-label='password visibility'
|
|
||||||
size='large'
|
|
||||||
onClick={handleClickShowPassword}
|
|
||||||
onMouseDown={handleMouseDownPassword}
|
|
||||||
>
|
|
||||||
{showPassword ? <Visibility /> : <VisibilityOff />}
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onChange={event => handleUpdate({password: event.target.value})}
|
|
||||||
onKeyUp={handleKeyUpLogin}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
disabled={loading || !validateEmail(loginCredentials.username) || loginCredentials.password === ''}
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='contained'
|
|
||||||
color='primary'
|
|
||||||
onClick={handleClickLogin}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Connexion
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
<ResetPassword />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={value} index={1}>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<InputLabel htmlFor='register-email'>E-mail</InputLabel>
|
|
||||||
<Input
|
|
||||||
autoComplete='email'
|
|
||||||
value={registerCredentials.email}
|
|
||||||
name='register-email'
|
|
||||||
type='email'
|
|
||||||
id='register-email'
|
|
||||||
onChange={event => handleRegisterUpdate({email: event.target.value})}
|
|
||||||
onKeyUp={handleKeyUpRegister}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth style={{marginTop: '1em'}}>
|
|
||||||
<InputLabel htmlFor='register-username'>Nom d’utilisateur</InputLabel>
|
|
||||||
<Input
|
|
||||||
autoComplete='username'
|
|
||||||
value={registerCredentials.username}
|
|
||||||
name='register-username'
|
|
||||||
type='text'
|
|
||||||
id='register-username'
|
|
||||||
onChange={event => handleRegisterUpdate({username: event.target.value})}
|
|
||||||
onKeyUp={handleKeyUpRegister}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth style={{marginTop: '1em'}}>
|
|
||||||
<InputLabel htmlFor='password'>Mot de passe</InputLabel>
|
|
||||||
<Input
|
|
||||||
autoComplete='new-password' // Disable auto complete
|
|
||||||
value={registerCredentials.password}
|
|
||||||
name='register-password'
|
|
||||||
type={showRegisterPassword ? 'text' : 'password'}
|
|
||||||
id='register-password'
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end'>
|
|
||||||
<IconButton
|
|
||||||
aria-label='password visibility'
|
|
||||||
size='large'
|
|
||||||
onClick={handleClickShowRegisterPassword}
|
|
||||||
onMouseDown={handleMouseDownRegisterPassword}
|
|
||||||
>
|
|
||||||
{showRegisterPassword ? <Visibility /> : <VisibilityOff />}
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onChange={event => handleRegisterUpdate({password: event.target.value})}
|
|
||||||
onKeyUp={handleKeyUpRegister}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth style={{marginTop: '1em'}}>
|
|
||||||
<InputLabel htmlFor='password'>Vérification du mot de passe</InputLabel>
|
|
||||||
<Input
|
|
||||||
value={passwordVerification}
|
|
||||||
name='verification-password'
|
|
||||||
type={showVerificationPassword ? 'text' : 'password'}
|
|
||||||
id='verification-password'
|
|
||||||
error={registerCredentials.password !== passwordVerification}
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end'>
|
|
||||||
<IconButton
|
|
||||||
aria-label='password visibility'
|
|
||||||
size='large'
|
|
||||||
onClick={handleClickShowVerificationPassword}
|
|
||||||
onMouseDown={handleMouseDownVerificationPassword}
|
|
||||||
>
|
|
||||||
{showVerificationPassword ? <Visibility /> : <VisibilityOff />}
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onChange={event => setPasswordVerification(event.target.value)}
|
|
||||||
onKeyUp={handleKeyUpRegister}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
disabled={loading || !validateEmail(registerCredentials.email) || registerCredentials.username === '' || registerCredentials.password === ''}
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='contained'
|
|
||||||
color='primary'
|
|
||||||
onClick={handleClickRegister}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Inscription
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='outlined'
|
|
||||||
color='warning'
|
|
||||||
onClick={() => setOpenDialog(true)}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Renvoyer l’e-mail de confirmation
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
</TabPanel>
|
|
||||||
</Box>
|
|
||||||
<ResetDialog
|
|
||||||
activation
|
|
||||||
lyen='send-email-confirmation'
|
|
||||||
title='Envoi de l’e-mail de confirmation'
|
|
||||||
content='Si vous n’avez pas reçu l’e-mail de confirmation, renseignez le champ et validez le formulaire.'
|
|
||||||
open={openDialog}
|
|
||||||
setOpen={setOpenDialog}
|
|
||||||
setLoading={setLoading}
|
|
||||||
setError={setError}
|
|
||||||
setSuccess={setSuccess}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{loading && <LinearProgress size={24} style={{width: '100%'}} />}
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<Snackbar
|
|
||||||
open={open}
|
|
||||||
autoHideDuration={6000}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='error' onClose={handleClose}><strong>{error}</strong></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{success && (
|
|
||||||
<Snackbar
|
|
||||||
open={success}
|
|
||||||
autoHideDuration={15_000}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<Alert severity='success' onClose={handleClose}><strong>Veuillez confirmer votre adresse e-mail via le lien qui vous a été envoyé.</strong></Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Koneksyon.propTypes = {
|
|
||||||
chimen: PropTypes.string.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Koneksyon
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import Image from 'next/image'
|
|
||||||
import {signIn} from 'next-auth/react'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
|
|
||||||
export default function LoginProvider({id, title, width, height, callbackUrl}) {
|
|
||||||
const hanleClick = event => {
|
|
||||||
event.preventDefault()
|
|
||||||
signIn(id, {
|
|
||||||
callbackUrl
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link passHref href='/api/auth/signin'>
|
|
||||||
<Button variant='outlined' color='info' startIcon={
|
|
||||||
<Image
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
alt={title}
|
|
||||||
src={`/images/${id === 'twitter' ? 'x' : id}.svg`}
|
|
||||||
/>
|
|
||||||
} onClick={hanleClick}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
LoginProvider.propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
width: PropTypes.number.isRequired,
|
|
||||||
height: PropTypes.number.isRequired,
|
|
||||||
callbackUrl: PropTypes.string.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
import {useState} from 'react'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {MenuItem, Menu, Button} from '@mui/material'
|
|
||||||
import {uniq} from 'lodash'
|
|
||||||
|
|
||||||
import OtomatikSwitch from './otomatik-switch'
|
|
||||||
|
|
||||||
const PREFIX = 'AjouteTradiksyon'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
paper: `${PREFIX}-paper`,
|
|
||||||
root: `${PREFIX}-root`
|
|
||||||
}
|
|
||||||
|
|
||||||
const Root = styled('div')((
|
|
||||||
{
|
|
||||||
theme
|
|
||||||
}
|
|
||||||
) => ({
|
|
||||||
[`& .${classes.paper}`]: {
|
|
||||||
border: '1px solid #d3d4d5'
|
|
||||||
},
|
|
||||||
|
|
||||||
[`& .${classes.root}`]: {
|
|
||||||
'&:focus': {
|
|
||||||
backgroundColor: theme.palette.primary.main,
|
|
||||||
'& .MuiListItemIcon-root, & .MuiListItemText-primary': {
|
|
||||||
color: theme.palette.common.white
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
function StyledMenu(props) {
|
|
||||||
return (
|
|
||||||
<Menu
|
|
||||||
elevation={0}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: 'top',
|
|
||||||
horizontal: 'center'
|
|
||||||
}}
|
|
||||||
transformOrigin={{
|
|
||||||
vertical: 'bottom',
|
|
||||||
horizontal: 'center'
|
|
||||||
}}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledMenuItem = MenuItem
|
|
||||||
|
|
||||||
function AjouteTradiksyon({showSwitch, disableSwitch, tradiksyonOtomatik, setTradiksyonOtomatik, chwaLang, setChwaLang}) {
|
|
||||||
const [anchorElement, setAnchorElement] = useState(null)
|
|
||||||
|
|
||||||
const handleClick = event => {
|
|
||||||
setAnchorElement(event.currentTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = event => {
|
|
||||||
setAnchorElement(null)
|
|
||||||
const chwaInik = uniq([...chwaLang, event.currentTarget.id])
|
|
||||||
setChwaLang(chwaInik.filter(c => c !== ''))
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{showSwitch && (
|
|
||||||
<OtomatikSwitch tradiksyonOtomatik={tradiksyonOtomatik} setTradiksyonOtomatik={setTradiksyonOtomatik} disabled={disableSwitch} />
|
|
||||||
)}
|
|
||||||
<Root style={{textAlign: 'center', marginTop: 20}}>
|
|
||||||
<Button
|
|
||||||
aria-controls='lang-menu'
|
|
||||||
variant='outlined'
|
|
||||||
aria-haspopup='true'
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
Ajouter une traduction <br /> 🇺🇸 🇫🇷 🇪🇸 🇩🇪 🇮🇹 🇧🇷 🇯🇵 🇰🇷
|
|
||||||
</Button>
|
|
||||||
<StyledMenu
|
|
||||||
keepMounted
|
|
||||||
id='lang-menu'
|
|
||||||
anchorEl={anchorElement}
|
|
||||||
open={Boolean(anchorElement)}
|
|
||||||
classes={{
|
|
||||||
paper: classes.paper
|
|
||||||
}}
|
|
||||||
onClose={handleClose}
|
|
||||||
>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='en'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇺🇸 Anglais (US)</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='fr'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇫🇷 Français</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='es'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇪🇸 Espagnol</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='de'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇩🇪 Allemand</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='it'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇮🇹 Italien</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='pt'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇧🇷 Portugais</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='ja'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇯🇵 Japonais</StyledMenuItem>
|
|
||||||
<StyledMenuItem
|
|
||||||
id='ko'
|
|
||||||
classes={{
|
|
||||||
root: classes.root
|
|
||||||
}}
|
|
||||||
onClick={handleClose}
|
|
||||||
>🇰🇷 Coréen</StyledMenuItem>
|
|
||||||
</StyledMenu>
|
|
||||||
</Root>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
AjouteTradiksyon.propTypes = {
|
|
||||||
showSwitch: PropTypes.bool.isRequired,
|
|
||||||
disableSwitch: PropTypes.bool.isRequired,
|
|
||||||
tradiksyonOtomatik: PropTypes.bool.isRequired,
|
|
||||||
setTradiksyonOtomatik: PropTypes.func.isRequired,
|
|
||||||
chwaLang: PropTypes.array.isRequired,
|
|
||||||
setChwaLang: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AjouteTradiksyon
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
import {useState} from 'react'
|
|
||||||
import {useSession} from 'next-auth/react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import Avatar from '@mui/material/Avatar'
|
|
||||||
import List from '@mui/material/List'
|
|
||||||
import ListItem from '@mui/material/ListItem'
|
|
||||||
import ListItemAvatar from '@mui/material/ListItemAvatar'
|
|
||||||
import ListItemText from '@mui/material/ListItemText'
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle'
|
|
||||||
import Dialog from '@mui/material/Dialog'
|
|
||||||
import {green} from '@mui/material/colors'
|
|
||||||
import Container from '@mui/material/Container'
|
|
||||||
import CircularProgress from '@mui/material/CircularProgress'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import AudiotrackIcon from '@mui/icons-material/Audiotrack'
|
|
||||||
|
|
||||||
import {jwennUserEpiToken} from '../../lib/oki-api'
|
|
||||||
|
|
||||||
function Chwa(props) {
|
|
||||||
const {onClose, teksLis, selectedTeks, loading, open} = props
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
onClose(selectedTeks)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleListItemClick = value => {
|
|
||||||
onClose(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={open} onClose={handleClose}>
|
|
||||||
<DialogTitle>Choisir la parole à modifier</DialogTitle>
|
|
||||||
{loading ? (
|
|
||||||
<Container sx={{textAlign: 'center', marginBottom: 1}}>
|
|
||||||
<CircularProgress size={20} sx={{color: '#29d'}} />
|
|
||||||
</Container>
|
|
||||||
) : (
|
|
||||||
<List sx={{pt: 0}}>
|
|
||||||
{teksLis.length > 0 ? teksLis.map(({id, titre, artistes}) => (
|
|
||||||
<ListItem key={id} button onClick={() => handleListItemClick(id)}>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar sx={{bgcolor: green[100], color: green[600]}}>
|
|
||||||
<AudiotrackIcon />
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText primary={`${new Intl.ListFormat('fr').format(artistes.map(({alias}) => alias))} - ${titre}`} />
|
|
||||||
</ListItem>
|
|
||||||
)) : (
|
|
||||||
<Typography sx={{textAlign: 'center'}} variant='button' component='div'>Aucun texte</Typography>
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
)}
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Chwa.propTypes = {
|
|
||||||
onClose: PropTypes.func.isRequired,
|
|
||||||
teksLis: PropTypes.array.isRequired,
|
|
||||||
loading: PropTypes.bool.isRequired,
|
|
||||||
open: PropTypes.bool.isRequired,
|
|
||||||
selectedTeks: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ChwaTeks({selectedTeks, setSelectedTeks}) {
|
|
||||||
const {data: session} = useSession()
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [teksLis, setTeksLis] = useState([])
|
|
||||||
|
|
||||||
const handleClickOpen = async () => {
|
|
||||||
setOpen(true)
|
|
||||||
setLoading(true)
|
|
||||||
|
|
||||||
const user = await jwennUserEpiToken(session?.jwt)
|
|
||||||
const {paroles} = user
|
|
||||||
const parolesList = paroles && paroles.length > 0 ? paroles : []
|
|
||||||
|
|
||||||
setTeksLis(parolesList)
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = value => {
|
|
||||||
const teks = teksLis.find(({id}) => id === value)
|
|
||||||
setSelectedTeks(teks)
|
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container sx={{textAlign: 'center', marginBottom: 1}} maxWidth='sm'>
|
|
||||||
<Button variant='outlined' onClick={handleClickOpen}>
|
|
||||||
Modifier mes paroles
|
|
||||||
</Button>
|
|
||||||
<Chwa
|
|
||||||
selectedTeks={selectedTeks}
|
|
||||||
teksLis={teksLis}
|
|
||||||
open={open}
|
|
||||||
loading={loading}
|
|
||||||
onClose={handleClose}
|
|
||||||
/>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChwaTeks.propTypes = {
|
|
||||||
selectedTeks: PropTypes.object,
|
|
||||||
setSelectedTeks: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
import {useCallback, useEffect, forwardRef, useState} from 'react'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import axios from 'axios'
|
|
||||||
import {useSession} from 'next-auth/react'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
import Button from '@mui/material/Button'
|
|
||||||
import Container from '@mui/material/Container'
|
|
||||||
import FormControl from '@mui/material/FormControl'
|
|
||||||
import FormHelperText from '@mui/material/FormHelperText'
|
|
||||||
import Grid from '@mui/material/Grid'
|
|
||||||
import IconButton from '@mui/material/IconButton'
|
|
||||||
import InputAdornment from '@mui/material/InputAdornment'
|
|
||||||
import InputLabel from '@mui/material/InputLabel'
|
|
||||||
import LinearProgress from '@mui/material/LinearProgress'
|
|
||||||
import OutlinedInput from '@mui/material/OutlinedInput'
|
|
||||||
import Snackbar from '@mui/material/Snackbar'
|
|
||||||
import TextField from '@mui/material/TextField'
|
|
||||||
import Tooltip from '@mui/material/Tooltip'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
|
|
||||||
import MuiAlert from '@mui/material/Alert'
|
|
||||||
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'
|
|
||||||
|
|
||||||
import {jwennAnTeks} from '../../lib/oki-api'
|
|
||||||
|
|
||||||
import AjouteTradiksyon from './ajoute-tradiksyon'
|
|
||||||
|
|
||||||
const PREFIX = 'EkriTeks'
|
|
||||||
|
|
||||||
const classes = {
|
|
||||||
tooltip: `${PREFIX}-tooltip`
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledContainer = styled(Container)(() => ({
|
|
||||||
[`& .${classes.tooltip}`]: {
|
|
||||||
fontSize: 18
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:1337'
|
|
||||||
|
|
||||||
const textLabels = {
|
|
||||||
fr: {
|
|
||||||
title: 'Traduction',
|
|
||||||
help: 'Traduction des paroles',
|
|
||||||
emoji: '🇫🇷'
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
title: 'Translation',
|
|
||||||
help: 'Translation of the lyrics',
|
|
||||||
emoji: '🇬🇧'
|
|
||||||
},
|
|
||||||
es: {
|
|
||||||
title: 'Traducción',
|
|
||||||
help: 'Traducción de la letra',
|
|
||||||
emoji: '🇪🇸'
|
|
||||||
},
|
|
||||||
de: {
|
|
||||||
title: 'Übersetzung',
|
|
||||||
help: 'Übersetzung von Liedtexten',
|
|
||||||
emoji: '🇩🇪'
|
|
||||||
},
|
|
||||||
it: {
|
|
||||||
title: 'Traduzione',
|
|
||||||
help: 'Traduzione del testo',
|
|
||||||
emoji: '🇮🇹'
|
|
||||||
},
|
|
||||||
pt: {
|
|
||||||
title: 'Tradução',
|
|
||||||
help: 'Tradução da letra',
|
|
||||||
emoji: '🇧🇷'
|
|
||||||
},
|
|
||||||
ja: {
|
|
||||||
title: '翻訳',
|
|
||||||
help: '歌詞の翻訳',
|
|
||||||
emoji: '🇯🇵'
|
|
||||||
},
|
|
||||||
ko: {
|
|
||||||
title: '번역',
|
|
||||||
help: '가사 번역',
|
|
||||||
emoji: '🇰🇷'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Alert = forwardRef(function Alert(props, ref) {
|
|
||||||
return <MuiAlert ref={ref} elevation={6} variant='filled' {...props} />
|
|
||||||
})
|
|
||||||
|
|
||||||
const RemoveTooltip = Tooltip
|
|
||||||
|
|
||||||
function EkriTeks({canAutoTranslate, selectedTeks, setSelectedTeks}) {
|
|
||||||
const {data: session} = useSession()
|
|
||||||
const {jwt, user} = session
|
|
||||||
const [teksEkri, setTeksEkri] = useState({awtis: '', tit: '', transkripsyon: ''})
|
|
||||||
const [tradiksyon, setTradiksyon] = useState({fr: '', en: '', es: '', de: '', it: '', pt: '', ja: '', ko: ''})
|
|
||||||
const [kiChawLang, setKiChwalang] = useState(['fr'])
|
|
||||||
const [error, setError] = useState('')
|
|
||||||
const [success, setSuccess] = useState('')
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [currentTeksId, setCurrentTeksId] = useState(null)
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [tradiksyonOtomatik, setTradiksyonOtomatik] = useState(false)
|
|
||||||
|
|
||||||
const handleUpdate = useCallback(update => {
|
|
||||||
setTeksEkri({...teksEkri, ...update})
|
|
||||||
}, [teksEkri])
|
|
||||||
|
|
||||||
const handleUpdateTradiksyon = useCallback(update => {
|
|
||||||
setTradiksyon({...tradiksyon, ...update})
|
|
||||||
}, [tradiksyon])
|
|
||||||
|
|
||||||
const handleRemoveTradiksyon = useCallback(idTradiksyon => {
|
|
||||||
setKiChwalang(kiChawLang.filter(t => t !== idTradiksyon))
|
|
||||||
}, [kiChawLang, setKiChwalang])
|
|
||||||
|
|
||||||
const handleClose = (event, reason) => {
|
|
||||||
if (reason === 'clickaway') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(false)
|
|
||||||
setSuccess('')
|
|
||||||
setError('')
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
setLoading(true)
|
|
||||||
const {awtis, tit, transkripsyon} = teksEkri
|
|
||||||
const {fr, en, es, de, it, pt, ja, ko} = tradiksyon
|
|
||||||
|
|
||||||
if (teksEkri.awtis === '' || teksEkri.tit === '' || teksEkri.transkripsyon === '') {
|
|
||||||
setError({message: 'Certains champs sont obligatoires'})
|
|
||||||
setLoading(false)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
Authorization: `Bearer ${jwt}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTeksId) {
|
|
||||||
try {
|
|
||||||
const teks = await jwennAnTeks(currentTeksId, jwt)
|
|
||||||
const teksResponse = await axios.put(`${API_URL}/paroles/${currentTeksId}`, {
|
|
||||||
data: {
|
|
||||||
id: teks.id,
|
|
||||||
titre: tit,
|
|
||||||
transcription: transkripsyon,
|
|
||||||
traductions: {
|
|
||||||
francais: fr === '' ? null : fr,
|
|
||||||
anglais: en === '' ? null : en,
|
|
||||||
espagnol: es === '' ? null : es,
|
|
||||||
allemand: de === '' ? null : de,
|
|
||||||
italien: it === '' ? null : it,
|
|
||||||
portugais: pt === '' ? null : pt,
|
|
||||||
japonais: ja === '' ? null : ja,
|
|
||||||
coreen: ko === '' ? null : ko
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email
|
|
||||||
},
|
|
||||||
artistes: teks.artistes.map(({id}) => id),
|
|
||||||
traductionAuto: tradiksyonOtomatik
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
headers
|
|
||||||
})
|
|
||||||
|
|
||||||
const {data} = teksResponse
|
|
||||||
setSuccess(`Le morceau "${data.tit}" a été modifié avec succès. Il apparaîtra sur le site après validation.`)
|
|
||||||
setLoading(false)
|
|
||||||
} catch (error) {
|
|
||||||
setError(error?.response?.data)
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const artiste = await axios.post(`${API_URL}/artistes`, {
|
|
||||||
data: {
|
|
||||||
alias: awtis,
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
headers
|
|
||||||
})
|
|
||||||
|
|
||||||
const parole = await axios.post(`${API_URL}/paroles`, {
|
|
||||||
data: {
|
|
||||||
titre: tit,
|
|
||||||
transcription: transkripsyon,
|
|
||||||
traductions: {
|
|
||||||
francais: fr === '' ? null : fr,
|
|
||||||
anglais: en === '' ? null : en,
|
|
||||||
espagnol: es === '' ? null : es,
|
|
||||||
allemand: de === '' ? null : de,
|
|
||||||
italien: it === '' ? null : it,
|
|
||||||
portugais: pt === '' ? null : pt,
|
|
||||||
japonais: ja === '' ? null : ja,
|
|
||||||
coreen: ko === '' ? null : ko
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email
|
|
||||||
},
|
|
||||||
artistes: [artiste.id],
|
|
||||||
traductionAuto: tradiksyonOtomatik
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
headers
|
|
||||||
})
|
|
||||||
|
|
||||||
const {data} = parole
|
|
||||||
|
|
||||||
setSuccess(`Le texte "${data.titre}" a été soumis avec succès. Il apparaîtra sur le site après validation.`)
|
|
||||||
setLoading(false)
|
|
||||||
} catch (error) {
|
|
||||||
setError(error?.response?.data)
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setTeksEkri({awtis: '', tit: '', transkripsyon: ''})
|
|
||||||
setTradiksyon({fr: '', en: '', es: '', de: '', it: '', pt: '', ja: '', ko: ''})
|
|
||||||
setKiChwalang(['fr'])
|
|
||||||
setCurrentTeksId(null)
|
|
||||||
setTradiksyonOtomatik(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (success) {
|
|
||||||
setOpen(true)
|
|
||||||
handleReset()
|
|
||||||
}
|
|
||||||
}, [success])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (error) {
|
|
||||||
setOpen(true)
|
|
||||||
}
|
|
||||||
}, [error])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedTeks) {
|
|
||||||
setTradiksyonOtomatik(false)
|
|
||||||
setCurrentTeksId(selectedTeks.id)
|
|
||||||
setTeksEkri({
|
|
||||||
awtis: new Intl.ListFormat('fr').format(selectedTeks.artistes.map(({alias}) => alias)),
|
|
||||||
tit: selectedTeks.titre,
|
|
||||||
transkripsyon: selectedTeks.transcription
|
|
||||||
})
|
|
||||||
setTradiksyon({
|
|
||||||
fr: selectedTeks?.traductions?.francais || '',
|
|
||||||
en: selectedTeks?.traductions?.anglais || '',
|
|
||||||
es: selectedTeks?.traductions?.espagnol || '',
|
|
||||||
de: selectedTeks?.traductions?.allemand || '',
|
|
||||||
it: selectedTeks?.traductions?.italien || '',
|
|
||||||
pt: selectedTeks?.traductions?.portugais || '',
|
|
||||||
ja: selectedTeks?.traductions?.japonais || '',
|
|
||||||
ko: selectedTeks?.traductions?.coreen || ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const jennLangId = () => {
|
|
||||||
const ids = []
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.francais) {
|
|
||||||
ids.push('fr')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.anglais) {
|
|
||||||
ids.push('en')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.espagnol) {
|
|
||||||
ids.push('es')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.allemand) {
|
|
||||||
ids.push('de')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.italien) {
|
|
||||||
ids.push('it')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.portugais) {
|
|
||||||
ids.push('pt')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.japonais) {
|
|
||||||
ids.push('ja')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedTeks?.traductions?.coreen) {
|
|
||||||
ids.push('ko')
|
|
||||||
}
|
|
||||||
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
const langIds = jennLangId()
|
|
||||||
|
|
||||||
setKiChwalang(langIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setSelectedTeks(null)
|
|
||||||
}
|
|
||||||
}, [teksEkri, selectedTeks, setSelectedTeks, kiChawLang])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer maxWidth='sm'>
|
|
||||||
<Box sx={{textAlign: 'center', marginBottom: 2}}>
|
|
||||||
<Typography display='inline' variant='h6' component='h1'>
|
|
||||||
Proposer une parole
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<form noValidate autoComplete='off'>
|
|
||||||
<Grid container style={{textAlign: 'center'}} spacing={1}>
|
|
||||||
<Grid xs='true'>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
id='awtis'
|
|
||||||
name='awtis'
|
|
||||||
label='Artiste'
|
|
||||||
value={teksEkri.awtis}
|
|
||||||
disabled={Boolean(currentTeksId)}
|
|
||||||
variant='outlined'
|
|
||||||
helperText='Nom de l’artiste (obligatoire)'
|
|
||||||
onChange={event => handleUpdate({awtis: event.target.value})}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid xs='true'>
|
|
||||||
<TextField
|
|
||||||
required
|
|
||||||
id='tit'
|
|
||||||
name='tit'
|
|
||||||
label='Titre'
|
|
||||||
value={selectedTeks?.tit || teksEkri.tit}
|
|
||||||
variant='outlined'
|
|
||||||
helperText='Titre de la musique (obligatoire)'
|
|
||||||
onChange={event => handleUpdate({tit: event.target.value})}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
multiline
|
|
||||||
required
|
|
||||||
helperText='Transcription des paroles (obligatoire)'
|
|
||||||
style={{marginTop: '1em'}}
|
|
||||||
id='transkripsyon'
|
|
||||||
label='Transcription'
|
|
||||||
value={teksEkri.transkripsyon}
|
|
||||||
rows={8}
|
|
||||||
variant='outlined'
|
|
||||||
onChange={event => handleUpdate({transkripsyon: event.target.value})}
|
|
||||||
/>
|
|
||||||
{kiChawLang.length > 0 && kiChawLang.map(k => (
|
|
||||||
<FormControl key={k} fullWidth style={{marginBlock: 10}}>
|
|
||||||
<InputLabel htmlFor={`tradiksyon-${k}`}>
|
|
||||||
<span dangerouslySetInnerHTML={{__html: textLabels[k].emoji}} /> {textLabels[k].title}
|
|
||||||
</InputLabel>
|
|
||||||
<OutlinedInput
|
|
||||||
multiline
|
|
||||||
style={{marginTop: '1em'}}
|
|
||||||
id={`tradiksyon-${k}`}
|
|
||||||
name={`tradiksyon-${k}`}
|
|
||||||
label={`${textLabels[k].title}`}
|
|
||||||
value={tradiksyon[k]}
|
|
||||||
rows={8}
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end'>
|
|
||||||
<RemoveTooltip
|
|
||||||
title='Cacher la fenêtre'
|
|
||||||
placement='left'
|
|
||||||
classes={{
|
|
||||||
tooltip: classes.tooltip
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
style={{bottom: 75}}
|
|
||||||
aria-label='remove-tradiksyon'
|
|
||||||
edge='end'
|
|
||||||
size='large'
|
|
||||||
onClick={() => handleRemoveTradiksyon(k)}
|
|
||||||
>
|
|
||||||
<VisibilityOffIcon />
|
|
||||||
</IconButton>
|
|
||||||
</RemoveTooltip>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
onChange={event => handleUpdateTradiksyon({[k]: event.target.value})}
|
|
||||||
/>
|
|
||||||
<FormHelperText id={`tradiksyon-${k}`}>{textLabels[k].help}</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
))}
|
|
||||||
</form>
|
|
||||||
<AjouteTradiksyon
|
|
||||||
showSwitch={Boolean(canAutoTranslate) && Boolean(!currentTeksId)}
|
|
||||||
disableSwitch={tradiksyon.fr === '' || tradiksyon.en !== '' || tradiksyon.es !== '' || tradiksyon.de !== '' || tradiksyon.it !== '' || tradiksyon.pt !== '' || tradiksyon.ja !== '' || tradiksyon.ko !== ''}
|
|
||||||
tradiksyonOtomatik={tradiksyonOtomatik}
|
|
||||||
setTradiksyonOtomatik={setTradiksyonOtomatik}
|
|
||||||
chwaLang={kiChawLang}
|
|
||||||
setChwaLang={setKiChwalang}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
disabled={loading || teksEkri.awtis === '' || teksEkri.tit === '' || teksEkri.transkripsyon === ''}
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='contained'
|
|
||||||
color='primary'
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Valider
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
disabled={loading || (teksEkri.awtis === '' && teksEkri.tit === '' && teksEkri.transkripsyon === '')}
|
|
||||||
style={{marginTop: 20}}
|
|
||||||
variant='contained'
|
|
||||||
color='error'
|
|
||||||
onClick={handleReset}
|
|
||||||
>
|
|
||||||
<Typography style={{fontWeight: 'bold'}}>
|
|
||||||
Tout effacer
|
|
||||||
</Typography>
|
|
||||||
</Button>
|
|
||||||
{loading && <LinearProgress size={24} style={{width: '100%', marginBlock: '1em'}} />}
|
|
||||||
</div>
|
|
||||||
{success && (
|
|
||||||
<Snackbar open={open} autoHideDuration={10_000} onClose={handleClose}>
|
|
||||||
<Alert severity='success' onClose={handleClose}>
|
|
||||||
<strong>{success}</strong>
|
|
||||||
</Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
{error && (
|
|
||||||
<Snackbar open={open} autoHideDuration={10_000} onClose={handleClose}>
|
|
||||||
<Alert severity='error' onClose={handleClose}>
|
|
||||||
<strong>Une erreur s’est produite</strong> : <i>{error?.error?.message}</i>
|
|
||||||
</Alert>
|
|
||||||
</Snackbar>
|
|
||||||
)}
|
|
||||||
</StyledContainer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
EkriTeks.propTypes = {
|
|
||||||
canAutoTranslate: PropTypes.bool,
|
|
||||||
selectedTeks: PropTypes.object,
|
|
||||||
setSelectedTeks: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EkriTeks
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import FormGroup from '@mui/material/FormGroup'
|
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel'
|
|
||||||
import Switch from '@mui/material/Switch'
|
|
||||||
|
|
||||||
export default function OtomatikSwitch({tradiksyonOtomatik, setTradiksyonOtomatik, disabled}) {
|
|
||||||
const handleChange = event => {
|
|
||||||
setTradiksyonOtomatik(event.target.checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormGroup sx={{marginTop: 1}}>
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Switch checked={tradiksyonOtomatik && !disabled} onChange={handleChange} />}
|
|
||||||
disabled={disabled}
|
|
||||||
label='Traduction du français vers les autres langues (remplir uniquement la traduction française)'
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
OtomatikSwitch.propTypes = {
|
|
||||||
tradiksyonOtomatik: PropTypes.bool.isRequired,
|
|
||||||
setTradiksyonOtomatik: PropTypes.func.isRequired,
|
|
||||||
disabled: PropTypes.bool.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import {styled} from '@mui/material/styles'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
import LinearProgress, {linearProgressClasses} from '@mui/material/LinearProgress'
|
|
||||||
|
|
||||||
const BorderLinearProgress = styled(LinearProgress)(({theme}) => ({
|
|
||||||
height: 10,
|
|
||||||
borderRadius: 5,
|
|
||||||
[`&.${linearProgressClasses.colorPrimary}`]: {
|
|
||||||
backgroundColor: 200,
|
|
||||||
...theme.applyStyles('dark', {
|
|
||||||
backgroundColor: 800
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[`& .${linearProgressClasses.bar}`]: {
|
|
||||||
borderRadius: 5,
|
|
||||||
backgroundColor: '#1a90ff',
|
|
||||||
...theme.applyStyles('dark', {
|
|
||||||
backgroundColor: '#308fe8'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
export default function BarStats({percent}) {
|
|
||||||
return (
|
|
||||||
<Box sx={{display: 'flex', alignItems: 'center'}}>
|
|
||||||
<Box sx={{width: '100%'}}>
|
|
||||||
<BorderLinearProgress variant='determinate' value={percent} />
|
|
||||||
</Box>
|
|
||||||
<Box sx={{minWidth: 35}}>
|
|
||||||
<Typography textAlign='right' sx={{fontWeight: 'bold'}} variant='body2' color='text.secondary'>{`${Math.round(
|
|
||||||
percent,
|
|
||||||
)}%`}</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BarStats.propTypes = {
|
|
||||||
percent: PropTypes.number.isRequired,
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import Card from '@mui/material/Card'
|
|
||||||
import CardContent from '@mui/material/CardContent'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import Grid from '@mui/material/Grid'
|
|
||||||
|
|
||||||
import TraductionsStats from './traductions-stats'
|
|
||||||
|
|
||||||
export default function KatStats({emoji, value, total}) {
|
|
||||||
return (
|
|
||||||
<Grid size={{xs: 12, md: 2}}>
|
|
||||||
<Card variant='outlined'>
|
|
||||||
<CardContent>
|
|
||||||
<Typography align='center' variant='h6'>
|
|
||||||
<span dangerouslySetInnerHTML={{__html: emoji}} />
|
|
||||||
</Typography>
|
|
||||||
<TraductionsStats value={value} total={total} />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
KatStats.propTypes = {
|
|
||||||
emoji: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.number.isRequired,
|
|
||||||
total: PropTypes.number.isRequired
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import Typography from '@mui/material/Typography'
|
|
||||||
import Box from '@mui/material/Box'
|
|
||||||
import Grid from '@mui/material/Grid'
|
|
||||||
|
|
||||||
import BarStats from './bar-stats'
|
|
||||||
|
|
||||||
export default function TraductionsStats({value, total}) {
|
|
||||||
const translated = total - value
|
|
||||||
const percent = (translated / total) * 100
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box sx={{width: '100%'}}>
|
|
||||||
<Grid container rowSpacing={3} columnSpacing={6} justifyContent='space-around'>
|
|
||||||
<Grid size={{xs: 12, md: 6}}>
|
|
||||||
<Typography style={{fontWeight: 'bold'}} align='center' variant='h6'>
|
|
||||||
{translated}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<BarStats value={value} percent={percent} />
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
TraductionsStats.propTypes = {
|
|
||||||
value: PropTypes.number.isRequired,
|
|
||||||
total: PropTypes.number.isRequired
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user