diff --git a/src/components/Editing/Editing.tsx b/src/components/Editing/Editing.tsx index 3c38ae101218b44e047b1d543f6b17dafccd1863..9fef0a70d11f73897c4ea7bbce838226e8b6b815 100644 --- a/src/components/Editing/Editing.tsx +++ b/src/components/Editing/Editing.tsx @@ -7,6 +7,7 @@ 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' const Editing: React.FC = () => { const [date, setDate] = useState<Date>(new Date()) @@ -15,10 +16,10 @@ const Editing: React.FC = () => { const [question, setQuestion] = useState<string>('') const [link, setLink] = useState<string>('') const [isTouched, setIsTouched] = useState<boolean>(false) - const [isMontlyNewsUpdating, setisMontlyNewsUpdating] = - useState<boolean>(false) - const [isPollUpdating, setisPollUpdating] = useState<boolean>(false) + const [refreshData, setRefreshData] = useState(false) + const [isLoading, setisLoading] = useState<boolean>(false) const { user }: Partial<UserContextProps> = useContext(UserContext) + const monthlyNewsService = new MonthlyNewsService() const handleSaveMonthlyNews = async (): Promise<void> => { if (user) { @@ -29,27 +30,39 @@ const Editing: React.FC = () => { quote, user.xsrftoken ) - console.log('saved') setIsTouched(false) } } const handleSavePoll = async (): Promise<void> => { if (user) { - const monthlyNewsService = new MonthlyNewsService() await monthlyNewsService.createPoll(date, question, link, user.xsrftoken) - console.log('saved') setIsTouched(false) } } - const handleCancelMonthlyNews = useCallback(() => { - setQuote('') - setHeader('') + const handleCancel = useCallback(() => { + setRefreshData(true) }, []) - const handleCancelPoll = useCallback(() => { - setLink('') - setQuestion('') - }, []) + 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 isEmpty = (): boolean => { if ((quote !== '' || header !== '') && isTouched) { @@ -85,6 +98,7 @@ const Editing: React.FC = () => { useEffect(() => { let subscribed = true resetFields() + setisLoading(true) async function getCurrentMonthlyNews() { if (user) { const monthlyNewsService = new MonthlyNewsService() @@ -103,24 +117,24 @@ const Editing: React.FC = () => { setHeader(montlhyNews.header) setQuote(montlhyNews.quote) setIsTouched(false) - setisMontlyNewsUpdating(true) } if (poll) { setLink(poll.link) setQuestion(poll.question) setIsTouched(false) - setisPollUpdating(true) } } + + setisLoading(false) } if (subscribed) { getCurrentMonthlyNews() - console.log('updating?', isMontlyNewsUpdating) } return () => { subscribed = false + setRefreshData(false) } - }, [date, user]) + }, [date, user, refreshData]) return ( <> @@ -129,24 +143,30 @@ const Editing: React.FC = () => { Édition des informations et de la citation du mois </p> </div> - <div className="content"> - <DateSelector date={date} setDate={setDate} isEmpty={isEmpty} /> - <MonthlyNews - header={header} - quote={quote} - onSave={handleSaveMonthlyNews} - onCancel={handleCancelMonthlyNews} - handleChange={handleEditorChange} - /> - <hr /> - <Poll - question={question} - link={link} - handleChange={handleEditorChange} - onSave={handleSavePoll} - onCancel={handleCancelPoll} - /> - </div> + {isLoading ? ( + <Loader /> + ) : ( + <div className="content"> + <DateSelector date={date} setDate={setDate} isEmpty={isEmpty} /> + <MonthlyNews + header={header} + quote={quote} + onSave={handleSaveMonthlyNews} + onCancel={handleCancel} + handleChange={handleEditorChange} + onDelete={handleDeleteMonthlyNews} + /> + <hr /> + <Poll + question={question} + link={link} + handleChange={handleEditorChange} + onSave={handleSavePoll} + onCancel={handleCancel} + onDelete={handleDeletePoll} + /> + </div> + )} </> ) } diff --git a/src/components/Editing/editing.scss b/src/components/Editing/editing.scss index 9d1a17d1708ae9d7f060bde29e46c8ef025a76d4..ecbd98691d21a350bdca44cf667fb7688f40d3f1 100644 --- a/src/components/Editing/editing.scss +++ b/src/components/Editing/editing.scss @@ -19,3 +19,6 @@ hr { margin: 2rem 1rem; } +.buttons { + display: flex; +} diff --git a/src/components/Loader/Loader.tsx b/src/components/Loader/Loader.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c282c3ce296aec13951f8eec6ead3242374277ff --- /dev/null +++ b/src/components/Loader/Loader.tsx @@ -0,0 +1,10 @@ +import './loader.scss' +const Loader: React.FC = () => { + return ( + <div className="loader-container"> + <div className="loader">Loading...</div> + </div> + ) +} + +export default Loader diff --git a/src/components/Loader/loader.scss b/src/components/Loader/loader.scss new file mode 100644 index 0000000000000000000000000000000000000000..872512dcdcfeff6f48209b63a669d4bf1b90ad45 --- /dev/null +++ b/src/components/Loader/loader.scss @@ -0,0 +1,75 @@ +@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); + } +} diff --git a/src/components/MonthlyNews/MonthlyNews.tsx b/src/components/MonthlyNews/MonthlyNews.tsx index 26d5a3012faf92bb7fd9d0bca73a4c2dc1fd1f7c..9920226b4a808731f70442e0182d4131fb362c3e 100644 --- a/src/components/MonthlyNews/MonthlyNews.tsx +++ b/src/components/MonthlyNews/MonthlyNews.tsx @@ -2,7 +2,7 @@ import { Editor } from '@tinymce/tinymce-react' import React from 'react' interface MonthlyNewsProps { - onSave: () => void + onSave: () => Promise<void> onCancel: () => void header: string quote: string @@ -10,6 +10,7 @@ interface MonthlyNewsProps { value: string, type: 'header' | 'quote' | 'question' | 'link' ) => void + onDelete: () => Promise<void> } const MonthlyNews: React.FC<MonthlyNewsProps> = ({ onSave, @@ -17,6 +18,7 @@ const MonthlyNews: React.FC<MonthlyNewsProps> = ({ header, quote, handleChange, + onDelete, }: MonthlyNewsProps) => { return ( <> @@ -101,12 +103,17 @@ const MonthlyNews: React.FC<MonthlyNewsProps> = ({ value={quote} onEditorChange={(newQuote) => handleChange(newQuote, 'quote')} /> - <button className="btnCancel" onClick={onCancel}> - Annuler - </button> - <button className="btnValid" onClick={onSave}> - Sauvegarder - </button> + <div className="buttons"> + <button className="btnCancel" onClick={onCancel}> + Annuler + </button> + <button className="btnValid" onClick={onSave}> + Sauvegarder + </button> + <button className="btnDelete" onClick={onDelete}> + Supprimer + </button> + </div> </div> </> ) diff --git a/src/components/Poll/Poll.tsx b/src/components/Poll/Poll.tsx index 075f11214591f526129c53db77b115f36d267f26..0cc8fb34170efd92b927ac95842b024a732cc862 100644 --- a/src/components/Poll/Poll.tsx +++ b/src/components/Poll/Poll.tsx @@ -9,8 +9,9 @@ interface PollProps { value: string, type: 'header' | 'quote' | 'question' | 'link' ) => void - onSave: () => void + onSave: () => Promise<void> onCancel: () => void + onDelete: () => Promise<void> } const Poll: React.FC<PollProps> = ({ question, @@ -18,6 +19,7 @@ const Poll: React.FC<PollProps> = ({ handleChange, onSave, onCancel, + onDelete, }: PollProps) => { const handleChangeLink = (e: ChangeEvent<HTMLInputElement>) => { handleChange(e.target.value, 'link') @@ -48,12 +50,17 @@ const Poll: React.FC<PollProps> = ({ handleChange(newQuestion, 'question') } /> - <button className="btnCancel" onClick={onCancel}> - Annuler - </button> - <button className="btnValid" onClick={onSave}> - Sauvegarder - </button> + <div className="buttons"> + <button className="btnCancel" onClick={onCancel}> + Annuler + </button> + <button className="btnValid" onClick={onSave}> + Sauvegarder + </button> + <button className="btnDelete" onClick={onDelete}> + Supprimer + </button> + </div> </div> </div> ) diff --git a/src/services/monthlyNews.service.ts b/src/services/monthlyNews.service.ts index 905a4e8106fd5ece295c570ac1b55baaafde8000..eca0a3347b497532d1a9fe35a2e08b4170b132cb 100644 --- a/src/services/monthlyNews.service.ts +++ b/src/services/monthlyNews.service.ts @@ -20,7 +20,7 @@ export class MonthlyNewsService { token: string ): Promise<void> => { try { - await axios.post( + await axios.put( `${this._apiUrl}api/admin/monthlyNews`, { month: date.getMonth(), @@ -34,7 +34,7 @@ export class MonthlyNewsService { }, } ) - toast.success('Monthly news succesfully created !') + toast.success('Monthly news succesfully saved !') } catch (e) { toast.error('Failed to create monthly news') console.log(e) @@ -58,7 +58,6 @@ export class MonthlyNewsService { }, } ) - console.log('monthlyFetched', data) if (data == {}) { return null } @@ -110,7 +109,7 @@ export class MonthlyNewsService { token: string ): Promise<void> => { try { - await axios.post( + await axios.put( `${this._apiUrl}api/admin/poll`, { month: date.getMonth(), @@ -124,7 +123,7 @@ export class MonthlyNewsService { }, } ) - toast.success('Poll succesfully created !') + toast.success('Poll successfully saved !') } catch (e) { toast.error('Failed to create poll') console.log(e) diff --git a/src/styles/_toast.scss b/src/styles/_toast.scss index d9073c1932b5bfa240f71cd3f46dd39ec8194008..9654eb81c1c93cc26ff6a5ffcab83c7a0f1e0d00 100644 --- a/src/styles/_toast.scss +++ b/src/styles/_toast.scss @@ -1,10 +1,25 @@ @import 'config/colors'; +.Toastify__toast { + background: #fafafa !important; +} +.Toastify__toast--success { + .toastBody { + color: #73a839; + } + .toastProgress { + background: #73a839 !important; + } +} + +.Toastify__toast--error { + .toastBody { + color: #c71c22; + } + .toastProgress { + background: #c71c22 !important; + } +} .toastBody { - color: $dark-light; font-size: 1rem !important; - font-weight: bold; -} -.toastProgress { - background: white !important; } diff --git a/src/styles/config/_typography.scss b/src/styles/config/_typography.scss index 147bb9b552e60d92545914e27dd352f281bbe570..380fea218b22eeb87ecce61e4ec1cd350c88c748 100644 --- a/src/styles/config/_typography.scss +++ b/src/styles/config/_typography.scss @@ -59,6 +59,19 @@ $main-spacing: 4px; background: darken($text-grey, 20%); } } +.btnDelete { + @include baseButton(); + display: inline-block; + margin-left: auto; + background: $dark-light; + color: $gold; + border: 1px solid $gold; + &:hover { + background: $dark-light; + color: $text-grey; + border: 1px solid $text-grey; + } +} .input-dark { display: inline-block; margin-left: 0.5rem;