✨ Website : ShortLinks
This commit is contained in:
parent
eba7858caf
commit
cc899a020a
2
.github/backup.sql
vendored
2
.github/backup.sql
vendored
File diff suppressed because one or more lines are too long
438
website/pages/functions/linkShortener.jsx
Normal file
438
website/pages/functions/linkShortener.jsx
Normal file
@ -0,0 +1,438 @@
|
||||
import { useState, useEffect, useContext, useRef, useCallback } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faPen, faTrash, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import { UserContext } from '../../contexts/UserContext'
|
||||
import redirect from '../../utils/redirect'
|
||||
import htmlParser from 'html-react-parser'
|
||||
import Loader from '../../components/Loader'
|
||||
import FunctionPage from '../../components/FunctionPage/FunctionPage'
|
||||
import FunctionTabs from '../../components/FunctionPage/FunctionTabs'
|
||||
import FunctionArticle from '../../components/FunctionPage/FunctionArticle'
|
||||
import FunctionComments from '../../components/FunctionPage/FunctionComments/FunctionComments'
|
||||
import Modal from '../../components/Modal'
|
||||
import api from '../../utils/api'
|
||||
import 'notyf/notyf.min.css'
|
||||
import '../../public/css/pages/FunctionComponent.css'
|
||||
import '../../public/css/pages/admin.css'
|
||||
|
||||
const CreateLink = () => {
|
||||
const { isAuth, user } = useContext(UserContext)
|
||||
const [inputState, setInputState] = useState({})
|
||||
const [message, setMessage] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const handleSubmit = async event => {
|
||||
setIsLoading(true)
|
||||
event.preventDefault()
|
||||
try {
|
||||
const response = await api.post('/links', inputState, {
|
||||
headers: { Authorization: user.token }
|
||||
})
|
||||
setMessage(response.data.resultHTML)
|
||||
setIsLoading(false)
|
||||
setInputState({ url: '', shortcutName: '' })
|
||||
} catch (error) {
|
||||
setIsLoading(false)
|
||||
setMessage(error.response.data.message)
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = event => {
|
||||
const inputStateNew = { ...inputState }
|
||||
inputStateNew[event.target.name] = event.target.value
|
||||
setInputState(inputStateNew)
|
||||
}
|
||||
|
||||
if (!isAuth) {
|
||||
return (
|
||||
<p className='text-center'>
|
||||
Vous devez être{' '}
|
||||
<Link href='/users/login'>
|
||||
<a>connecté</a>
|
||||
</Link>{' '}
|
||||
pour gérer des liens raccourcis.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='container-fluid'>
|
||||
<div className='row'>
|
||||
<div className='col-24'>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='url'>
|
||||
Entrez le lien à raccourcir :
|
||||
</label>
|
||||
<input
|
||||
onChange={handleChange}
|
||||
type='text'
|
||||
name='url'
|
||||
id='url'
|
||||
placeholder='(e.g : https://divlo.fr)'
|
||||
className='form-control'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='shortcutName'>
|
||||
Entrez le nom du raccourci :
|
||||
</label>
|
||||
<input
|
||||
onChange={handleChange}
|
||||
type='text'
|
||||
name='shortcutName'
|
||||
id='shortcutName'
|
||||
placeholder='(e.g : divlo)'
|
||||
className='form-control'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group text-center'>
|
||||
<button type='submit' className='btn btn-dark'>
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div className='form-result text-center'>
|
||||
{isLoading ? <Loader /> : htmlParser(message)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let pageLinks = 1
|
||||
const LinksList = () => {
|
||||
const { isAuth, user } = useContext(UserContext)
|
||||
const [linksData, setLinksData] = useState({
|
||||
hasMore: true,
|
||||
rows: [],
|
||||
totalItems: 0
|
||||
})
|
||||
const [isLoadingLinks, setLoadingLinks] = useState(true)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
|
||||
const [defaultInputState, setDefaultInputState] = useState({
|
||||
shortcutName: '',
|
||||
url: '',
|
||||
id: 1
|
||||
})
|
||||
const [message, setMessage] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const toggleModal = () => {
|
||||
if (isEditing) {
|
||||
setIsLoading(false)
|
||||
setMessage('')
|
||||
setDefaultInputState({ shortcutName: '', url: '', id: 1 })
|
||||
}
|
||||
setIsEditing(!isEditing)
|
||||
}
|
||||
|
||||
// Récupère les liens initiales
|
||||
useEffect(() => {
|
||||
getLinksData().then(data => setLinksData(data))
|
||||
}, [])
|
||||
|
||||
const handleChange = event => {
|
||||
const inputStateNew = { ...defaultInputState }
|
||||
inputStateNew[event.target.name] = event.target.value
|
||||
setDefaultInputState(inputStateNew)
|
||||
}
|
||||
|
||||
const handleRemoveLink = async linkId => {
|
||||
try {
|
||||
await api.delete(`/links/${linkId}`, {
|
||||
headers: { Authorization: user.token }
|
||||
})
|
||||
const linksDataState = { ...linksData }
|
||||
const deletedLinkIndex = linksData.rows.findIndex(link => {
|
||||
return link.id === linkId
|
||||
})
|
||||
linksDataState.rows.splice(deletedLinkIndex, 1)
|
||||
setLinksData(linksDataState)
|
||||
|
||||
let Notyf
|
||||
if (typeof window !== 'undefined') {
|
||||
Notyf = require('notyf')
|
||||
}
|
||||
const notyf = new Notyf.Notyf({
|
||||
duration: 5000
|
||||
})
|
||||
notyf.success('Succès: lien raccourci supprimé!')
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleEditLink = linkInfo => {
|
||||
toggleModal()
|
||||
setDefaultInputState({
|
||||
shortcutName: linkInfo.shortcut,
|
||||
url: linkInfo.url,
|
||||
id: linkInfo.id
|
||||
})
|
||||
}
|
||||
|
||||
const handleEditSubmit = async event => {
|
||||
setIsLoading(true)
|
||||
event.preventDefault()
|
||||
try {
|
||||
const response = await api.put(
|
||||
`/links/${defaultInputState.id}`,
|
||||
defaultInputState,
|
||||
{
|
||||
headers: { Authorization: user.token }
|
||||
}
|
||||
)
|
||||
const linksDataState = { ...linksData }
|
||||
const editedLinkIndex = linksData.rows.findIndex(link => {
|
||||
return link.id === defaultInputState.id
|
||||
})
|
||||
if (editedLinkIndex != null) {
|
||||
linksDataState.rows[editedLinkIndex].shortcut =
|
||||
defaultInputState.shortcutName
|
||||
linksDataState.rows[editedLinkIndex].url = defaultInputState.url
|
||||
setLinksData(linksDataState)
|
||||
}
|
||||
setMessage(response.data.resultHTML)
|
||||
setIsLoading(false)
|
||||
} catch (error) {
|
||||
setIsLoading(false)
|
||||
setMessage(error.response.data.message)
|
||||
}
|
||||
}
|
||||
|
||||
const getLinksData = async () => {
|
||||
setLoadingLinks(true)
|
||||
const { data } = await api.get(`/links?page=${pageLinks}&limit=20`, {
|
||||
headers: { Authorization: user.token }
|
||||
})
|
||||
setLoadingLinks(false)
|
||||
return data
|
||||
}
|
||||
|
||||
// Permet la pagination au scroll
|
||||
const observer = useRef()
|
||||
const lastLinkRef = useCallback(
|
||||
node => {
|
||||
if (isLoadingLinks) return
|
||||
if (observer.current) observer.current.disconnect()
|
||||
observer.current = new window.IntersectionObserver(
|
||||
entries => {
|
||||
if (entries[0].isIntersecting && linksData.hasMore) {
|
||||
pageLinks += 1
|
||||
getLinksData().then(data => {
|
||||
setLinksData(oldData => {
|
||||
return {
|
||||
hasMore: data.hasMore,
|
||||
rows: [...oldData.rows, ...data.rows],
|
||||
totalItems: data.totalItems
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
{ threshold: 1 }
|
||||
)
|
||||
if (node) observer.current.observe(node)
|
||||
},
|
||||
[isLoadingLinks, linksData.hasMore]
|
||||
)
|
||||
|
||||
if (!isAuth) {
|
||||
return (
|
||||
<p className='text-center'>
|
||||
Vous devez être{' '}
|
||||
<Link href='/users/login'>
|
||||
<a>connecté</a>
|
||||
</Link>{' '}
|
||||
pour gérer des liens raccourcis.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='container-fluid text-center'>
|
||||
<div className='row justify-content-center'>
|
||||
<div className='col-24'>
|
||||
<h1>Gérer les liens</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className='row justify-content-center'>
|
||||
<div className='container-fluid'>
|
||||
{!isEditing ? (
|
||||
<div className='col-24 table-column'>
|
||||
<table className='table' style={{ marginBottom: '40px' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='table-row' scope='col'>
|
||||
Liens
|
||||
</th>
|
||||
<th className='table-row' scope='col'>
|
||||
Nom
|
||||
</th>
|
||||
<th className='table-row' scope='col'>
|
||||
Modifier
|
||||
</th>
|
||||
<th className='table-row' scope='col'>
|
||||
Supprimer
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{linksData.rows.map((link, index) => {
|
||||
const linkJSX = (
|
||||
<>
|
||||
<td className='table-row'>
|
||||
<a href={link.url}>{link.url}</a>
|
||||
</td>
|
||||
<td className='table-row'>{link.shortcut}</td>
|
||||
<td
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => handleEditLink(link)}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faPen}
|
||||
style={{ width: '1.5rem' }}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => handleRemoveLink(link.id)}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faTrash}
|
||||
style={{ width: '1.5rem' }}
|
||||
/>
|
||||
</td>
|
||||
</>
|
||||
)
|
||||
// Si c'est le dernier élément
|
||||
if (linksData.rows.length === index + 1) {
|
||||
return (
|
||||
<tr key={index} ref={lastLinkRef}>
|
||||
{linkJSX}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
return <tr key={index}>{linkJSX}</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<Modal>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='col-24'>
|
||||
<form onSubmit={handleEditSubmit}>
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='url'>
|
||||
Entrez le lien à raccourcir :
|
||||
</label>
|
||||
<input
|
||||
value={defaultInputState.url}
|
||||
onChange={handleChange}
|
||||
type='text'
|
||||
name='url'
|
||||
id='url'
|
||||
placeholder='(e.g : https://divlo.fr)'
|
||||
className='form-control'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label className='form-label' htmlFor='shortcutName'>
|
||||
Entrez le nom du raccourci :
|
||||
</label>
|
||||
<input
|
||||
value={defaultInputState.shortcutName}
|
||||
onChange={handleChange}
|
||||
type='text'
|
||||
name='shortcutName'
|
||||
id='shortcutName'
|
||||
placeholder='(e.g : divlo)'
|
||||
className='form-control'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group text-center'>
|
||||
<button type='submit' className='btn btn-dark'>
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className='form-result text-center'>
|
||||
{isLoading ? <Loader /> : htmlParser(message)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const FunctionTabManager = props => {
|
||||
return (
|
||||
<FunctionTabs
|
||||
setSlideIndex={props.setSlideIndex}
|
||||
slideIndex={props.slideIndex}
|
||||
>
|
||||
<div className='FunctionComponent__slide'>
|
||||
<CreateLink />
|
||||
</div>
|
||||
<div className='FunctionComponent__slide'>
|
||||
<LinksList />
|
||||
</div>
|
||||
<div className='FunctionComponent__slide'>
|
||||
<FunctionArticle article={props.article} />
|
||||
</div>
|
||||
<div className='FunctionComponent__slide'>
|
||||
<FunctionComments functionId={props.id} />
|
||||
</div>
|
||||
</FunctionTabs>
|
||||
)
|
||||
}
|
||||
|
||||
const linkShortener = props => (
|
||||
<FunctionPage
|
||||
FunctionTabManager={FunctionTabManager}
|
||||
{...props}
|
||||
tabNames={['⚙️ Utilisation', '📜 Liste', '📝 Article', '📬 Commentaires']}
|
||||
/>
|
||||
)
|
||||
|
||||
export async function getServerSideProps (context) {
|
||||
return api
|
||||
.get('/functions/linkShortener')
|
||||
.then(response => ({ props: response.data }))
|
||||
.catch(() => redirect(context, '/404'))
|
||||
}
|
||||
|
||||
export default linkShortener
|
Reference in New Issue
Block a user