From 538df29af366df456d70d63a4a26b2ec54dbc572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20PAILHAREY?= <rpailharey@grandlyon.com> Date: Wed, 2 Mar 2022 14:43:01 +0000 Subject: [PATCH] feat: added MailSubject section --- src/components/Editing/CustomEditor.tsx | 4 +- src/components/Editing/Editing.tsx | 109 +++++++++++++++------ src/components/ImagePicker/ImagePicker.tsx | 9 +- src/components/MailSuject/mailSubject.scss | 10 ++ src/components/MailSuject/mailSubject.tsx | 53 ++++++++++ src/components/MonthlyInfo/MonthlyInfo.tsx | 9 +- src/components/MonthlyNews/MonthlyNews.tsx | 2 +- src/components/Poll/Poll.tsx | 2 +- src/models/mailSubject.model.ts | 5 + src/services/newsletter.service.ts | 95 ++++++++++++++++++ 10 files changed, 262 insertions(+), 36 deletions(-) create mode 100644 src/components/MailSuject/mailSubject.scss create mode 100644 src/components/MailSuject/mailSubject.tsx create mode 100644 src/models/mailSubject.model.ts diff --git a/src/components/Editing/CustomEditor.tsx b/src/components/Editing/CustomEditor.tsx index d30a02e1..9d842295 100644 --- a/src/components/Editing/CustomEditor.tsx +++ b/src/components/Editing/CustomEditor.tsx @@ -7,10 +7,10 @@ import './customEditor.scss' interface CustomEditorProps { baseState: EditorState - editorType: 'info' | 'title' | 'content' | 'question' | 'link' + editorType: 'info' | 'title' | 'content' | 'question' | 'link' | 'subject' handleChange: ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'subject' ) => void } diff --git a/src/components/Editing/Editing.tsx b/src/components/Editing/Editing.tsx index 0522e5d7..543f80bd 100644 --- a/src/components/Editing/Editing.tsx +++ b/src/components/Editing/Editing.tsx @@ -5,6 +5,8 @@ import { UserContext, UserContextProps } from '../../hooks/userContext' import { IMonthlyNews } from '../../models/monthlyNews.model' import { IMonthlyInfo } from '../../models/monthlyInfo.model' import { IPoll } from '../../models/poll.model' +import { IMailSubject } from '../../models/mailSubject.model' +import MailSubject from '../MailSuject/mailSubject' import Poll from '../Poll/Poll' import MonthlyInfo from '../MonthlyInfo/MonthlyInfo' import MonthlyNews from '../MonthlyNews/MonthlyNews' @@ -12,7 +14,12 @@ import Loader from '../Loader/Loader' import Modal from '../Modal/Modal' import './editing.scss' -export type ContentItems = 'monthlyInfo' | 'monthlyNews' | 'poll' | '' +export type ContentItems = + | 'monthlyInfo' + | 'monthlyNews' + | 'poll' + | 'subject' + | '' const Editing: React.FC = () => { // Fonctional rule : @@ -28,6 +35,7 @@ const Editing: React.FC = () => { const [date, setDate] = useState<Date>(getCurrentNewsletterDate()) const [info, setInfo] = useState<string>('') const [title, setTitle] = useState<string>('') + const [subject, setSubject] = useState<string>('') const [imageURL, setImageURL] = useState<string>('') const [content, setContent] = useState<string>('') const [question, setQuestion] = useState<string>('') @@ -40,6 +48,14 @@ const Editing: React.FC = () => { const { user }: Partial<UserContextProps> = useContext(UserContext) const newsletterService = new NewsletterService() + const handleSaveSubject = async (): Promise<void> => { + if (user) { + const newsletterService = new NewsletterService() + await newsletterService.saveMailSubject(date, subject, user.xsrftoken) + setIsTouched(false) + } + } + const handleSaveMonthlyInfo = async (): Promise<void> => { if (user) { const newsletterService = new NewsletterService() @@ -75,6 +91,12 @@ const Editing: React.FC = () => { setRefreshData(true) }, []) + const handleDeleteMailSubject = async (): Promise<void> => { + if (user) { + await newsletterService.deleteMailSubject(date, user.xsrftoken) + setRefreshData(true) + } + } const handleDeleteMonthlyInfo = async (): Promise<void> => { if (user) { await newsletterService.deleteMonthlyInfo(date, user.xsrftoken) @@ -98,6 +120,9 @@ const Editing: React.FC = () => { setwarningModal(true) } const handleConfirmAlert = () => { + if (toDelete === 'subject') { + handleDeleteMailSubject() + } if (toDelete === 'monthlyInfo') { handleDeleteMonthlyInfo() } @@ -117,42 +142,50 @@ const Editing: React.FC = () => { content !== '' || question !== '' || imageURL !== '' || - link !== '') && + link !== '' || + subject !== '') && isTouched ) { return false } else return true } - const handleEditorChange = useCallback( - ( - value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' | 'image' - ): void => { - setIsTouched(true) - if (type === 'info') { - setInfo(value) - } - if (type === 'title') { - setTitle(value) - } - if (type === 'content') { - setContent(value) - } - if (type === 'question') { - setQuestion(value) - } - if (type === 'link') { - setLink(value) - } - if (type === 'image') { - setImageURL(value) - } - }, - [] - ) - + const handleEditorChange = ( + value: string, + type: + | 'info' + | 'title' + | 'content' + | 'question' + | 'link' + | 'image' + | 'subject' + ): void => { + setIsTouched(true) + if (type === 'info') { + setInfo(value) + } + if (type === 'title') { + setTitle(value) + } + if (type === 'content') { + setContent(value) + } + if (type === 'question') { + setQuestion(value) + } + if (type === 'link') { + setLink(value) + } + if (type === 'subject') { + setSubject(value) + } + if (type === 'image') { + setImageURL(value) + } + } const resetFields = useCallback(() => { + setSubject('') setImageURL('') setInfo('') setTitle('') @@ -168,6 +201,8 @@ const Editing: React.FC = () => { async function getCurrentMonthlyNews() { if (user) { const newsletterService = new NewsletterService() + const mailSubject: IMailSubject | null = + await newsletterService.getSingleMailSubject(date, user.xsrftoken) const montlhyInfo: IMonthlyInfo | null = await newsletterService.getSingleMonthlyInfo(date, user.xsrftoken) const montlhyNews: IMonthlyNews | null = @@ -176,6 +211,10 @@ const Editing: React.FC = () => { date, user.xsrftoken ) + if (mailSubject) { + setSubject(mailSubject.subject) + setIsTouched(false) + } if (montlhyInfo) { setInfo(montlhyInfo.info) setImageURL(montlhyInfo.image) @@ -213,6 +252,14 @@ const Editing: React.FC = () => { <Loader /> ) : ( <div className="content"> + <MailSubject + onSave={handleSaveSubject} + onCancel={handleCancel} + subject={subject} + handleChange={handleEditorChange} + onDelete={handleOpenDeleteModal} + ></MailSubject> + <hr /> <MonthlyInfo info={info} onSave={handleSaveMonthlyInfo} @@ -250,6 +297,8 @@ const Editing: React.FC = () => { ? 'cette info mensuelle ' : toDelete === 'monthlyNews' ? 'cette news mensuelle' + : toDelete === 'subject' + ? 'cet objet' : 'ce sondage'}{' '} ? </div> diff --git a/src/components/ImagePicker/ImagePicker.tsx b/src/components/ImagePicker/ImagePicker.tsx index 7362ea59..df90034f 100644 --- a/src/components/ImagePicker/ImagePicker.tsx +++ b/src/components/ImagePicker/ImagePicker.tsx @@ -9,7 +9,14 @@ interface ImagePickerProps { imageURL: string handleChange: ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' | 'image' + type: + | 'info' + | 'title' + | 'content' + | 'question' + | 'link' + | 'image' + | 'subject' ) => void } diff --git a/src/components/MailSuject/mailSubject.scss b/src/components/MailSuject/mailSubject.scss new file mode 100644 index 00000000..87f0d6d6 --- /dev/null +++ b/src/components/MailSuject/mailSubject.scss @@ -0,0 +1,10 @@ +.mailSubject { + margin: 2rem 0; + .title { + margin: 1rem 0; + } + input { + min-width: 300px; + margin-left: 0; + } +} diff --git a/src/components/MailSuject/mailSubject.tsx b/src/components/MailSuject/mailSubject.tsx new file mode 100644 index 00000000..6f06da5b --- /dev/null +++ b/src/components/MailSuject/mailSubject.tsx @@ -0,0 +1,53 @@ +import React, { ChangeEvent } from 'react' +import { ContentItems } from '../Editing/Editing' +import './mailSubject.scss' + +interface MailSubjectProps { + onSave: () => Promise<void> + onCancel: () => void + subject: string + handleChange: ( + value: string, + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'subject' + ) => void + onDelete: (target: ContentItems) => void +} +const MailSubject: React.FC<MailSubjectProps> = ({ + onSave, + onCancel, + subject, + handleChange, + onDelete, +}: MailSubjectProps) => { + const handleChangeSubject = (e: ChangeEvent<HTMLInputElement>) => { + handleChange(e.target.value, 'subject') + } + return ( + <div className="mailSubject"> + <h2>Objet de la newsletter</h2> + <p className="title">Objet</p> + <input + type="text" + className="input-dark" + placeholder="Par défaut : [Ecolyo] Votre bilan..." + value={subject} + onChange={handleChangeSubject} + /> + <div> + <div className="buttons"> + <button className="btnCancel" onClick={onCancel}> + Annuler + </button> + <button className="btnValid" onClick={onSave}> + Sauvegarder + </button> + <button className="btnDelete" onClick={() => onDelete('subject')}> + Supprimer + </button> + </div> + </div> + </div> + ) +} + +export default MailSubject diff --git a/src/components/MonthlyInfo/MonthlyInfo.tsx b/src/components/MonthlyInfo/MonthlyInfo.tsx index 99caef2b..f737ec7a 100644 --- a/src/components/MonthlyInfo/MonthlyInfo.tsx +++ b/src/components/MonthlyInfo/MonthlyInfo.tsx @@ -11,7 +11,14 @@ interface MonthlyInfoProps { info: string handleChange: ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' | 'image' + type: + | 'info' + | 'title' + | 'content' + | 'question' + | 'link' + | 'image' + | 'subject' ) => void onDelete: (target: ContentItems) => void imageURL: string diff --git a/src/components/MonthlyNews/MonthlyNews.tsx b/src/components/MonthlyNews/MonthlyNews.tsx index 44727c70..b04223e8 100644 --- a/src/components/MonthlyNews/MonthlyNews.tsx +++ b/src/components/MonthlyNews/MonthlyNews.tsx @@ -12,7 +12,7 @@ interface MonthlyNewsProps { content: string handleChange: ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'subject' ) => void onDelete: (target: ContentItems) => void } diff --git a/src/components/Poll/Poll.tsx b/src/components/Poll/Poll.tsx index 60957109..d8df0bf0 100644 --- a/src/components/Poll/Poll.tsx +++ b/src/components/Poll/Poll.tsx @@ -10,7 +10,7 @@ interface PollProps { link: string handleChange: ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'subject' ) => void onSave: () => Promise<void> onCancel: () => void diff --git a/src/models/mailSubject.model.ts b/src/models/mailSubject.model.ts new file mode 100644 index 00000000..89706994 --- /dev/null +++ b/src/models/mailSubject.model.ts @@ -0,0 +1,5 @@ +export interface IMailSubject { + year: number + month: number + subject: string +} diff --git a/src/services/newsletter.service.ts b/src/services/newsletter.service.ts index 7a27ccca..f0676360 100644 --- a/src/services/newsletter.service.ts +++ b/src/services/newsletter.service.ts @@ -1,9 +1,104 @@ import axios from 'axios' +import { IMailSubject } from '../models/mailSubject.model' import { IMonthlyNews } from '../models/monthlyNews.model' import { IMonthlyInfo } from '../models/monthlyInfo.model' import { IPoll } from '../models/poll.model' import { toast } from 'react-toastify' export class NewsletterService { + /** + * Saves a mail subject for selected month + * @param date + * @param subject + * @param token + */ + public saveMailSubject = async ( + date: Date, + subject: string, + token: string + ): Promise<void> => { + try { + await axios.put( + `/api/admin/mailSubject`, + { + month: date.getMonth() + 1, + year: date.getFullYear(), + subject: subject, + }, + { + headers: { + 'XSRF-TOKEN': token, + }, + } + ) + toast.success('Mail subject succesfully saved !') + } catch (e: any) { + if (e.response.status === 403) { + toast.error( + "Unauthorized : You don't have the rights to do this operation" + ) + } else { + toast.error('Failed to create mail subject') + } + console.error(e) + } + } + + /** + * Gets the mail subject for selected month + * @param date + * @param token + */ + public getSingleMailSubject = async ( + date: Date, + token: string + ): Promise<IMailSubject | null> => { + try { + const { data } = await axios.get( + `/api/admin/mailSubject/${date.getFullYear()}/${date.getMonth() + 1}`, + { + headers: { + 'XSRF-TOKEN': token, + }, + } + ) + return data as IMailSubject + } catch (e: any) { + console.error('error', e) + return null + } + } + + /** + * Deletes the mail subject for selected month + * @param date + * @param token + */ + public deleteMailSubject = async ( + date: Date, + token: string + ): Promise<void> => { + try { + await axios.delete( + `/api/admin/mailSubject/${date.getFullYear()}/${date.getMonth() + 1}`, + { + headers: { + 'XSRF-TOKEN': token, + }, + } + ) + toast.success('Mail subject succesfully deleted !') + } catch (e: any) { + if (e.response.status === 403) { + toast.error( + "Unauthorized : You don't have the rights to do this operation" + ) + } else { + toast.error('Failed to delete mail subject') + } + console.error(e) + } + } + /** * Creates a monthlyInfo for selected month * @param date -- GitLab