🎨 website/utils API_URL + standardJS

This commit is contained in:
divlo
2020-08-03 14:37:05 +02:00
parent 4be7a46a10
commit 653068c9c7
20 changed files with 1108 additions and 501 deletions

View File

@ -6,42 +6,68 @@ import AddEditFunction from '../../components/FunctionAdmin/AddEditFunction'
import EditArticleFunction from '../../components/FunctionAdmin/EditArticleFunction'
import EditFormFunction from '../../components/FunctionAdmin/EditFormFunction'
import redirect from '../../utils/redirect'
import api from '../../utils/api'
import { API_URL } from '../../utils/config/config'
import api, { API_URL } from '../../utils/api'
import '../../components/FunctionPage/FunctionTabs.css'
import '../../public/css/pages/admin.css'
const AdminFunctionComponent = (props) => {
const AdminFunctionComponent = props => {
const [slideIndex, setSlideIndex] = useState(0)
const handleDeleteFunction = async () => {
await api.delete(`/admin/functions/${props.functionInfo.id}`, { headers: { Authorization: props.user.token } })
await api.delete(`/admin/functions/${props.functionInfo.id}`, {
headers: { Authorization: props.user.token }
})
redirect({}, '/admin')
}
return (
<>
<HeadTag title={props.functionInfo.title} description={props.functionInfo.description} image={API_URL + props.functionInfo.image} />
<HeadTag
title={props.functionInfo.title}
description={props.functionInfo.description}
image={API_URL + props.functionInfo.image}
/>
<div className='container-fluid'>
<div className='container'>
<div className='row justify-content-center'>
<ul className='FunctionTabs__nav'>
<li className='FunctionTabs__nav-item'>
<a onClick={() => setSlideIndex(0)} className={`FunctionTabs__nav-link ${(slideIndex === 0) && 'FunctionTabs__nav-link-active'}`}> Modifier</a>
<a
onClick={() => setSlideIndex(0)}
className={`FunctionTabs__nav-link ${slideIndex === 0 &&
'FunctionTabs__nav-link-active'}`}
>
Modifier
</a>
</li>
<li className='FunctionTabs__nav-item'>
<a onClick={() => setSlideIndex(1)} className={`FunctionTabs__nav-link ${(slideIndex === 1) && 'FunctionTabs__nav-link-active'}`}>📝 Article</a>
<a
onClick={() => setSlideIndex(1)}
className={`FunctionTabs__nav-link ${slideIndex === 1 &&
'FunctionTabs__nav-link-active'}`}
>
📝 Article
</a>
</li>
<li className='FunctionTabs__nav-item'>
<a onClick={() => setSlideIndex(2)} className={`FunctionTabs__nav-link ${(slideIndex === 2) && 'FunctionTabs__nav-link-active'}`}> Utilisation</a>
<a
onClick={() => setSlideIndex(2)}
className={`FunctionTabs__nav-link ${slideIndex === 2 &&
'FunctionTabs__nav-link-active'}`}
>
Utilisation
</a>
</li>
</ul>
</div>
</div>
<div className='container-fluid'>
<SwipeableViews onChangeIndex={(index) => setSlideIndex(index)} index={slideIndex}>
<SwipeableViews
onChangeIndex={index => setSlideIndex(index)}
index={slideIndex}
>
<div className='Admin__Function-slide'>
<AddEditFunction
defaultInputState={{ ...props.functionInfo }}
@ -49,7 +75,9 @@ const AdminFunctionComponent = (props) => {
isEditing
/>
<div style={{ marginBottom: '30px' }} className='text-center'>
<button onClick={handleDeleteFunction} className='btn btn-dark'>Supprimer la fonction</button>
<button onClick={handleDeleteFunction} className='btn btn-dark'>
Supprimer la fonction
</button>
</div>
</div>
<div className='Admin__Function-slide'>
@ -72,8 +100,9 @@ export async function getServerSideProps (context) {
if (!user.isAdmin) {
return redirect(context, '/404')
}
return api.get(`/admin/functions/${slug}`, { headers: { Authorization: user.token } })
.then((response) => {
return api
.get(`/admin/functions/${slug}`, { headers: { Authorization: user.token } })
.then(response => {
return {
props: {
user,

View File

@ -10,54 +10,77 @@ import AddEditFunction from '../../components/FunctionAdmin/AddEditFunction'
import redirect from '../../utils/redirect'
import '../../public/css/pages/admin.css'
const Admin = (props) => {
const Admin = props => {
const [isOpen, setIsOpen] = useState(false)
const toggleModal = () => setIsOpen(!isOpen)
return (
<>
<HeadTag title='Admin - FunctionProject' description="Page d'administration de FunctionProject." />
<HeadTag
title='Admin - FunctionProject'
description="Page d'administration de FunctionProject."
/>
{/* Création d'une fonction */}
{(isOpen)
? (
<Modal toggleModal={toggleModal}>
<div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'>
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span onClick={toggleModal} style={{ cursor: 'pointer', position: 'absolute', left: 0 }}>
<FontAwesomeIcon icon={faTimes} style={{ width: '1.5rem', color: 'red' }} />
</span>
<h2 className='text-center'>Crée une nouvelle fonction</h2>
</div>
{isOpen ? (
<Modal toggleModal={toggleModal}>
<div className='Admin__Modal__container container-fluid'>
<div className='Admin__Modal__row row'>
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span
onClick={toggleModal}
style={{
cursor: 'pointer',
position: 'absolute',
left: 0
}}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
<h2 className='text-center'>Crée une nouvelle fonction</h2>
</div>
</div>
</div>
<div className='col-24'>
<AddEditFunction defaultInputState={{ type: 'form' }} {...props} />
</div>
<div className='col-24'>
<AddEditFunction
defaultInputState={{ type: 'form' }}
{...props}
/>
</div>
</div>
</Modal>
)
: (
<FunctionsList isAdmin token={props.user.token}>
<div className='col-24'>
<h1 className='Functions__title'>Administration</h1>
<button onClick={toggleModal} style={{ margin: '0 0 40px 0' }} className='btn btn-dark'>Crée une nouvelle fonction</button>
<Link href='/admin/manageCategories'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>Gérer les catégories</button>
</Link>
<Link href='/admin/manageQuotes'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>Gérer les citations</button>
</Link>
</div>
</FunctionsList>
)}
</div>
</Modal>
) : (
<FunctionsList isAdmin token={props.user.token}>
<div className='col-24'>
<h1 className='Functions__title'>Administration</h1>
<button
onClick={toggleModal}
style={{ margin: '0 0 40px 0' }}
className='btn btn-dark'
>
Crée une nouvelle fonction
</button>
<Link href='/admin/manageCategories'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>
Gérer les catégories
</button>
</Link>
<Link href='/admin/manageQuotes'>
<button style={{ margin: '0 0 0 20px' }} className='btn btn-dark'>
Gérer les citations
</button>
</Link>
</div>
</FunctionsList>
)}
</>
)
}

View File

@ -15,23 +15,36 @@ import '../../public/css/pages/admin.css'
const defaultCategoryState = { name: '', color: '#ffffff' }
const AddEditCategory = (props) => {
const AddEditCategory = props => {
const [inputState, setInputState] = useState(props.defaultInputState)
const [message, setMessage] = useState('')
const [isLoading, setIsLoading] = useState(false)
const handleChange = (event, isTypeCheck = false) => {
const inputStateNew = { ...inputState }
inputStateNew[event.target.name] = (event.target.files != null) ? event.target.files[0] : (isTypeCheck) ? event.target.checked : event.target.value
inputStateNew[event.target.name] =
event.target.files != null
? event.target.files[0]
: isTypeCheck
? event.target.checked
: event.target.value
setInputState(inputStateNew)
}
const apiCallCategory = () => {
if (props.isEditing) return api.put(`/admin/categories/${inputState.id}`, { name: inputState.name, color: inputState.color }, { headers: { Authorization: props.user.token } })
return api.post('/admin/categories', inputState, { headers: { Authorization: props.user.token } })
if (props.isEditing) {
return api.put(
`/admin/categories/${inputState.id}`,
{ name: inputState.name, color: inputState.color },
{ headers: { Authorization: props.user.token } }
)
}
return api.post('/admin/categories', inputState, {
headers: { Authorization: props.user.token }
})
}
const handleSubmit = (event) => {
const handleSubmit = event => {
event.preventDefault()
setIsLoading(true)
apiCallCategory()
@ -39,8 +52,10 @@ const AddEditCategory = (props) => {
setIsLoading(false)
window.location.reload(true)
})
.catch((error) => {
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`)
.catch(error => {
setMessage(
`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`
)
setIsLoading(false)
})
}
@ -51,10 +66,20 @@ const AddEditCategory = (props) => {
<div className='col-24'>
<div className='Admin__Modal-top-container row'>
<div className='col-24'>
<span onClick={props.handleToggleModal} style={{ cursor: 'pointer', position: 'absolute', left: 0 }}>
<FontAwesomeIcon icon={faTimes} style={{ width: '1.5rem', color: 'red' }} />
<span
onClick={props.handleToggleModal}
style={{ cursor: 'pointer', position: 'absolute', left: 0 }}
>
<FontAwesomeIcon
icon={faTimes}
style={{ width: '1.5rem', color: 'red' }}
/>
</span>
<h2 className='text-center'>{(props.isEditing) ? 'Modifier la catégorie' : 'Crée une nouvelle catégorie'}</h2>
<h2 className='text-center'>
{props.isEditing
? 'Modifier la catégorie'
: 'Crée une nouvelle catégorie'}
</h2>
</div>
</div>
</div>
@ -62,25 +87,39 @@ const AddEditCategory = (props) => {
<div className='col-24'>
<form onSubmit={handleSubmit}>
<div className='form-group'>
<label className='form-label' htmlFor='name'>Nom :</label>
<input value={inputState.name} onChange={handleChange} type='text' name='name' id='name' className='form-control' placeholder='(e.g : ✨ Utilitaires)' />
<label className='form-label' htmlFor='name'>
Nom :
</label>
<input
value={inputState.name}
onChange={handleChange}
type='text'
name='name'
id='name'
className='form-control'
placeholder='(e.g : ✨ Utilitaires)'
/>
</div>
<div className='form-group'>
<label className='form-label' htmlFor='title'>Couleur :</label>
<PhotoshopPicker color={inputState.color} onChange={(color) => handleChange({ target: { name: 'color', value: color.hex } })} />
<label className='form-label' htmlFor='title'>
Couleur :
</label>
<PhotoshopPicker
color={inputState.color}
onChange={color =>
handleChange({ target: { name: 'color', value: color.hex } })}
/>
</div>
<div className='form-group text-center'>
<button type='submit' className='btn btn-dark'>Envoyer</button>
<button type='submit' className='btn btn-dark'>
Envoyer
</button>
</div>
</form>
<div className='form-result text-center'>
{
(isLoading)
? <Loader />
: htmlParser(message)
}
{isLoading ? <Loader /> : htmlParser(message)}
</div>
</div>
</div>
@ -88,22 +127,26 @@ const AddEditCategory = (props) => {
)
}
const manageCategories = (props) => {
const manageCategories = props => {
const [, categories] = useAPI('/categories')
const [isOpen, setIsOpen] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const [defaultInputState, setDefaultInputState] = useState(defaultCategoryState)
const [defaultInputState, setDefaultInputState] = useState(
defaultCategoryState
)
const toggleModal = () => setIsOpen(!isOpen)
const handleRemoveCategory = async (categoryId) => {
const handleRemoveCategory = async categoryId => {
try {
await api.delete(`/admin/categories/${categoryId}`, { headers: { Authorization: props.user.token } })
await api.delete(`/admin/categories/${categoryId}`, {
headers: { Authorization: props.user.token }
})
window.location.reload(true)
} catch {}
}
const handleEditCategory = (categoryInfo) => {
const handleEditCategory = categoryInfo => {
setDefaultInputState(categoryInfo)
setIsEditing(true)
toggleModal()
@ -111,64 +154,124 @@ const manageCategories = (props) => {
return (
<>
<HeadTag title='Admin - FunctionProject' description="Page d'administration de FunctionProject. Gérer les catégories." />
<HeadTag
title='Admin - FunctionProject'
description="Page d'administration de FunctionProject. Gérer les catégories."
/>
{
(isOpen)
? (
<Modal>
<AddEditCategory handleToggleModal={toggleModal} defaultInputState={defaultInputState} {...props} isEditing={isEditing} />
</Modal>
)
: (
<div className='container-fluid text-center'>
<div className='row justify-content-center'>
<div className='col-24'>
<h1>Gérer les catégories</h1>
<button onClick={() => { setDefaultInputState(defaultCategoryState); toggleModal(); setIsEditing(false) }} style={{ margin: '0 0 40px 0' }} className='btn btn-dark'>Ajouter une catégorie</button>
</div>
</div>
<div className='row justify-content-center'>
<div className='container-fluid'>
<div className='col-24 table-column'>
<table className='table'>
<thead>
<tr>
<th className='table-row' scope='col'>id</th>
<th className='table-row' scope='col'>name</th>
<th className='table-row' scope='col'>color</th>
<th className='table-row' scope='col'>createdAt</th>
<th className='table-row' scope='col'>updatedAt</th>
<th className='table-row' scope='col'>Modifier</th>
<th className='table-row' scope='col'>Supprimer</th>
{isOpen ? (
<Modal>
<AddEditCategory
handleToggleModal={toggleModal}
defaultInputState={defaultInputState}
{...props}
isEditing={isEditing}
/>
</Modal>
) : (
<div className='container-fluid text-center'>
<div className='row justify-content-center'>
<div className='col-24'>
<h1>Gérer les catégories</h1>
<button
onClick={() => {
setDefaultInputState(defaultCategoryState)
toggleModal()
setIsEditing(false)
}}
style={{ margin: '0 0 40px 0' }}
className='btn btn-dark'
>
Ajouter une catégorie
</button>
</div>
</div>
<div className='row justify-content-center'>
<div className='container-fluid'>
<div className='col-24 table-column'>
<table className='table'>
<thead>
<tr>
<th className='table-row' scope='col'>
id
</th>
<th className='table-row' scope='col'>
name
</th>
<th className='table-row' scope='col'>
color
</th>
<th className='table-row' scope='col'>
createdAt
</th>
<th className='table-row' scope='col'>
updatedAt
</th>
<th className='table-row' scope='col'>
Modifier
</th>
<th className='table-row' scope='col'>
Supprimer
</th>
</tr>
</thead>
<tbody>
{categories.map(category => {
return (
<tr
key={category.id}
style={{ backgroundColor: category.color }}
>
<td className='table-row'>{category.id}</td>
<td className='table-row'>{category.name}</td>
<td className='table-row'>{category.color}</td>
<td className='table-row'>
{date.format(
new Date(category.createdAt),
'DD/MM/YYYY à HH:mm',
true
)}
</td>
<td className='table-row'>
{date.format(
new Date(category.updatedAt),
'DD/MM/YYYY à HH:mm',
true
)}
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() =>
handleEditCategory({
name: category.name,
color: category.color,
id: category.id
})}
>
<FontAwesomeIcon
icon={faPen}
style={{ width: '1.5rem' }}
/>
</td>
<td
style={{ cursor: 'pointer' }}
onClick={() => handleRemoveCategory(category.id)}
>
<FontAwesomeIcon
icon={faTrash}
style={{ width: '1.5rem' }}
/>
</td>
</tr>
</thead>
<tbody>
{categories.map((category) => {
return (
<tr key={category.id} style={{ backgroundColor: category.color }}>
<td className='table-row'>{category.id}</td>
<td className='table-row'>{category.name}</td>
<td className='table-row'>{category.color}</td>
<td className='table-row'>{date.format(new Date(category.createdAt), 'DD/MM/YYYY à HH:mm', true)}</td>
<td className='table-row'>{date.format(new Date(category.updatedAt), 'DD/MM/YYYY à HH:mm', true)}</td>
<td style={{ cursor: 'pointer' }} onClick={() => handleEditCategory({ name: category.name, color: category.color, id: category.id })}>
<FontAwesomeIcon icon={faPen} style={{ width: '1.5rem' }} />
</td>
<td style={{ cursor: 'pointer' }} onClick={() => handleRemoveCategory(category.id)}>
<FontAwesomeIcon icon={faTrash} style={{ width: '1.5rem' }} />
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)
})}
</tbody>
</table>
</div>
</div>
)
}
</div>
</div>
)}
</>
)
}

View File

@ -8,8 +8,12 @@ import HeadTag from '../../components/HeadTag'
import api from '../../utils/api'
import '../../public/css/pages/admin.css'
const manageQuotes = (props) => {
const [quotesData, setQuotesData] = useState({ hasMore: true, rows: [], totalItems: 0 })
const manageQuotes = props => {
const [quotesData, setQuotesData] = useState({
hasMore: true,
rows: [],
totalItems: 0
})
const [isLoadingQuotes, setLoadingQuotes] = useState(true)
const [pageQuotes, setPageQuotes] = useState(1)
@ -20,7 +24,9 @@ const manageQuotes = (props) => {
const getQuotesData = async () => {
setLoadingQuotes(true)
const { data } = await api.get(`/admin/quotes?limit=20page=${pageQuotes}`, { headers: { Authorization: props.user.token } })
const { data } = await api.get(`/admin/quotes?limit=20page=${pageQuotes}`, {
headers: { Authorization: props.user.token }
})
setQuotesData({
hasMore: data.hasMore,
rows: [...quotesData.rows, ...data.rows],
@ -31,33 +37,48 @@ const manageQuotes = (props) => {
// Permet la pagination au scroll
const observer = useRef()
const lastQuoteRef = useCallback((node) => {
if (isLoadingQuotes) return
if (observer.current) observer.current.disconnect()
observer.current = new window.IntersectionObserver((entries) => {
if (entries[0].isIntersecting && quotesData.hasMore) {
setPageQuotes(pageQuotes + 1)
}
}, { threshold: 1 })
if (node) observer.current.observe(node)
}, [isLoadingQuotes, quotesData.hasMore])
const lastQuoteRef = useCallback(
node => {
if (isLoadingQuotes) return
if (observer.current) observer.current.disconnect()
observer.current = new window.IntersectionObserver(
entries => {
if (entries[0].isIntersecting && quotesData.hasMore) {
setPageQuotes(pageQuotes + 1)
}
},
{ threshold: 1 }
)
if (node) observer.current.observe(node)
},
[isLoadingQuotes, quotesData.hasMore]
)
const handleValidationQuote = async (id, isValid) => {
try {
await api.put(`/admin/quotes/${id}`, { isValid }, { headers: { Authorization: props.user.token } })
await api.put(
`/admin/quotes/${id}`,
{ isValid },
{ headers: { Authorization: props.user.token } }
)
window.location.reload(true)
} catch {}
}
return (
<>
<HeadTag title='Admin - FunctionProject' description="Page d'administration de FunctionProject. Gérer les citations." />
<HeadTag
title='Admin - FunctionProject'
description="Page d'administration de FunctionProject. Gérer les citations."
/>
<div className='container-fluid'>
<div className='row justify-content-center'>
<div className='col-24 text-center'>
<h2>Liste des citations (non validées) : </h2>
<p style={{ marginTop: '5px' }}>Total de {quotesData.totalItems} citations.</p>
<p style={{ marginTop: '5px' }}>
Total de {quotesData.totalItems} citations.
</p>
</div>
</div>
@ -66,35 +87,72 @@ const manageQuotes = (props) => {
<table className='table'>
<thead>
<tr>
<th className='table-row' scope='col'>Citation/Proverbe</th>
<th className='table-row' scope='col'>Auteur</th>
<th className='table-row' scope='col'>Proposée par</th>
<th className='table-row' scope='col'>Valider</th>
<th className='table-row' scope='col'>Supprimer</th>
<th className='table-row' scope='col'>
Citation/Proverbe
</th>
<th className='table-row' scope='col'>
Auteur
</th>
<th className='table-row' scope='col'>
Proposée par
</th>
<th className='table-row' scope='col'>
Valider
</th>
<th className='table-row' scope='col'>
Supprimer
</th>
</tr>
</thead>
<tbody>
{quotesData.rows.map((currentQuote, index) => {
const quoteJSX = (
<>
<td className='table-row text-center'>{currentQuote.quote}</td>
<td className='table-row text-center'>{currentQuote.author}</td>
<td className='table-row text-center'>
<Link href='/users/[name]' as={`/users/${currentQuote.user.name}`}>
{currentQuote.quote}
</td>
<td className='table-row text-center'>
{currentQuote.author}
</td>
<td className='table-row text-center'>
<Link
href='/users/[name]'
as={`/users/${currentQuote.user.name}`}
>
<a>{currentQuote.user.name}</a>
</Link>
</td>
<td onClick={() => handleValidationQuote(currentQuote.id, true)} className='table-row text-center' style={{ cursor: 'pointer' }}>
<FontAwesomeIcon icon={faCheck} style={{ width: '1.5rem' }} />
<td
onClick={() =>
handleValidationQuote(currentQuote.id, true)}
className='table-row text-center'
style={{ cursor: 'pointer' }}
>
<FontAwesomeIcon
icon={faCheck}
style={{ width: '1.5rem' }}
/>
</td>
<td onClick={() => handleValidationQuote(currentQuote.id, false)} className='table-row text-center' style={{ cursor: 'pointer' }}>
<FontAwesomeIcon icon={faTrash} style={{ width: '1.5rem' }} />
<td
onClick={() =>
handleValidationQuote(currentQuote.id, false)}
className='table-row text-center'
style={{ cursor: 'pointer' }}
>
<FontAwesomeIcon
icon={faTrash}
style={{ width: '1.5rem' }}
/>
</td>
</>
)
// Si c'est le dernier élément
if (quotesData.rows.length === index + 1) {
return <tr key={index} ref={lastQuoteRef}>{quoteJSX}</tr>
return (
<tr key={index} ref={lastQuoteRef}>
{quoteJSX}
</tr>
)
}
return <tr key={index}>{quoteJSX}</tr>
})}