frontend: Modifier/supprimer une catégorie /admin
This commit is contained in:
parent
f7c047120d
commit
5a26d28e34
36
website/package-lock.json
generated
36
website/package-lock.json
generated
@ -1432,6 +1432,11 @@
|
|||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@icons/material": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
|
||||||
|
},
|
||||||
"@next/polyfill-nomodule": {
|
"@next/polyfill-nomodule": {
|
||||||
"version": "9.3.2",
|
"version": "9.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/polyfill-nomodule/-/polyfill-nomodule-9.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@next/polyfill-nomodule/-/polyfill-nomodule-9.3.2.tgz",
|
||||||
@ -5007,6 +5012,11 @@
|
|||||||
"object-visit": "^1.0.0"
|
"object-visit": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"material-colors": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||||
|
},
|
||||||
"md5.js": {
|
"md5.js": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||||
@ -8456,6 +8466,19 @@
|
|||||||
"prop-types": "^15.6.2"
|
"prop-types": "^15.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-color": {
|
||||||
|
"version": "2.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.18.0.tgz",
|
||||||
|
"integrity": "sha512-FyVeU1kQiSokWc8NPz22azl1ezLpJdUyTbWL0LPUpcuuYDrZ/Y1veOk9rRK5B3pMlyDGvTk4f4KJhlkIQNRjEA==",
|
||||||
|
"requires": {
|
||||||
|
"@icons/material": "^0.2.4",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"material-colors": "^1.2.1",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"reactcss": "^1.2.0",
|
||||||
|
"tinycolor2": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.13.0",
|
"version": "16.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.0.tgz",
|
||||||
@ -8571,6 +8594,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reactcss": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"read-pkg": {
|
"read-pkg": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
||||||
@ -9675,6 +9706,11 @@
|
|||||||
"setimmediate": "^1.0.4"
|
"setimmediate": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tinycolor2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
|
||||||
|
},
|
||||||
"to-arraybuffer": {
|
"to-arraybuffer": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"next-fonts": "^1.0.3",
|
"next-fonts": "^1.0.3",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"react": "16.13.0",
|
"react": "16.13.0",
|
||||||
|
"react-color": "^2.18.0",
|
||||||
"react-dom": "16.13.0",
|
"react-dom": "16.13.0",
|
||||||
"react-swipeable-views": "^0.13.9",
|
"react-swipeable-views": "^0.13.9",
|
||||||
"react-swipeable-views-utils": "^0.13.9",
|
"react-swipeable-views-utils": "^0.13.9",
|
||||||
|
@ -90,12 +90,12 @@ const Admin = (props) => {
|
|||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label className="form-label" htmlFor="title">Titre :</label>
|
<label className="form-label" htmlFor="title">Titre :</label>
|
||||||
<input value={inputState.name} onChange={handleChange} type="text" name="title" id="title" className="form-control" placeholder="(e.g : Nombre aléatoire)" />
|
<input value={inputState.title} onChange={handleChange} type="text" name="title" id="title" className="form-control" placeholder="(e.g : Nombre aléatoire)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label className="form-label" htmlFor="slug">Slug :</label>
|
<label className="form-label" htmlFor="slug">Slug :</label>
|
||||||
<input value={inputState.name} onChange={handleChange} type="text" name="slug" id="slug" className="form-control" placeholder="(e.g : randomNumber)" />
|
<input value={inputState.slug} onChange={handleChange} type="text" name="slug" id="slug" className="form-control" placeholder="(e.g : randomNumber)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
|
@ -1,16 +1,116 @@
|
|||||||
import { Fragment } from 'react';
|
import { Fragment, useState } from 'react';
|
||||||
import Cookies from "universal-cookie";
|
import Cookies from "universal-cookie";
|
||||||
import date from 'date-and-time';
|
import date from 'date-and-time';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faPen, faTrash } from '@fortawesome/free-solid-svg-icons';
|
import { faPen, faTrash, faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { PhotoshopPicker } from 'react-color';
|
||||||
import HeadTag from '../../components/HeadTag';
|
import HeadTag from '../../components/HeadTag';
|
||||||
|
import Modal from '../../components/Modal';
|
||||||
import redirect from '../../utils/redirect';
|
import redirect from '../../utils/redirect';
|
||||||
|
import htmlParser from 'html-react-parser';
|
||||||
|
import Loader from '../../components/Loader';
|
||||||
import useAPI from '../../hooks/useAPI';
|
import useAPI from '../../hooks/useAPI';
|
||||||
|
import api from '../../utils/api';
|
||||||
import '../../public/css/pages/admin.css';
|
import '../../public/css/pages/admin.css';
|
||||||
|
|
||||||
|
const defaultCategoryState = { name: "", color: "#ffffff" };
|
||||||
|
|
||||||
|
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 != undefined) ? 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 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
apiCallCategory()
|
||||||
|
.then(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
window.location.reload(true);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`);
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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={props.toggleModal} 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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)" />
|
||||||
|
</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 } })} />
|
||||||
|
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const manageCategories = (props) => {
|
const manageCategories = (props) => {
|
||||||
|
|
||||||
const [, categories] = useAPI('/categories');
|
const [, categories] = useAPI('/categories');
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [defaultInputState, setDefaultInputState] = useState(defaultCategoryState);
|
||||||
|
|
||||||
|
const toggleModal = () => setIsOpen(!isOpen);
|
||||||
|
|
||||||
|
const handleRemoveCategory = async (categoryId) => {
|
||||||
|
try {
|
||||||
|
await api.delete(`/admin/categories/${categoryId}`, { headers: { 'Authorization': props.user.token } });
|
||||||
|
window.location.reload(true);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEditCategory = (categoryInfo) => {
|
||||||
|
setDefaultInputState(categoryInfo);
|
||||||
|
setIsEditing(true);
|
||||||
|
toggleModal();
|
||||||
|
}
|
||||||
|
|
||||||
if (!props.user.isAdmin && typeof window != 'undefined') {
|
if (!props.user.isAdmin && typeof window != 'undefined') {
|
||||||
return redirect({}, '/404');
|
return redirect({}, '/404');
|
||||||
@ -20,52 +120,59 @@ const manageCategories = (props) => {
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<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." />
|
||||||
|
|
||||||
<div className="container-fluid text-center">
|
{
|
||||||
<div className="row justify-content-center">
|
(isOpen) ?
|
||||||
<div className="col-24">
|
<Modal>
|
||||||
<h1>Gérer les catégories</h1>
|
<AddEditCategory toggleModal={toggleModal} defaultInputState={defaultInputState} { ...props } isEditing={isEditing} />
|
||||||
<button style={{ margin: '0 0 40px 0' }} className="btn btn-dark">Ajouter une catégorie</button>
|
</Modal>
|
||||||
</div>
|
:
|
||||||
</div>
|
<div className="container-fluid text-center">
|
||||||
<div className="row justify-content-center">
|
<div className="row justify-content-center">
|
||||||
<div className="container-fluid">
|
<div className="col-24">
|
||||||
<div className="col-24 Admin__table-column">
|
<h1>Gérer les catégories</h1>
|
||||||
<table className="Admin__table">
|
<button onClick={() => { setDefaultInputState(defaultCategoryState); toggleModal(); }} style={{ margin: '0 0 40px 0' }} className="btn btn-dark">Ajouter une catégorie</button>
|
||||||
<thead>
|
</div>
|
||||||
<tr>
|
</div>
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">id</th>
|
<div className="row justify-content-center">
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">name</th>
|
<div className="container-fluid">
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">color</th>
|
<div className="col-24 Admin__table-column">
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">createdAt</th>
|
<table className="Admin__table">
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">updatedAt</th>
|
<thead>
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">Modifier</th>
|
<tr>
|
||||||
<th className="Admin__table-row Admin__table-head-row" scope="col">Supprimer</th>
|
<th className="Admin__table-row Admin__table-head-row" scope="col">id</th>
|
||||||
</tr>
|
<th className="Admin__table-row Admin__table-head-row" scope="col">name</th>
|
||||||
</thead>
|
<th className="Admin__table-row Admin__table-head-row" scope="col">color</th>
|
||||||
<tbody>
|
<th className="Admin__table-row Admin__table-head-row" scope="col">createdAt</th>
|
||||||
{categories.map((category) => {
|
<th className="Admin__table-row Admin__table-head-row" scope="col">updatedAt</th>
|
||||||
return (
|
<th className="Admin__table-row Admin__table-head-row" scope="col">Modifier</th>
|
||||||
<tr key={category.id} style={{ backgroundColor: category.color }}>
|
<th className="Admin__table-row Admin__table-head-row" scope="col">Supprimer</th>
|
||||||
<td className="Admin__table-row">{category.id}</td>
|
|
||||||
<td className="Admin__table-row">{category.name}</td>
|
|
||||||
<td className="Admin__table-row">{category.color}</td>
|
|
||||||
<td className="Admin__table-row">{date.format(new Date(category.createdAt), 'DD/MM/YYYY à HH:mm', true)}</td>
|
|
||||||
<td className="Admin__table-row">{date.format(new Date(category.updatedAt), 'DD/MM/YYYY à HH:mm', true)}</td>
|
|
||||||
<td style={{ cursor: 'pointer' }}>
|
|
||||||
<FontAwesomeIcon icon={faPen} style={{ width: '1.5rem' }} />
|
|
||||||
</td>
|
|
||||||
<td style={{ cursor: 'pointer' }}>
|
|
||||||
<FontAwesomeIcon icon={faTrash} style={{ width: '1.5rem' }} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
</thead>
|
||||||
})}
|
<tbody>
|
||||||
</tbody>
|
{categories.map((category) => {
|
||||||
</table>
|
return (
|
||||||
|
<tr key={category.id} style={{ backgroundColor: category.color }}>
|
||||||
|
<td className="Admin__table-row">{category.id}</td>
|
||||||
|
<td className="Admin__table-row">{category.name}</td>
|
||||||
|
<td className="Admin__table-row">{category.color}</td>
|
||||||
|
<td className="Admin__table-row">{date.format(new Date(category.createdAt), 'DD/MM/YYYY à HH:mm', true)}</td>
|
||||||
|
<td className="Admin__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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
</div>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user