Skip to content
Snippets Groups Projects
Commit d139f5bb authored by Bastien DUMONT's avatar Bastien DUMONT :angel:
Browse files

Merge branch 'feat/US827-custom-popup-endDate' into 'dev'

Feat/us827 custom popup end date

See merge request web-et-numerique/llle_project/backoffice-client!71
parents c740241c bf9a8277
No related branches found
No related tags found
6 merge requests!96Deploy OpenShift v2,!95MEP fix liens undefined,!91MEP: removed Meilisearch,!79Fix: nginx unprivileged image,!77Back-office SGE before canary release,!71Feat/us827 custom popup end date
Pipeline #43545 passed
import { DateTime } from 'luxon'
import React, { useCallback, useContext, useEffect, useState } from 'react' import React, { useCallback, useContext, useEffect, useState } from 'react'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import { getAxiosXSRFHeader } from '../../axios.config' import { getAxiosXSRFHeader } from '../../axios.config'
import { CheckboxType } from '../../enum/checkboxType.enum' import { CheckboxType } from '../../enum/checkboxType.enum'
import { UserContext, UserContextProps } from '../../hooks/userContext' import { UserContext, UserContextProps } from '../../hooks/userContext'
import { ICustomPopup } from '../../models/cutomPopup.model' import { ICustomPopup, PopupDuration } from '../../models/cutomPopup.model'
import {
durationEnum,
durationType,
Option,
} from '../../models/durationOptios.model'
import { IPartnersInfo } from '../../models/partnersInfo.model' import { IPartnersInfo } from '../../models/partnersInfo.model'
import { CustomPopupService } from '../../services/customPopup.service' import { CustomPopupService } from '../../services/customPopup.service'
import { PartnersInfoService } from '../../services/partnersInfo.service' import { PartnersInfoService } from '../../services/partnersInfo.service'
import Loader from '../Loader/Loader' import Loader from '../Loader/Loader'
import './settings.scss' import './settings.scss'
const OPTIONS: Array<Option> = [
{
value: durationEnum.hours,
label: 'Heures',
},
{
value: durationEnum.days,
label: 'Jours',
},
{
value: durationEnum.infinite,
label: 'Indéterminée',
},
]
const Settings: React.FC = () => { const Settings: React.FC = () => {
const [refreshData, setRefreshData] = useState(false) const [refreshData, setRefreshData] = useState(false)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
...@@ -19,11 +40,18 @@ const Settings: React.FC = () => { ...@@ -19,11 +40,18 @@ const Settings: React.FC = () => {
egl_failure: false, egl_failure: false,
notification_activated: false, notification_activated: false,
}) })
const [previousEndDate, setPreviousEndDate] = useState<string>()
const [customPopup, setCustomPopup] = useState<ICustomPopup>({ const [customPopup, setCustomPopup] = useState<ICustomPopup>({
popupEnabled: false, popupEnabled: false,
title: '', title: '',
description: '', description: '',
endDate: DateTime.local().toISO(),
})
const [popupDuration, setPopupDuration] = useState<PopupDuration>({
type: 'days',
duration: 0,
}) })
const { user }: Partial<UserContextProps> = useContext(UserContext) const { user }: Partial<UserContextProps> = useContext(UserContext)
const isPartnerNotificationOn = () => const isPartnerNotificationOn = () =>
...@@ -31,6 +59,12 @@ const Settings: React.FC = () => { ...@@ -31,6 +59,12 @@ const Settings: React.FC = () => {
partnersInfo.egl_failure || partnersInfo.egl_failure ||
partnersInfo.grdf_failure partnersInfo.grdf_failure
/**
* Only one type of popup can be enabled
*/
const isPageValid = () =>
!(isPartnerNotificationOn() && customPopup.popupEnabled)
const handleCheckboxChange = (value: boolean, type: CheckboxType): void => { const handleCheckboxChange = (value: boolean, type: CheckboxType): void => {
switch (type) { switch (type) {
case CheckboxType.GRDF: case CheckboxType.GRDF:
...@@ -90,7 +124,7 @@ const Settings: React.FC = () => { ...@@ -90,7 +124,7 @@ const Settings: React.FC = () => {
resetFields() resetFields()
setIsLoading(true) setIsLoading(true)
async function getSettings() { async function loadSettings() {
if (user) { if (user) {
const partnersInfoService = new PartnersInfoService() const partnersInfoService = new PartnersInfoService()
const customPopupService = new CustomPopupService() const customPopupService = new CustomPopupService()
...@@ -106,12 +140,13 @@ const Settings: React.FC = () => { ...@@ -106,12 +140,13 @@ const Settings: React.FC = () => {
setCustomPopup({ setCustomPopup({
...customPopupData, ...customPopupData,
}) })
setPreviousEndDate(customPopupData.endDate || undefined)
} }
} }
setIsLoading(false) setIsLoading(false)
} }
if (subscribed) { if (subscribed) {
getSettings() loadSettings()
} }
return () => { return () => {
subscribed = false subscribed = false
...@@ -137,9 +172,59 @@ const Settings: React.FC = () => { ...@@ -137,9 +172,59 @@ const Settings: React.FC = () => {
customPopup, customPopup,
getAxiosXSRFHeader(user.xsrftoken) getAxiosXSRFHeader(user.xsrftoken)
) )
setPreviousEndDate(customPopup.endDate)
} }
} }
const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setPopupDuration((prev) => ({
...prev,
type: event.target.value as durationType,
}))
}
/**
* Handles duration change
*/
useEffect(() => {
const now = DateTime.local()
let newDate: DateTime
if (popupDuration.type !== durationEnum.infinite) {
newDate = now.plus({
[popupDuration.type]: popupDuration.duration,
})
} else if (popupDuration.type === 'infinite') {
newDate = now.plus({
years: 1,
})
}
setCustomPopup((prev) => ({
...prev,
endDate: newDate.toISO(),
}))
}, [popupDuration])
const isPopupOutdated = (date: string) =>
DateTime.local() >= DateTime.fromISO(date)
/**
* Returns "Popup expirée" OR "Temps restant : ..."
*/
const getRemainingDuration = (date: string) => {
if (isPopupOutdated(date)) {
return <p className="endDate">Popup expirée</p>
}
return (
<p className="endDate">
Temps d&apos;activation restant :<br />
{DateTime.fromISO(date)
.diffNow(['days', 'hours', 'minutes', 'seconds'])
.set({ second: 0 })
.toHuman()}
</p>
)
}
return ( return (
<> <>
<div className="header"> <div className="header">
...@@ -201,22 +286,28 @@ const Settings: React.FC = () => { ...@@ -201,22 +286,28 @@ const Settings: React.FC = () => {
</div> </div>
</div> </div>
<div className="customInfo"> <div className="customPopup">
<h2 className="title">Affichage de pop-up personnalisée</h2> <h2 className="title">Affichage de pop-up personnalisée</h2>
<div className="switch_div"> <div className="currentPopup">
<span>Pop-up active</span> <div className="switch_div">
<input <span>Pop-up active</span>
type="checkbox" <input
id="switch_popup" type="checkbox"
checked={customPopup.popupEnabled} id="switch_popup"
onChange={(event) => { checked={customPopup.popupEnabled}
handleCheckboxChange( onChange={(event) => {
event.currentTarget.checked, handleCheckboxChange(
CheckboxType.CUSTOM event.currentTarget.checked,
) CheckboxType.CUSTOM
}} )
/> }}
<label htmlFor="switch_popup"></label> />
<label htmlFor="switch_popup"></label>
</div>
{customPopup.popupEnabled &&
previousEndDate &&
getRemainingDuration(previousEndDate)}
</div> </div>
{customPopup.popupEnabled && ( {customPopup.popupEnabled && (
<> <>
...@@ -226,6 +317,7 @@ const Settings: React.FC = () => { ...@@ -226,6 +317,7 @@ const Settings: React.FC = () => {
type="text" type="text"
name="title" name="title"
id="title" id="title"
min={1}
placeholder="Titre" placeholder="Titre"
value={customPopup.title} value={customPopup.title}
onChange={(event) => handlePopupChange(event, 'title')} onChange={(event) => handlePopupChange(event, 'title')}
...@@ -248,6 +340,38 @@ const Settings: React.FC = () => { ...@@ -248,6 +340,38 @@ const Settings: React.FC = () => {
{customPopup.description.length} / 250 {customPopup.description.length} / 250
</p> </p>
</div> </div>
<div className="popupEndDate">
<label htmlFor="title">Nouvelle Durée</label>
<div className="durationInput">
<select
value={popupDuration.type}
onChange={(event) => handleSelectChange(event)}
>
{OPTIONS.map((option) => (
<option
key={option.value}
value={option.value}
selected={popupDuration.type === option.value}
>
{option.label}
</option>
))}
</select>
{popupDuration.type !== 'infinite' && (
<input
type="number"
min="0"
value={popupDuration.duration}
onChange={(e) =>
setPopupDuration((prev) => ({
...prev,
duration: Number(e.target.value),
}))
}
/>
)}
</div>
</div>
</> </>
)} )}
</div> </div>
...@@ -256,7 +380,12 @@ const Settings: React.FC = () => { ...@@ -256,7 +380,12 @@ const Settings: React.FC = () => {
<button className="btnCancel" onClick={handleCancel}> <button className="btnCancel" onClick={handleCancel}>
Annuler Annuler
</button> </button>
<button className="btnValid" onClick={handleSave}> <button
className="btnValid"
onClick={handleSave}
disabled={!isPageValid()}
title="Un seul type de popup peut être activé"
>
Sauvegarder Sauvegarder
</button> </button>
</div> </div>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.settings { .settings {
.partnersInfo, .partnersInfo,
.customInfo { .customPopup {
h2.title { h2.title {
margin: 1rem 0; margin: 1rem 0;
} }
...@@ -19,12 +19,28 @@ ...@@ -19,12 +19,28 @@
} }
} }
.customPopup {
.currentPopup {
display: flex;
margin-bottom: 1rem;
.switch_div {
padding-top: 0;
}
p.endDate {
color: $gold-dark;
font-weight: bold;
}
}
}
.popupTitle { .popupTitle {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.popupTitle, .popupTitle,
.popupDescription { .popupDescription,
.popupEndDate {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.5rem; gap: 0.5rem;
...@@ -56,6 +72,33 @@ ...@@ -56,6 +72,33 @@
} }
} }
.popupEndDate {
.durationInput {
display: flex;
gap: 1.5rem;
max-height: 36px;
input,
select {
background: #383941;
border: 1px solid $text-chart;
border-radius: 2px;
}
input {
max-width: 100px;
}
select {
max-width: 180px;
padding: 0.5rem 1rem;
option {
background-color: $grey-light;
}
}
}
}
.buttons { .buttons {
position: fixed; position: fixed;
bottom: 1rem; bottom: 1rem;
...@@ -121,11 +164,4 @@ ...@@ -121,11 +164,4 @@
width: 34px; width: 34px;
} }
} }
.customInfo {
.switch_div {
padding-top: 0;
margin-bottom: 1rem;
}
}
} }
import { durationType } from './durationOptios.model'
export interface ICustomPopup { export interface ICustomPopup {
description: string description: string
popupEnabled: boolean popupEnabled: boolean
endDate: string
title: string title: string
} }
export interface PopupDuration {
type: durationType
duration: number
}
export type durationType = 'hours' | 'days' | 'infinite'
export enum durationEnum {
hours = 'hours',
days = 'days',
infinite = 'infinite',
}
export interface Option {
value: durationType
label: string
}
...@@ -5,6 +5,7 @@ $dark-background: radial-gradient( ...@@ -5,6 +5,7 @@ $dark-background: radial-gradient(
#1b1c22 100% #1b1c22 100%
); );
$gold: #e3b82a; $gold: #e3b82a;
$gold-dark: #e2a70d;
$btn-gold: #f1c017; $btn-gold: #f1c017;
$dark-light: #1b1c22; $dark-light: #1b1c22;
$grey-dark: #25262b; $grey-dark: #25262b;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment