📦 NEW: frontend: Modifier info et Article
This commit is contained in:
parent
5eb64d200b
commit
9c5d1fc06b
129
website/components/AddEditFunction.js
Normal file
129
website/components/AddEditFunction.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import { Fragment, useState, useEffect } from 'react';
|
||||||
|
import htmlParser from 'html-react-parser';
|
||||||
|
import Loader from '../components/Loader';
|
||||||
|
import useAPI from '../hooks/useAPI';
|
||||||
|
import api from '../utils/api';
|
||||||
|
import '../public/css/pages/admin.css';
|
||||||
|
|
||||||
|
const AddEditFunction = (props) => {
|
||||||
|
|
||||||
|
const [, categories] = useAPI('/categories');
|
||||||
|
const [inputState, setInputState] = useState(props.defaultInputState);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (categories.length > 0 && !props.isEditing) {
|
||||||
|
handleChange({
|
||||||
|
target: {
|
||||||
|
name: "categorieId",
|
||||||
|
value: categories[0].id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [categories]);
|
||||||
|
|
||||||
|
const apiCallFunction = (formData) => {
|
||||||
|
if (props.isEditing) return api.put(`/admin/functions/${inputState.id}`, formData, { headers: { 'Authorization': props.user.token } });
|
||||||
|
return api.post('/admin/functions', formData, { headers: { 'Authorization': props.user.token } });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 handleSubmit = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('type', inputState.type);
|
||||||
|
formData.append('categorieId', inputState.categorieId);
|
||||||
|
formData.append('title', inputState.title);
|
||||||
|
formData.append('slug', inputState.slug);
|
||||||
|
formData.append('description', inputState.description);
|
||||||
|
formData.append('image', inputState.image);
|
||||||
|
|
||||||
|
if (props.isEditing) {
|
||||||
|
formData.append('isOnline', inputState.isOnline);
|
||||||
|
}
|
||||||
|
|
||||||
|
apiCallFunction(formData)
|
||||||
|
.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 (
|
||||||
|
<Fragment>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="form-group">
|
||||||
|
<label className="form-label" htmlFor="title">Titre :</label>
|
||||||
|
<input value={inputState.title} onChange={handleChange} type="text" name="title" id="title" className="form-control" placeholder="(e.g : Nombre aléatoire)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className="form-label" htmlFor="slug">Slug :</label>
|
||||||
|
<input value={inputState.slug} onChange={handleChange} type="text" name="slug" id="slug" className="form-control" placeholder="(e.g : randomNumber)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className="form-label" htmlFor="description">Description :</label>
|
||||||
|
<textarea style={{ height: 'auto' }} value={inputState.description} onChange={handleChange} name="description" id="description" className="form-control" rows="5"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className="form-label" htmlFor="type">Type :</label>
|
||||||
|
<select onChange={handleChange} name="type" id="type" className="form-control" { ...(props.isEditing) && { value: inputState.type } }>
|
||||||
|
<option value="form">Formulaire</option>
|
||||||
|
<option value="article">Article</option>
|
||||||
|
<option value="page">Page</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className="form-label" htmlFor="categorieId">Catégorie :</label>
|
||||||
|
<select onChange={handleChange} name="categorieId" id="categorieId" className="form-control" { ...(props.isEditing) && { value: inputState.categorieId } }>
|
||||||
|
{categories.map((category) => (
|
||||||
|
<option key={category.id} value={category.id} className="Admin__Modal-select-option" style={{ backgroundColor: category.color }}>{category.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className="form-label" htmlFor="image">Image <em>(150x150 recommandé)</em> :</label>
|
||||||
|
<br/>
|
||||||
|
<input onChange={handleChange} accept="image/jpeg,image/jpg,image/png" type="file" name="image" id="image" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(props.isEditing) &&
|
||||||
|
<div className="form-group custom-control custom-switch">
|
||||||
|
<input onChange={(event) => handleChange(event, true)} type="checkbox" name="isOnline" checked={inputState.isOnline} className="custom-control-input" id="isOnline" />
|
||||||
|
<label className="custom-control-label" htmlFor="isOnline">isOnline</label>
|
||||||
|
</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>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddEditFunction;
|
46
website/components/EditArticleFunction.js
Normal file
46
website/components/EditArticleFunction.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import 'suneditor/dist/css/suneditor.min.css';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
import htmlParser from 'html-react-parser';
|
||||||
|
import { complex } from '../utils/sunEditorConfig';
|
||||||
|
import api from '../utils/api';
|
||||||
|
import 'notyf/notyf.min.css'; // for React and Vue
|
||||||
|
|
||||||
|
const SunEditor = dynamic(
|
||||||
|
() => import('suneditor-react'),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const EditArticleFunction = (props) => {
|
||||||
|
|
||||||
|
const [htmlContent, setHtmlContent] = useState("");
|
||||||
|
|
||||||
|
const handleEditorChange = (content) => {
|
||||||
|
setHtmlContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = async (content) => {
|
||||||
|
let Notyf;
|
||||||
|
if (typeof window != 'undefined') {
|
||||||
|
Notyf = require('notyf');
|
||||||
|
}
|
||||||
|
const notyf = new Notyf.Notyf({
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await api.put(`/admin/functions/article/${props.functionInfo.id}`, { article: content }, { headers: { 'Authorization': props.user.token } });
|
||||||
|
notyf.success('Sauvegardé!');
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-fluid">
|
||||||
|
<SunEditor setContents={props.functionInfo.article} lang="fr" onChange={handleEditorChange} setOptions={{ buttonList: complex, callBackSave: handleSave }} />
|
||||||
|
<div className="container-fluid">
|
||||||
|
{htmlParser(htmlContent)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditArticleFunction;
|
@ -1,7 +1,16 @@
|
|||||||
|
import htmlParser from 'html-react-parser';
|
||||||
import FunctionTabs from './FunctionTabs/FunctionTabs';
|
import FunctionTabs from './FunctionTabs/FunctionTabs';
|
||||||
import FunctionForm from './FunctionForm';
|
import FunctionForm from './FunctionForm';
|
||||||
import FunctionComments from './FunctionComments/FunctionComments';
|
import FunctionComments from './FunctionComments/FunctionComments';
|
||||||
|
|
||||||
|
const Article = ({ article }) => {
|
||||||
|
return (
|
||||||
|
<div className="container-fluid">
|
||||||
|
{(article != undefined) && htmlParser(article)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const FunctionTabManager = (props) => {
|
const FunctionTabManager = (props) => {
|
||||||
if (props.type === "form") {
|
if (props.type === "form") {
|
||||||
return (
|
return (
|
||||||
@ -9,7 +18,9 @@ const FunctionTabManager = (props) => {
|
|||||||
<div className="FunctionComponent__slide">
|
<div className="FunctionComponent__slide">
|
||||||
<FunctionForm inputArray={ [...props.utilizationForm || []] } slug={props.slug} />
|
<FunctionForm inputArray={ [...props.utilizationForm || []] } slug={props.slug} />
|
||||||
</div>
|
</div>
|
||||||
<div className="FunctionComponent__slide text-center">Article</div>
|
<div className="FunctionComponent__slide">
|
||||||
|
<Article article={props.article} />
|
||||||
|
</div>
|
||||||
<div className="FunctionComponent__slide">
|
<div className="FunctionComponent__slide">
|
||||||
<FunctionComments functionId={props.id} />
|
<FunctionComments functionId={props.id} />
|
||||||
</div>
|
</div>
|
||||||
@ -19,7 +30,9 @@ const FunctionTabManager = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FunctionTabs type={props.type}>
|
<FunctionTabs type={props.type}>
|
||||||
<div className="FunctionComponent__slide text-center">Article</div>
|
<div className="FunctionComponent__slide">
|
||||||
|
<Article article={props.article} />
|
||||||
|
</div>
|
||||||
<div className="FunctionComponent__slide">
|
<div className="FunctionComponent__slide">
|
||||||
<FunctionComments functionId={props.id} />
|
<FunctionComments functionId={props.id} />
|
||||||
</div>
|
</div>
|
||||||
|
19
website/package-lock.json
generated
19
website/package-lock.json
generated
@ -5862,6 +5862,11 @@
|
|||||||
"sort-keys": "^1.0.0"
|
"sort-keys": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"notyf": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/notyf/-/notyf-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-TqirJJOj5xTrkUGbat94aeFdBCMuJqlxvFAeySV08WRhcZDVzegIsQrYgek1ZN/Vhc+Ux3BgUmndS4ii1W2TJg=="
|
||||||
|
},
|
||||||
"nprogress": {
|
"nprogress": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||||
@ -9537,6 +9542,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
||||||
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
||||||
},
|
},
|
||||||
|
"suneditor": {
|
||||||
|
"version": "2.28.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/suneditor/-/suneditor-2.28.4.tgz",
|
||||||
|
"integrity": "sha512-KLmKMq1QrBT9GT+/yv0d/a6YGEQ5+QUSmzZYJaIXGJ8QNNRV0BqfQP0gBnWY8s35HFKiU5meXWuzrI03AmubRg=="
|
||||||
|
},
|
||||||
|
"suneditor-react": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/suneditor-react/-/suneditor-react-2.9.1.tgz",
|
||||||
|
"integrity": "sha512-O26aLQ4dRWbdJv+d78pPaL/mLwWgVMiz64uo531+kHjBlNlREpV2dvvIYVH7TNxKi6LboeDZybJ5rm8kPMchAQ==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"suneditor": "^2.28.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||||
|
@ -18,12 +18,14 @@
|
|||||||
"html-react-parser": "^0.10.2",
|
"html-react-parser": "^0.10.2",
|
||||||
"next": "9.3.2",
|
"next": "9.3.2",
|
||||||
"next-fonts": "^1.0.3",
|
"next-fonts": "^1.0.3",
|
||||||
|
"notyf": "^3.6.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"react": "16.13.0",
|
"react": "16.13.0",
|
||||||
"react-color": "^2.18.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",
|
||||||
|
"suneditor-react": "^2.9.1",
|
||||||
"universal-cookie": "^4.0.3"
|
"universal-cookie": "^4.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,31 +1,76 @@
|
|||||||
import { Fragment } from 'react';
|
import { Fragment, useState } from 'react';
|
||||||
import Cookies from "universal-cookie";
|
import Cookies from "universal-cookie";
|
||||||
|
import SwipeableViews from 'react-swipeable-views';
|
||||||
import HeadTag from '../../components/HeadTag';
|
import HeadTag from '../../components/HeadTag';
|
||||||
|
import AddEditFunction from '../../components/AddEditFunction';
|
||||||
|
import EditArticleFunction from '../../components/EditArticleFunction';
|
||||||
import redirect from '../../utils/redirect';
|
import redirect from '../../utils/redirect';
|
||||||
|
import api from '../../utils/api';
|
||||||
|
import { API_URL } from '../../utils/config';
|
||||||
|
import '../../components/FunctionTabs/FunctionTabs.css';
|
||||||
|
import '../../public/css/pages/admin.css';
|
||||||
|
|
||||||
const AdminFunctionComponent = (props) => {
|
const AdminFunctionComponent = (props) => {
|
||||||
|
|
||||||
if (!props.user.isAdmin && typeof window != 'undefined') {
|
const [slideIndex, setSlideIndex] = useState(0);
|
||||||
return redirect({}, '/404');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<HeadTag />
|
<HeadTag title={props.functionInfo.title} description={props.functionInfo.description} image={API_URL + props.functionInfo.image} />
|
||||||
<p>{props.slug}</p>
|
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
|
<li className="FunctionTabs__nav-item">
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container-fluid">
|
||||||
|
<SwipeableViews onChangeIndex={(index) => setSlideIndex(index)} index={slideIndex}>
|
||||||
|
<div className="Admin__Function-slide">
|
||||||
|
<AddEditFunction
|
||||||
|
defaultInputState={{ ...props.functionInfo }}
|
||||||
|
user={props.user}
|
||||||
|
isEditing
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="Admin__Function-slide">
|
||||||
|
<EditArticleFunction { ...props } />
|
||||||
|
</div>
|
||||||
|
</SwipeableViews>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getServerSideProps({ req, params }) {
|
export async function getServerSideProps(context) {
|
||||||
const cookies = new Cookies(req.headers.cookie);
|
const cookies = new Cookies(context.req.headers.cookie);
|
||||||
const { slug } = params;
|
const user = { ...cookies.get('user') };
|
||||||
|
const { slug } = context.params;
|
||||||
|
if (!user.isAdmin) {
|
||||||
|
return redirect(context, '/404');
|
||||||
|
}
|
||||||
|
return api.get(`/admin/functions/${slug}`, { headers: { 'Authorization': user.token } })
|
||||||
|
.then((response) => {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
user: { ...cookies.get('user') },
|
user,
|
||||||
slug
|
functionInfo: response.data
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
.catch(() => redirect(context, '/404'));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminFunctionComponent;
|
export default AdminFunctionComponent;
|
@ -1,67 +1,21 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Fragment, useState, useEffect } from 'react';
|
import { Fragment, useState } from 'react';
|
||||||
import Cookies from "universal-cookie";
|
import Cookies from "universal-cookie";
|
||||||
import HeadTag from '../../components/HeadTag';
|
import HeadTag from '../../components/HeadTag';
|
||||||
import FunctionsList from '../../components/FunctionsList/FunctionsList';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
import { faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Modal from '../../components/Modal';
|
import Modal from '../../components/Modal';
|
||||||
|
import FunctionsList from '../../components/FunctionsList/FunctionsList';
|
||||||
|
import AddEditFunction from '../../components/AddEditFunction';
|
||||||
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 api from '../../utils/api';
|
|
||||||
import '../../public/css/pages/admin.css';
|
import '../../public/css/pages/admin.css';
|
||||||
|
|
||||||
const Admin = (props) => {
|
const Admin = (props) => {
|
||||||
|
|
||||||
const [, categories] = useAPI('/categories');
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [inputState, setInputState] = useState({ type: 'form' });
|
|
||||||
const [message, setMessage] = useState("");
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (categories.length > 0) {
|
|
||||||
handleChange({
|
|
||||||
target: {
|
|
||||||
name: "categorieId",
|
|
||||||
value: categories[0].id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [categories]);
|
|
||||||
|
|
||||||
const toggleModal = () => setIsOpen(!isOpen);
|
const toggleModal = () => setIsOpen(!isOpen);
|
||||||
|
|
||||||
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 handleSubmit = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setIsLoading(true);
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('type', inputState.type);
|
|
||||||
formData.append('categorieId', inputState.categorieId);
|
|
||||||
formData.append('title', inputState.title);
|
|
||||||
formData.append('slug', inputState.slug);
|
|
||||||
formData.append('description', inputState.description);
|
|
||||||
formData.append('image', inputState.image);
|
|
||||||
|
|
||||||
api.post('/admin/functions', formData, { headers: { 'Authorization': props.user.token } })
|
|
||||||
.then((response) => {
|
|
||||||
setMessage(`<p class="form-success"><b>Succès:</b> ${response.data.message}</p>`);
|
|
||||||
setIsLoading(false);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setMessage(`<p class="form-error"><b>Erreur:</b> ${error.response.data.message}</p>`);
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.user.isAdmin && typeof window != 'undefined') {
|
if (!props.user.isAdmin && typeof window != 'undefined') {
|
||||||
return redirect({}, '/404');
|
return redirect({}, '/404');
|
||||||
}
|
}
|
||||||
@ -87,58 +41,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-24">
|
<div className="col-24">
|
||||||
<form onSubmit={handleSubmit}>
|
<AddEditFunction defaultInputState={{ type: 'form' }} { ...props } />
|
||||||
<div className="form-group">
|
|
||||||
<label className="form-label" htmlFor="title">Titre :</label>
|
|
||||||
<input value={inputState.title} onChange={handleChange} type="text" name="title" id="title" className="form-control" placeholder="(e.g : Nombre aléatoire)" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label className="form-label" htmlFor="slug">Slug :</label>
|
|
||||||
<input value={inputState.slug} onChange={handleChange} type="text" name="slug" id="slug" className="form-control" placeholder="(e.g : randomNumber)" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label className="form-label" htmlFor="description">Description :</label>
|
|
||||||
<textarea style={{ height: 'auto' }} value={inputState.biography} onChange={handleChange} name="description" id="description" className="form-control" rows="5"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label className="form-label" htmlFor="type">Type :</label>
|
|
||||||
<select onChange={handleChange} name="type" id="type" className="form-control">
|
|
||||||
<option value="form">Formulaire</option>
|
|
||||||
<option value="article">Article</option>
|
|
||||||
<option value="page">Page</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label className="form-label" htmlFor="categorieId">Catégorie :</label>
|
|
||||||
<select onChange={handleChange} name="categorieId" id="categorieId" className="form-control">
|
|
||||||
{categories.map((category) => (
|
|
||||||
<option key={category.id} value={category.id} className="Admin__Modal-select-option" style={{ backgroundColor: category.color }}>{category.name}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label className="form-label" htmlFor="image">Image <em>(150x150 recommandé)</em> :</label>
|
|
||||||
<br/>
|
|
||||||
<input onChange={handleChange} accept="image/jpeg,image/jpg,image/png" type="file" name="image" id="image" />
|
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,6 +44,9 @@ a:hover {
|
|||||||
div[aria-hidden="true"] > * {
|
div[aria-hidden="true"] > * {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.notyf__message {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
/* LOADING */
|
/* LOADING */
|
||||||
.isLoading {
|
.isLoading {
|
||||||
@ -136,3 +139,66 @@ a, .important {
|
|||||||
background-color: #343a40;
|
background-color: #343a40;
|
||||||
border-color: #343a40;
|
border-color: #343a40;
|
||||||
}
|
}
|
||||||
|
.custom-control {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.custom-control-input {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.custom-control-label {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.custom-control-input:checked~.custom-control-label::before {
|
||||||
|
color: #fff;
|
||||||
|
border-color: #007bff;
|
||||||
|
background-color: #007bff;
|
||||||
|
}
|
||||||
|
.custom-switch .custom-control-label::before {
|
||||||
|
left: -2.25rem;
|
||||||
|
width: 1.75rem;
|
||||||
|
pointer-events: all;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
.custom-control-label::before {
|
||||||
|
position: absolute;
|
||||||
|
top: .25rem;
|
||||||
|
left: -1.5rem;
|
||||||
|
display: block;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
pointer-events: none;
|
||||||
|
content: "";
|
||||||
|
background-color: #fff;
|
||||||
|
border: #adb5bd solid 1px;
|
||||||
|
}
|
||||||
|
.custom-switch .custom-control-input:checked~.custom-control-label::after {
|
||||||
|
background-color: #fff;
|
||||||
|
-webkit-transform: translateX(.75rem);
|
||||||
|
transform: translateX(.75rem);
|
||||||
|
}
|
||||||
|
.custom-switch .custom-control-label::after {
|
||||||
|
top: calc(.25rem + 2px);
|
||||||
|
left: calc(-2.25rem + 2px);
|
||||||
|
width: calc(1rem - 4px);
|
||||||
|
height: calc(1rem - 4px);
|
||||||
|
background-color: #adb5bd;
|
||||||
|
border-radius: .5rem;
|
||||||
|
transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
||||||
|
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||||
|
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
||||||
|
}
|
||||||
|
.custom-control-label::after {
|
||||||
|
position: absolute;
|
||||||
|
top: .25rem;
|
||||||
|
left: -1.5rem;
|
||||||
|
display: block;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
content: "";
|
||||||
|
background: no-repeat 50%/50% 50%;
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin-top: 30px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
.FunctionComponent__title {
|
.FunctionComponent__title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -30,3 +30,6 @@
|
|||||||
.Admin__table-row {
|
.Admin__table-row {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
.Admin__Function-slide {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
@ -26,66 +26,3 @@
|
|||||||
.Profile__Modal-top-container {
|
.Profile__Modal-top-container {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
.custom-control {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.custom-control-input {
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.custom-control-label {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 0;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.custom-control-input:checked~.custom-control-label::before {
|
|
||||||
color: #fff;
|
|
||||||
border-color: #007bff;
|
|
||||||
background-color: #007bff;
|
|
||||||
}
|
|
||||||
.custom-switch .custom-control-label::before {
|
|
||||||
left: -2.25rem;
|
|
||||||
width: 1.75rem;
|
|
||||||
pointer-events: all;
|
|
||||||
border-radius: .5rem;
|
|
||||||
}
|
|
||||||
.custom-control-label::before {
|
|
||||||
position: absolute;
|
|
||||||
top: .25rem;
|
|
||||||
left: -1.5rem;
|
|
||||||
display: block;
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
pointer-events: none;
|
|
||||||
content: "";
|
|
||||||
background-color: #fff;
|
|
||||||
border: #adb5bd solid 1px;
|
|
||||||
}
|
|
||||||
.custom-switch .custom-control-input:checked~.custom-control-label::after {
|
|
||||||
background-color: #fff;
|
|
||||||
-webkit-transform: translateX(.75rem);
|
|
||||||
transform: translateX(.75rem);
|
|
||||||
}
|
|
||||||
.custom-switch .custom-control-label::after {
|
|
||||||
top: calc(.25rem + 2px);
|
|
||||||
left: calc(-2.25rem + 2px);
|
|
||||||
width: calc(1rem - 4px);
|
|
||||||
height: calc(1rem - 4px);
|
|
||||||
background-color: #adb5bd;
|
|
||||||
border-radius: .5rem;
|
|
||||||
transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
|
||||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
|
||||||
transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;
|
|
||||||
}
|
|
||||||
.custom-control-label::after {
|
|
||||||
position: absolute;
|
|
||||||
top: .25rem;
|
|
||||||
left: -1.5rem;
|
|
||||||
display: block;
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
content: "";
|
|
||||||
background: no-repeat 50%/50% 50%;
|
|
||||||
}
|
|
14
website/utils/sunEditorConfig.js
Normal file
14
website/utils/sunEditorConfig.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export const complex = [
|
||||||
|
["undo", "redo"],
|
||||||
|
["font", "fontSize", "formatBlock"],
|
||||||
|
["bold", "underline", "italic", "strike", "subscript", "superscript"],
|
||||||
|
["removeFormat"],
|
||||||
|
"/",
|
||||||
|
["fontColor", "hiliteColor"],
|
||||||
|
["outdent", "indent"],
|
||||||
|
["align", "horizontalRule", "list", "table"],
|
||||||
|
["link", "image", "video"],
|
||||||
|
["fullScreen", "showBlocks", "codeView"],
|
||||||
|
["preview", "print"],
|
||||||
|
["save", "template"]
|
||||||
|
];
|
Reference in New Issue
Block a user