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