Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • web-et-numerique/factory/llle_project/backoffice-client
1 result
Show changes
Commits on Source (14)
Showing
with 595 additions and 16290 deletions
NODE_TLS_REJECT_UNAUTHORIZED = '0'
HTTPS=true
SSL_CRT_FILE=cert.pem
SSL_KEY_FILE=key.pem
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIID+TCCAuGgAwIBAgIUEs1Oq9v38qBci7a98TvOxCCMS8EwDQYJKoZIhvcNAQEL
BQAwgYsxCzAJBgNVBAYTAkZSMQ4wDAYDVQQIDAVSaG9uZTENMAsGA1UEBwwETHlv
bjEOMAwGA1UECgwFTWV0cm8xDjAMBgNVBAsMBU1ldHJvMQ4wDAYDVQQDDAVNZXRy
bzEtMCsGCSqGSIb3DQEJARYeZ3VpbGhlbS5jYXJyb25Ac29wcmFzdGVyaWEuY29t
MB4XDTIxMDczMDA5MTgzMFoXDTIyMDczMDA5MTgzMFowgYsxCzAJBgNVBAYTAkZS
MQ4wDAYDVQQIDAVSaG9uZTENMAsGA1UEBwwETHlvbjEOMAwGA1UECgwFTWV0cm8x
DjAMBgNVBAsMBU1ldHJvMQ4wDAYDVQQDDAVNZXRybzEtMCsGCSqGSIb3DQEJARYe
Z3VpbGhlbS5jYXJyb25Ac29wcmFzdGVyaWEuY29tMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAvMjRUpX6Az1ClIxHwRCsrUl6F0IoTbqI8+mr9AN4IKmo
R7TbUVQQTpGM0UBTqzd/+24IlD6E7N2SlU6rTRF9/aj+3FMnqS7dXBuGlHivbsQ7
NYd4FgLs+TmUfN256vZ/zKd5ZBOqeKf0h7fF8Jk9G8bsQpdaG0LYrnwLFCZIx5Cf
fnecrx5LZ+drwCwp/xdxB4oNo86bBYv1Sv/HbbJc7BZuGzLuJd8lR9EUhdyLpF7o
mX7RxBxDzKKBX+OahiSSPbux6X2jV3kysXXc0D1d4HdKBVCPb2+FvCGgVu5K0rFT
ZsZ/qgjHZp0zK5ILdZ0fKY1NYQnTVlJJq3n3xsAwcwIDAQABo1MwUTAdBgNVHQ4E
FgQU2kCTqeiTfKmXgyQQDU+ijcVq52QwHwYDVR0jBBgwFoAU2kCTqeiTfKmXgyQQ
DU+ijcVq52QwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANAb2
hjsLDggmrcTw5O5LxO92GlzFPzdc5GWpyJVHJn0D0hDgF8Q4yYJoL90M4huFYMKY
9C3xJ0s01QgRsE/1uPChGMz9vvvG/HfDnLeq/qwAZcmUlojuAzxO3lgNGzIxGTox
9/bM2Zg+ieYvAerXx134r/T467KQRHYo/6qIjmJzf2AjXKhKDMvSfLnuFmdQ4OOD
gwxU1BNcF+s67vN3uZIZOsBdjTiwn7UAWgA9r8AaKZsU2kRf5z8v4JKNzE52vx4/
WVjxAKj+U6F5eg7URvl+A6BEZS4pgHTGyrSYWeBYHW61YnSGGc5JxCewi3ClYnNm
98UE6eyxqT42Wd9E2Q==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAvMjRUpX6Az1ClIxHwRCsrUl6F0IoTbqI8+mr9AN4IKmoR7Tb
UVQQTpGM0UBTqzd/+24IlD6E7N2SlU6rTRF9/aj+3FMnqS7dXBuGlHivbsQ7NYd4
FgLs+TmUfN256vZ/zKd5ZBOqeKf0h7fF8Jk9G8bsQpdaG0LYrnwLFCZIx5Cffnec
rx5LZ+drwCwp/xdxB4oNo86bBYv1Sv/HbbJc7BZuGzLuJd8lR9EUhdyLpF7omX7R
xBxDzKKBX+OahiSSPbux6X2jV3kysXXc0D1d4HdKBVCPb2+FvCGgVu5K0rFTZsZ/
qgjHZp0zK5ILdZ0fKY1NYQnTVlJJq3n3xsAwcwIDAQABAoIBAEa/9/q8bDskjTxA
qm+HgT9cJx5qU9J1kvfeXewQFMgg7QkalYsQNyBK2fxd4D1tilA/N4XfPE/M5i+A
kSBL6uzSldkabOWxw5HOWwxvgke/0PTJtU6CgC8SzjxRmGifQtv+87Z2zE91mmX2
kH8P3t/3F/oI4OMQlUYnBLvjcSkgu5EVg41RD0O0T1KnVYXlXdQ6F4mL1eqTMi7+
cOnZugonWwUOZiLDHg2PmDj2CfDIiFr1O90z/6Gf+bGn5mEVWEHgqIvNXqEO+ZvV
n6oXYpbLP58ZBu4+EOX49yA5IuZWGC30KtyYx/+aY1yOYJCjJ5TybPylTvmccUKV
QFXNMLkCgYEA84cyZY/6SswOYDWT3bXAb9k6FwDLCzZEB1C6HJKKPu7SO7jFv8le
F8fVYFf9kgFLLamEEPwlXYgBaaMd9PfdbUSttJ28644fXw3cmNKOdVqNIclQ+5Qv
SEFuQZnmMI8mRoSkoS8S0LnwSgbdFmJsOp2UyJtA9FUPM1Bmnysv9ocCgYEAxnPl
zoSU4109uzN47uojFyLTtoSgfG3TEbIDYL7MpVK7Kdf+bzY1D3PQE0XqCPXyFU91
bxdeVt86TCic6cOljXt1QaKJK0pBsMFSn59tYc24egfbCLzLfYdFsqxPdRXOlg9w
Bdm9uaIc5WbwAKT1wA3/e17KqEuYQVcOsBkXxbUCgYEA3ut4F34HOGa4FhuVQlM4
V2o6gYoJUzcWaLfd1X/Sqq8EpQSLh/a1egFD8lA5Xmhuv9q3eLDHJ2nzGRbmujIW
cAp5E6d8owdS8jkHIKFFJGmCXVYiGljYO0Uhv004aGTxPYX4CU/E4moCalEEGpR3
y+QpqMRtXftq0acceUEYQ4sCgYEAgboXeoXOskYsaWg/YKZro98fWIugQValiEa+
0IpJSrUnJjZM27QqwVwqo76rO4pS4r1bffBfuhgYitptoJ5FZZRT1UEDu+nBwWLO
uQvFvA53zkW+WBraRsebty3OjzJfJlcXGqv4yxV/IDLHJxSulQpRfPs9LQClmejn
8BOnJ5UCgYEA2UOrEkH5pROr8WmqBaMKaIbUcG/ASRvhN8CuaMZBU5mTKg79McYI
1QY3MDmUekRIeGb+vMfKOweGO3dt9ZhJO1wZr4x/mFjWgHHtKfdydowShnyO6I/Q
EVICHLfX8SoshYJabv33CYyOalX6tlj++HwEe4iA4ECfqWBzAS4w7Eo=
-----END RSA PRIVATE KEY-----
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIg6hUy7m+qJYCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECMyxy5+YOzwlBIIEyAeyjCYgTPl+
JAax0tlKp6NSEB7QxmXYAewV6XY+xfvjeWg/p+qpgQJbTwGbw6JuCejDsX98Jq6F
4N9H0B3pLkbxsLawRC2xnQdQp72rex48T9/Sohc5hO2rqi+29ivmt21xhbkakRYD
i/y+ZRrJdUfvMNg58QVJFHjYZa+R1sieW9jcQ/okfiow2EnBRAy1StgudeXFrJuo
H7gXWK46rnuGCZ+SAOxMyfo5ckDYoG3Ps/9FeuDYpN1f6suTwxkjqSK4OTf2aXIh
iVIoGtTuaTmRH+WHsgBve9qudqSPjHuaKo+ydD2OQNr564XDDg08WiKnyziOTHPF
WRvoD7uPxCSlvlolcHgRInD2a5qHvc3kcv5/brNPvRj1jfx5u5SgyPWtYQeXwDul
nsq7LkDrkzl22CXxQEJAjIINvBe39i0aZW2xVbK4MJwHMOr1N3UDQXFIi6I0OZP0
bEBR4md50/3FYxCceCxx10/+DewugykchKSF0Qha3Nw2PwPKaw6OGAM7hl8JuMcg
xEqRCpfs2eO3GCnxPOa/pzUB8LlJCY9cXNSiEGz3AMQl/Mn0+L69U4PbPHCgi4dw
0V3LzdMTROgh6QB0D54SaaFQx0tGJVf5b+HOk/aCe9UeGJpwTmaQZTGMq6YQlVbo
SN/OLI6N6rRKiAncf/DYn3h2u+DkQ8Ad9LFRB3BAtIP2+fTg63hfpNlPNqBTUy0o
5ZaZGdLmY+WdMIA1MgwRamjq40lP6WheESfhKdJMf+Ti4ta1MFNu7l8/OdsZa5JM
Dp1xqhhuhzjPAcQhllTto7MKpka0fKCaA//3XtPzrO6GQeAa1yLyMZUzgYdeopds
N5I4obvh2+uIAHb6vUUmopiJf1LIqUraV0kchHNo3A/XvgxWr/8Pv2e4Lf/zWvJi
MMRL6okbmtSs5ocMDFWfo1b9qvUKTSa9C5avm5ALLvttOS9DFdyJvg/U8fgIInrN
rN3enNwxI/43ClqCFtF9sm6CYpsVZT8N4zvFwOmMZIkuUD3n3oTNKnXBRr/QYlzo
OIPveJOo9eMkORpgJES0yEGnoeKVLY+h9QUDn9RJXxXpf758Mbofp8LX26eF69Ng
QfCdnqOcEVGdNQuQZFHFc84nVvL49TqRset5O7PiWN923vga78IC/yzgF0iwIvI8
yEclOga1HFC97Cy43eh1qtfi2g+r/s7uIamaxxV4KwfAcX1cTgyr7Rz1b3q+1UKn
2DuqoPaTjiNzCwVMHfcZAPfrHlRnw4DALDS3gH+aRKa5SyZ2SoQX/jUVE0+chBnm
DeeHKUraCK1FQWDRn5oDocy3yNaDz/b/2hzc8Nxl7snjPlkYzqRIOsVsUzMsRw/a
5E/tnd/99U/7t6b++emGopSLpUSqjLfKN3dm063jBPHKsSpNumsU6I00qlZkXCoB
8Usqt+GszrSt9XtLvK1Su5KFjhWJTICIy3lPw056peQZAv1I1ARZcecUDyLB9wwo
wmkRPpSmALLvRLdKvQnUk/oOnnqbSJFA5pGbyNaH/0q2CYjaDi8u7UlqmRezEw9a
zEA0N10VyXZKxss9F0KaTHrsMMfjFfKSaTyxzqqNUCa8QbezyAG+ISRua2smyQbB
4AkwJDFwXtzPUivYVtBZLw==
-----END ENCRYPTED PRIVATE KEY-----
This diff is collapsed.
......@@ -25,7 +25,6 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Backoffice Ecolyo</title>
<script src="https://cloud.tinymce.com/stable/tinymce.min.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
import React from 'react'
import { useState, useEffect } from 'react'
import { BrowserRouter } from 'react-router-dom'
import Layout from './components/Layout/Layout'
import Routes from './components/Routes/Routes'
import { useAuth } from './hooks/useAuth'
import { UserContext } from './hooks/userContext'
import { User } from './models/user.model'
import { ToastContainer } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
function App() {
const { loginUser, error, isUserLogged, getUser, logoutUser } = useAuth()
const [isLogged, setisLogged] = useState<boolean>(false)
const [user, setuser] = useState<User>()
useEffect(() => {
let subscribed = true
async function checkIsLogged() {
const userLogged = await isUserLogged()
if (userLogged) {
setisLogged(true)
setuser(getUser() as User)
} else setisLogged(false)
}
if (subscribed) checkIsLogged()
return () => {
subscribed = false
}
}, [isLogged, setisLogged])
return (
<BrowserRouter>
<Layout>
<Routes />
</Layout>
<UserContext.Provider
value={{ user, loginUser, error, isLogged, logoutUser, setisLogged }}
>
<Layout>
<Routes />
</Layout>
</UserContext.Provider>
<ToastContainer
progressClassName="toastProgress"
bodyClassName="toastBody"
/>
</BrowserRouter>
)
}
......
@import '../../styles/config/colors';
@import '../../styles/config/typography';
@import '../../styles/config/breakpoints';
.date-selector {
display: flex;
align-items: center;
......@@ -7,6 +8,10 @@
height: 3rem;
margin: auto;
max-width: 250px;
@media screen and(min-width: $width-desktop) {
position: relative;
left: -2rem;
}
.text {
@include text-large();
color: white;
......
import React, { useCallback, useState } from 'react'
import 'react-datepicker/dist/react-datepicker.css'
import { Editor } from '@tinymce/tinymce-react'
import React, { useCallback, useContext, useEffect, useState } from 'react'
import DateSelector from '../DateSelector/DateSelector'
import './editing.scss'
import { EditorService } from '../../services/editor.service'
import { MonthlyNewsService } from '../../services/monthlyNews.service'
import { UserContext, UserContextProps } from '../../hooks/userContext'
import { IMonthlyNews } from '../../models/monthlyNews.model'
import Poll from '../Poll/Poll'
import MonthlyNews from '../MonthlyNews/MonthlyNews'
import { IPoll } from '../../models/poll.model'
import Loader from '../Loader/Loader'
import Modal from '../Modal/Modal'
export type ContentItems = 'poll' | 'monthlyNews' | ''
const Editing: React.FC = () => {
const [date, setDate] = useState<Date>(new Date())
const [header, setHeader] = useState<string>('')
const [quote, setQuote] = useState<string>('')
const [question, setQuestion] = useState<string>('')
const [link, setLink] = useState<string>('')
const [isTouched, setIsTouched] = useState<boolean>(false)
const [refreshData, setRefreshData] = useState(false)
const [isLoading, setisLoading] = useState<boolean>(false)
const [warningModal, setwarningModal] = useState<boolean>(false)
const [toDelete, settoDelete] = useState<ContentItems>('')
const { user }: Partial<UserContextProps> = useContext(UserContext)
const monthlyNewsService = new MonthlyNewsService()
const handleSave = async () => {
const editorService = new EditorService()
await editorService.sendQuotation(date, header, quote)
const handleSaveMonthlyNews = async (): Promise<void> => {
if (user) {
const monthlyNewsService = new MonthlyNewsService()
await monthlyNewsService.createMonthlyReport(
date,
header,
quote,
user.xsrftoken
)
setIsTouched(false)
}
}
const handleSavePoll = async (): Promise<void> => {
if (user) {
await monthlyNewsService.createPoll(date, question, link, user.xsrftoken)
setIsTouched(false)
}
}
const handleCancel = useCallback(() => {
setQuote('')
setHeader('')
setRefreshData(true)
}, [])
const handleDeleteMonthlyNews = async (): Promise<void> => {
if (user) {
await monthlyNewsService.deleteMonthlyNews(
date.getFullYear(),
date.getMonth(),
user.xsrftoken
)
setRefreshData(true)
}
}
const handleDeletePoll = async (): Promise<void> => {
if (user) {
await monthlyNewsService.deletePoll(
date.getFullYear(),
date.getMonth(),
user.xsrftoken
)
setRefreshData(true)
}
}
const handleOpenDeleteModal = (target: ContentItems) => {
settoDelete(target)
setwarningModal(true)
}
const handleConfirmAlert = () => {
if (toDelete === 'monthlyNews') {
handleDeleteMonthlyNews()
}
if (toDelete === 'poll') {
handleDeletePoll()
}
setwarningModal(false)
}
const isEmpty = (): boolean => {
if (quote !== '' || header !== '') {
if ((quote !== '' || header !== '') && isTouched) {
return false
} else return true
}
const handleEditorChange = (
value: string,
type: 'header' | 'quote' | 'question' | 'link'
): void => {
setIsTouched(true)
if (type === 'header') {
setHeader(value)
}
if (type === 'quote') {
setQuote(value)
}
if (type === 'question') {
setQuestion(value)
}
if (type === 'link') {
setLink(value)
}
}
const resetFields = useCallback(() => {
setQuote('')
setHeader('')
setLink('')
setQuestion('')
}, [])
useEffect(() => {
let subscribed = true
resetFields()
setisLoading(true)
async function getCurrentMonthlyNews() {
if (user) {
const monthlyNewsService = new MonthlyNewsService()
const montlhyNews: IMonthlyNews | null =
await monthlyNewsService.getSingleMonthlyReport(
date.getFullYear(),
date.getMonth(),
user.xsrftoken
)
const poll: IPoll | null = await monthlyNewsService.getSinglePoll(
date.getFullYear(),
date.getMonth(),
user.xsrftoken
)
if (montlhyNews) {
setHeader(montlhyNews.header)
setQuote(montlhyNews.quote)
setIsTouched(false)
}
if (poll) {
setLink(poll.link)
setQuestion(poll.question)
setIsTouched(false)
}
}
setisLoading(false)
}
if (subscribed) {
getCurrentMonthlyNews()
}
return () => {
subscribed = false
setRefreshData(false)
}
}, [date, user, refreshData])
return (
<>
<div className="header">
<p className="title pagetitle">
Édition des informations et de la citation du mois
</p>
</div>
<div className="content">
<DateSelector date={date} setDate={setDate} isEmpty={isEmpty} />
<div className="subtitle">
<p className="title">Informations du mois</p>
</div>
<div>
<Editor
initialValue=""
init={{
menubar: false,
toolbar:
'undo redo | bold italic underline | alignleft aligncenter alignright | code',
}}
value={header}
onEditorChange={(newHeader, editor) => setHeader(newHeader)}
</div>
{isLoading ? (
<Loader />
) : (
<div className="content">
<MonthlyNews
header={header}
quote={quote}
onSave={handleSaveMonthlyNews}
onCancel={handleCancel}
handleChange={handleEditorChange}
onDelete={handleOpenDeleteModal}
/>
<div className="subtitle">
<p className="title">Citation du mois</p>
</div>
<Editor
initialValue=""
init={{
menubar: false,
toolbar:
'undo redo | bold italic underline | alignleft aligncenter alignright | code',
}}
value={quote}
onEditorChange={(newQuote, editor) => setQuote(newQuote)}
<hr />
<Poll
question={question}
link={link}
handleChange={handleEditorChange}
onSave={handleSavePoll}
onCancel={handleCancel}
onDelete={handleOpenDeleteModal}
/>
<button className="btnCancel" onClick={handleCancel}>
Annuler
</button>
<button className="btnValid" onClick={handleSave}>
Sauvegarder
</button>
</div>
</div>
)}
{warningModal && (
<Modal>
<>
<div className="modal-text">
Etes-vous sûr de vouloir supprimer{' '}
{toDelete === 'poll' ? 'ce sondage' : 'cette news mensuelle'} ?
</div>
<div className="buttons">
<button
className="btnCancel"
onClick={() => setwarningModal(false)}
>
Annuler
</button>
<button className="btnValid" onClick={handleConfirmAlert}>
Continuer
</button>
</div>
</>
</Modal>
)}
</>
)
}
......
@import '../../styles/config/typography.scss';
@import '../../styles/config/layout.scss';
.header {
position: fixed;
z-index: 1500;
width: inherit;
background: radial-gradient(
74.83% 76.97% at 50% 13.64%,
#343641 0%,
#1b1c22 100%
);
height: 5rem;
box-shadow: 0px 5px 5px rgb(0 0 0 / 20%), 0px 3px 14px rgb(0 0 0 / 12%),
0px 8px 10px rgb(0 0 0 / 14%);
height: $navigator-height;
box-shadow: 0px 5px 5px rgb(0 0 0 / 0%), 0px 3px 14px rgb(0 0 0 / 0%),
0px 8px 10px rgb(0 0 0 / 15%);
padding: 1.7rem;
}
.content {
padding: 1rem;
margin-top: $navigator-height;
}
.subtitle {
margin: 1rem 0;
}
hr {
margin: 2rem 1rem;
}
.buttons {
display: flex;
}
......@@ -12,6 +12,8 @@
left: 0;
width: $menu-width;
height: 100vh;
z-index: 1501;
@media screen and(max-width: $width-phone) {
width: 0;
display: none;
......@@ -23,6 +25,7 @@
left: 0;
height: $navbar-height;
width: 100%;
z-index: 1500;
}
.wrapper {
flex: 1;
......@@ -38,5 +41,6 @@
}
main {
width: 100%;
height: inherit;
}
}
import './loader.scss'
const Loader: React.FC = () => {
return (
<div className="loader-container">
<div className="loader">Loading...</div>
</div>
)
}
export default Loader
@import '../../styles/config/colors.scss';
.loader-container {
width: 100%;
height: 80vh;
display: flex;
overflow: hidden;
}
.loader {
font-size: 10px;
margin: auto;
text-indent: -9999em;
width: 11em;
height: 11em;
border-radius: 50%;
background: $gold;
background: -moz-linear-gradient(left, $gold 10%, rgba(255, 255, 255, 0) 42%);
background: -webkit-linear-gradient(
left,
$gold 10%,
rgba(255, 255, 255, 0) 42%
);
background: -o-linear-gradient(left, $gold 10%, rgba(255, 255, 255, 0) 42%);
background: -ms-linear-gradient(left, $gold 10%, rgba(255, 255, 255, 0) 42%);
background: linear-gradient(to right, $gold 10%, rgba(255, 255, 255, 0) 42%);
position: relative;
-webkit-animation: load3 1.4s infinite linear;
animation: load3 1.4s infinite linear;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.loader:before {
width: 50%;
height: 50%;
background: $gold;
border-radius: 100% 0 0 0;
position: absolute;
top: 0;
left: 0;
content: '';
}
.loader:after {
background: $dark-light;
width: 75%;
height: 75%;
border-radius: 50%;
content: '';
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
@-webkit-keyframes load3 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes load3 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
import React, { useContext } from 'react'
import { useAuth } from '../../hooks/useAuth'
import { UserContext } from '../../hooks/userContext'
import './login.scss'
const Login: React.FC = () => {
const { loginUser, setisLogged } = useContext(UserContext)
const handleClickLogin = async () => {
if (loginUser && setisLogged) {
await loginUser()
setisLogged(true)
}
}
return (
<div className="login">
<div className="container">
<h1>Bienvenue sur le Backoffice d'Ecolyo !</h1>
<button className="btnValid" onClick={handleClickLogin}>
Login
</button>
</div>
</div>
)
}
export default Login
.login {
display: flex;
width: 100%;
height: inherit;
min-height: 95vh;
.container {
margin: auto;
}
}
import React, { useState } from 'react'
import React, { useContext } from 'react'
import routes from '../../constants/routes.json'
import logo from '../../assets/icons/ecolyo-logo.svg'
import './menu.scss'
import { NavLink } from 'react-router-dom'
import { UserContext } from '../../hooks/userContext'
const Menu: React.FC = () => {
const [isLogged] = useState<boolean>(false)
const { isLogged, logoutUser, setisLogged } = useContext(UserContext)
const handleLogout = () => {
if (logoutUser && setisLogged) {
logoutUser()
setisLogged(false)
}
}
return (
<nav className={'menu'}>
<div className="logo-container">
......@@ -23,7 +30,9 @@ const Menu: React.FC = () => {
</div>
<div className="administration">
{isLogged ? (
<p>Account</p>
<button className="btnValid logButton" onClick={handleLogout}>
Logout
</button>
) : (
<NavLink to="/login" activeClassName="active">
Login
......
......@@ -10,6 +10,9 @@
border-top: unset;
border-right: unset;
background-color: $grey-dark;
position: relative;
z-index: 1501;
.logo-container {
display: flex;
}
......@@ -35,7 +38,8 @@
.administration {
margin-top: auto;
}
}
.mobileMenu {
margin-left: -$menu-width;
.logButton {
width: 100px;
min-width: 0;
}
}
......@@ -8,7 +8,7 @@
position: fixed;
top: 0;
width: 100%;
z-index: 999;
z-index: 9999;
.modal-container {
align-items: center;
......
import { Editor } from '@tinymce/tinymce-react'
import React from 'react'
import { ContentItems } from '../Editing/Editing'
interface MonthlyNewsProps {
onSave: () => Promise<void>
onCancel: () => void
header: string
quote: string
handleChange: (
value: string,
type: 'header' | 'quote' | 'question' | 'link'
) => void
onDelete: (target: ContentItems) => void
}
const MonthlyNews: React.FC<MonthlyNewsProps> = ({
onSave,
onCancel,
header,
quote,
handleChange,
onDelete,
}: MonthlyNewsProps) => {
return (
<>
<div className="subtitle">
<p className="title">Informations du mois</p>
</div>
<div>
<Editor
init={{
menubar: false,
toolbar:
'undo redo | bold italic underline | alignleft aligncenter alignright | code | ecolyoLink',
}}
value={header}
onEditorChange={(newHeader) => handleChange(newHeader, 'header')}
/>
<div className="subtitle">
<p className="title">Citation du mois</p>
</div>
<Editor
init={{
menubar: false,
toolbar:
'undo redo | bold italic underline | alignleft aligncenter alignright | code | ecolyoLink',
setup: function (editor) {
editor.ui.registry.addMenuButton('ecolyoLink', {
text: 'Lien Ecolyo',
fetch: function (callback) {
var items: any = [
{
type: 'menuitem',
text: 'consumption',
onAction: function () {
editor.insertContent(
'&nbsp;<a href="https://ecolyo.{cozyUrl}/consumption">Page de consommation</a>'
)
},
},
{
type: 'menuitem',
text: 'challenges',
onAction: function () {
editor.insertContent(
'&nbsp;<a href="https://ecolyo.{cozyUrl}/challenges">Page challenges</a>'
)
},
},
{
type: 'menuitem',
text: 'ecogestures',
onAction: function () {
editor.insertContent(
'&nbsp;<a href="https://ecolyo.{cozyUrl}/ecogestures">Page ecogestures</a>'
)
},
},
{
type: 'menuitem',
text: 'analysis',
onAction: function () {
editor.insertContent(
'&nbsp;<a href="https://ecolyo.{cozyUrl}/analysis">Page analyse</a>'
)
},
},
{
type: 'menuitem',
text: 'options',
onAction: function () {
editor.insertContent(
'&nbsp;<a href="https://ecolyo.{cozyUrl}/options">Page options</a>'
)
},
},
]
callback(items)
},
})
},
}}
value={quote}
onEditorChange={(newQuote) => handleChange(newQuote, 'quote')}
/>
<div className="buttons">
<button className="btnCancel" onClick={onCancel}>
Annuler
</button>
<button className="btnValid" onClick={onSave}>
Sauvegarder
</button>
<button className="btnDelete" onClick={() => onDelete('monthlyNews')}>
Supprimer
</button>
</div>
</div>
</>
)
}
export default MonthlyNews