diff --git a/src/components/CommonKit/Modal/Modal.tsx b/src/components/CommonKit/Modal/Modal.tsx index 88e366ea0611331b159fd7784403f33120cb539b..e79e37fb107ff7c5411c8204240b0e3e37fdeb65 100644 --- a/src/components/CommonKit/Modal/Modal.tsx +++ b/src/components/CommonKit/Modal/Modal.tsx @@ -1,71 +1,74 @@ -import React, { useEffect, ReactNode } from 'react' -import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' -import CloseIcon from 'assets/icons/ico/close.svg' -import './Modal.scss' -interface ModalProps { - border?: boolean - hideClosure?: boolean - handleCloseClick: () => void - children: ReactNode - yellowBorder?: boolean -} - -const Modal: React.FC<ModalProps> = ({ - border, - hideClosure, - handleCloseClick, - children, - yellowBorder, -}: ModalProps) => { - const yellow = yellowBorder ? 'yellow-border' : '' - const displayCloseButton = hideClosure ? false : true - - const disableBackgroundScroll = (disable: boolean) => { - const backgroundDesktop = document.querySelector( - '.app-content' - ) as HTMLElement - const backgroundMobile = document.querySelector('html') as HTMLElement - const bgStyleDesktop = backgroundDesktop && backgroundDesktop.style - const bgStyleMobile = backgroundMobile && backgroundMobile.style - if (bgStyleDesktop && bgStyleMobile) { - if (disable) { - bgStyleDesktop.overflowY = 'hidden' - bgStyleMobile.overflowY = 'hidden' - } else { - bgStyleDesktop.overflowY = 'unset' - bgStyleMobile.overflowY = 'unset' - } - } - } - - const closeClick = () => { - disableBackgroundScroll(false) - handleCloseClick() - } - - useEffect(() => { - disableBackgroundScroll(true) - return () => { - disableBackgroundScroll(false) - } - }, []) - - return ( - <div className={`modal-overlay modal-opened`}> - <div - className={`modal-box ${border ? 'modal-box-bordered' : ''} ${yellow}`} - > - {displayCloseButton && ( - <StyledIconButton - className="modal-close-button" - icon={CloseIcon} - onClick={closeClick} - /> - )} - <div className="modal-content">{children}</div> - </div> - </div> - ) -} - -export default Modal +import React, { useEffect, ReactNode } from 'react' +import { IuseI18n, useI18n } from 'cozy-ui/transpiled/react/I18n' +import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' +import CloseIcon from 'assets/icons/ico/close.svg' +import './Modal.scss' +interface ModalProps { + border?: boolean + hideClosure?: boolean + handleCloseClick: () => void + children: ReactNode + yellowBorder?: boolean +} + +const Modal: React.FC<ModalProps> = ({ + border, + hideClosure, + handleCloseClick, + children, + yellowBorder, +}: ModalProps) => { + const { t }: IuseI18n = useI18n() + const yellow = yellowBorder ? 'yellow-border' : '' + const displayCloseButton = hideClosure ? false : true + + const disableBackgroundScroll = (disable: boolean) => { + const backgroundDesktop = document.querySelector( + '.app-content' + ) as HTMLElement + const backgroundMobile = document.querySelector('html') as HTMLElement + const bgStyleDesktop = backgroundDesktop && backgroundDesktop.style + const bgStyleMobile = backgroundMobile && backgroundMobile.style + if (bgStyleDesktop && bgStyleMobile) { + if (disable) { + bgStyleDesktop.overflowY = 'hidden' + bgStyleMobile.overflowY = 'hidden' + } else { + bgStyleDesktop.overflowY = 'unset' + bgStyleMobile.overflowY = 'unset' + } + } + } + + const closeClick = () => { + disableBackgroundScroll(false) + handleCloseClick() + } + + useEffect(() => { + disableBackgroundScroll(true) + return () => { + disableBackgroundScroll(false) + } + }, []) + + return ( + <div className={`modal-overlay modal-opened`}> + <div + className={`modal-box ${border ? 'modal-box-bordered' : ''} ${yellow}`} + > + {displayCloseButton && ( + <StyledIconButton + title={t('modal.accessibility.close')} + className="modal-close-button" + icon={CloseIcon} + onClick={closeClick} + /> + )} + <div className="modal-content">{children}</div> + </div> + </div> + ) +} + +export default Modal diff --git a/src/components/Connection/ConnectionFormLogin.tsx b/src/components/Connection/ConnectionFormLogin.tsx index ca445fed04cc79fecef4ac0d9246166122629e26..9d0542262ad8aa548477bd91b13f1dc6e727249e 100644 --- a/src/components/Connection/ConnectionFormLogin.tsx +++ b/src/components/Connection/ConnectionFormLogin.tsx @@ -186,7 +186,7 @@ const ConnectionFormLogin: React.FC<ConnectionFormLoginProps> = ({ : 'form-control form-input --error' } aria-describedby="PasswordHelp" - placeholder="Mot de passe" + placeholder={t('auth.password_label')} name="password" onChange={(e: React.ChangeEvent<HTMLInputElement>) => changePassword(e.target.value) @@ -196,6 +196,7 @@ const ConnectionFormLogin: React.FC<ConnectionFormLoginProps> = ({ <span> <StyledIconButton icon={TrailingIcon} + title={t('auth.accessibility.reveal_password')} className="form-trailing-icon" size={22} onClick={() => revealPassword('idFieldPassword' + fluidName)} @@ -207,6 +208,7 @@ const ConnectionFormLogin: React.FC<ConnectionFormLoginProps> = ({ <div className="kloginauthform-button"> <MuiButton type="submit" + title={t('auth.accessibility.connect')} disabled={loading} classes={{ root: 'btn-highlight', @@ -235,6 +237,7 @@ const ConnectionFormLogin: React.FC<ConnectionFormLoginProps> = ({ <MuiButton disabled={loading} + title={t('auth.accessibility.create_account')} onClick={() => window.open(siteLink, '_blank')} classes={{ root: 'btn-secondary-positive', diff --git a/src/components/Connection/ConnectionFormOAuth.tsx b/src/components/Connection/ConnectionFormOAuth.tsx index 98ae1bb8e543cd046bebfa1681c37faa20d65adb..0ed2afda3256d8e4486e54c126de9a0eb85bb35c 100644 --- a/src/components/Connection/ConnectionFormOAuth.tsx +++ b/src/components/Connection/ConnectionFormOAuth.tsx @@ -1,104 +1,105 @@ -import React, { useCallback } from 'react' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { useClient } from 'cozy-client' -import { useDispatch } from 'react-redux' -import './connectionFormOAuth.scss' - -import { Konnector, Trigger, FluidStatus, FluidConnection } from 'models' -import OAuthForm from 'components/Connection/OAuthForm' -import MuiButton from '@material-ui/core/Button' -import AccountService from 'services/account.service' -import TriggerService from 'services/triggers.service' -import { updatedFluidConnection } from 'store/global/global.actions' - -interface ConnectionFormOAuthProps { - fluidStatus: FluidStatus - onSuccess: Function -} - -const ConnectionFormOAuth: React.FC<ConnectionFormOAuthProps> = ({ - fluidStatus, - onSuccess, -}: ConnectionFormOAuthProps) => { - const { t } = useI18n() - const client = useClient() - const dispatch = useDispatch() - - const konnectorSlug: string = fluidStatus.connection.konnectorConfig.slug - const siteLink: string = fluidStatus.connection.konnectorConfig.siteLink - const konnector: Konnector | null = fluidStatus.connection.konnector - - const handleSuccess = useCallback( - async (accountId: string) => { - if (konnector) { - const accountService = new AccountService(client) - const account = await accountService.getAccount(accountId) - if (!account) { - const updatedConnection: FluidConnection = { - ...fluidStatus.connection, - account: null, - trigger: null, - } - dispatch( - updatedFluidConnection(fluidStatus.fluidType, updatedConnection) - ) - } else { - const triggersServices = new TriggerService(client) - const trigger: Trigger = await triggersServices.createTrigger( - account, - konnector - ) - const updatedConnection: FluidConnection = { - ...fluidStatus.connection, - account: account, - trigger: trigger, - } - dispatch( - updatedFluidConnection(fluidStatus.fluidType, updatedConnection) - ) - onSuccess() - } - } - }, - [ - client, - konnector, - dispatch, - fluidStatus.fluidType, - fluidStatus.connection, - onSuccess, - ] - ) - - return ( - <div className="koauthform"> - <p className="info-provider text-16-normal"> - {t('auth.' + `${konnectorSlug}` + '.connect.info')} - </p> - <div className="koauthform-button"> - <OAuthForm konnector={konnector} onSuccess={handleSuccess} /> - </div> - <div className="koauthform-text text-16-bold"> - <div className="text-16-bold"> - {t('auth.' + konnectorSlug + '.no_account.title')} - </div> - <div className="text-16-normal"> - {t('auth.' + konnectorSlug + '.no_account.text')} - </div> - </div> - <div className="koauthform-button"> - <MuiButton - onClick={() => window.open(siteLink, '_blank')} - classes={{ - root: 'btn-secondary-positive', - label: 'text-16-normal', - }} - > - {t('auth.' + konnectorSlug + '.create_account')} - </MuiButton> - </div> - </div> - ) -} - -export default ConnectionFormOAuth +import React, { useCallback } from 'react' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { useClient } from 'cozy-client' +import { useDispatch } from 'react-redux' +import './connectionFormOAuth.scss' + +import { Konnector, Trigger, FluidStatus, FluidConnection } from 'models' +import OAuthForm from 'components/Connection/OAuthForm' +import MuiButton from '@material-ui/core/Button' +import AccountService from 'services/account.service' +import TriggerService from 'services/triggers.service' +import { updatedFluidConnection } from 'store/global/global.actions' + +interface ConnectionFormOAuthProps { + fluidStatus: FluidStatus + onSuccess: Function +} + +const ConnectionFormOAuth: React.FC<ConnectionFormOAuthProps> = ({ + fluidStatus, + onSuccess, +}: ConnectionFormOAuthProps) => { + const { t } = useI18n() + const client = useClient() + const dispatch = useDispatch() + + const konnectorSlug: string = fluidStatus.connection.konnectorConfig.slug + const siteLink: string = fluidStatus.connection.konnectorConfig.siteLink + const konnector: Konnector | null = fluidStatus.connection.konnector + + const handleSuccess = useCallback( + async (accountId: string) => { + if (konnector) { + const accountService = new AccountService(client) + const account = await accountService.getAccount(accountId) + if (!account) { + const updatedConnection: FluidConnection = { + ...fluidStatus.connection, + account: null, + trigger: null, + } + dispatch( + updatedFluidConnection(fluidStatus.fluidType, updatedConnection) + ) + } else { + const triggersServices = new TriggerService(client) + const trigger: Trigger = await triggersServices.createTrigger( + account, + konnector + ) + const updatedConnection: FluidConnection = { + ...fluidStatus.connection, + account: account, + trigger: trigger, + } + dispatch( + updatedFluidConnection(fluidStatus.fluidType, updatedConnection) + ) + onSuccess() + } + } + }, + [ + client, + konnector, + dispatch, + fluidStatus.fluidType, + fluidStatus.connection, + onSuccess, + ] + ) + + return ( + <div className="koauthform"> + <p className="info-provider text-16-normal"> + {t('auth.' + `${konnectorSlug}` + '.connect.info')} + </p> + <div className="koauthform-button"> + <OAuthForm konnector={konnector} onSuccess={handleSuccess} /> + </div> + <div className="koauthform-text text-16-bold"> + <div className="text-16-bold"> + {t('auth.' + konnectorSlug + '.no_account.title')} + </div> + <div className="text-16-normal"> + {t('auth.' + konnectorSlug + '.no_account.text')} + </div> + </div> + <div className="koauthform-button"> + <MuiButton + title={t('auth.accessibility.create_account')} + onClick={() => window.open(siteLink, '_blank')} + classes={{ + root: 'btn-secondary-positive', + label: 'text-16-normal', + }} + > + {t('auth.' + konnectorSlug + '.create_account')} + </MuiButton> + </div> + </div> + ) +} + +export default ConnectionFormOAuth diff --git a/src/components/Connection/OAuthForm.tsx b/src/components/Connection/OAuthForm.tsx index ede56f234af35c056ccd6d5553a1c08e21f77649..ab951d7319990828f69056ffbd92cc1dbb99839e 100644 --- a/src/components/Connection/OAuthForm.tsx +++ b/src/components/Connection/OAuthForm.tsx @@ -71,6 +71,7 @@ const OAuthForm: React.FC<OAuthFormProps> = ({ return !konnector ? null : ( <> <MuiButton + title={t('auth.accessibility.connect')} onClick={ konnector.slug === fluidconfig[FluidType.GAS].konnectorConfig.slug ? toggleGRDFModal diff --git a/src/components/Feedback/FeedbackModal.tsx b/src/components/Feedback/FeedbackModal.tsx index 9e8a7f09689c7be765056eca082e9a77f7436b93..aee001cffcc7c03b664ff7c098883de60c8eec8d 100644 --- a/src/components/Feedback/FeedbackModal.tsx +++ b/src/components/Feedback/FeedbackModal.tsx @@ -1,234 +1,244 @@ -import React, { useState } from 'react' -import { IuseI18n, useI18n } from 'cozy-ui/transpiled/react/I18n' -import { Client, useClient } from 'cozy-client' -import { detect } from 'detect-browser' - -import Modal from 'components/CommonKit/Modal/Modal' -import StyledIcon from 'components/CommonKit/Icon/StyledIcon' -import MuiButton from '@material-ui/core/Button' -import StyledIconBorderedButton from 'components/CommonKit/IconButton/StyledIconBorderedButton' - -import BugOnIcon from 'assets/icons/visu/feedback/bug-on.svg' -import BugOffIcon from 'assets/icons/visu/feedback/bug-off.svg' -import IdeaOnIcon from 'assets/icons/visu/feedback/idea-on.svg' -import IdeaOffIcon from 'assets/icons/visu/feedback/idea-off.svg' -import OtherOnIcon from 'assets/icons/visu/feedback/other-on.svg' -import OtherOffIcon from 'assets/icons/visu/feedback/other-off.svg' -import warningIcon from 'assets/icons/ico/warning-yellow.svg' -import successIcon from 'assets/icons/visu/data-ok.svg' -import MailService from 'services/mail.service' -import './feedbackModal.scss' -import useExploration from 'components/Hooks/useExploration' -import { UserExplorationID } from 'enum/userExploration.enum' - -const FEEDBACK_EMAIL = 'ecolyo@grandlyon.com' -const browser = detect() - -interface FeedbackModalProps { - handleCloseClick: () => void -} - -const FeedbackModal: React.FC<FeedbackModalProps> = ({ - handleCloseClick, -}: FeedbackModalProps) => { - const { t }: IuseI18n = useI18n() - const client: Client = useClient() - const [type, setType] = useState<string>('bug') - const [description, setDescription] = useState<string>('') - const [sending, setSending] = useState<boolean>(false) - const [sent, setSent] = useState<boolean>(false) - const [error, setError] = useState<string>('') - - const [, setValidExploration] = useExploration() - - const resetInputs = () => { - setType('bug') - setDescription('') - setSent(false) - setError('') - } - - const closeModal = () => { - resetInputs() - handleCloseClick() - } - - const sendEmail = async () => { - setSending(true) - if (description === '') { - setError(t('feedback.error_empty_description')) - } else { - setValidExploration(UserExplorationID.EXPLORATION005) - const envInfo = - `Ecolyo` + - `\nv${client.appMetadata.version}` + - `\n\nLocation: ${window.location}` + - '\n\nBrowser:' + - `\nOn ${browser && browser.os}` + - `\nFrom ${browser && browser.name}` + - `\n${browser && browser.version}` + - '\n\nNavigator:' + - `\nOn ${navigator.platform}` + - `\nFrom ${navigator.vendor}` + - `\n${navigator.userAgent}` - - const mailContent = - 'Type: ' + - t('feedback.type_' + type) + - '\n\nDescription:\n' + - description + - '\n' + - envInfo - - const mailData = { - mode: 'from', - to: [{ name: 'Support', email: FEEDBACK_EMAIL }], - subject: '[Ecolyo] - Feedbacks - ' + t('feedback.type_' + type), - parts: [{ type: 'text/plain', body: mailContent }], - } - try { - const mailService = new MailService() - mailService.SendMail(client, mailData) - } catch (e) { - // eslint-disable-next-line no-console - console.error(e) - setError(t('feedback.error_sending')) - } - } - setSending(false) - setSent(true) - } - - const validResult = () => { - setSent(false) - setError('') - if (error === '') { - resetInputs() - closeModal() - } - } - - const getOnIcon = (_type: string) => { - switch (_type) { - case 'bug': - return BugOnIcon - case 'idea': - return IdeaOnIcon - case 'other': - return OtherOnIcon - default: - return BugOnIcon - } - } - const getOffIcon = (_type: string) => { - switch (_type) { - case 'bug': - return BugOffIcon - case 'idea': - return IdeaOffIcon - case 'other': - return OtherOffIcon - default: - return BugOffIcon - } - } - - const selectorItem = (itemType: string) => { - const selected = type === itemType - return ( - <div className="fb-selector-item"> - <StyledIconBorderedButton - icon={selected ? getOnIcon(itemType) : getOffIcon(itemType)} - selected={selected} - size={36} - onClick={() => setType(itemType)} - > - <div - className={ - selected - ? 'fb-selector-item-selectedlabel text-10-bold' - : 'fb-selector-item-label text-10-normal' - } - > - {t('feedback.type_' + itemType)} - </div> - </StyledIconBorderedButton> - </div> - ) - } - - return ( - <Modal yellowBorder={true} handleCloseClick={closeModal}> - {sent ? ( - <div className="fb-root"> - <div className="fb-content"> - {error === '' ? ( - <div className="fb-content-success"> - <div className="fb-icon"> - <StyledIcon icon={successIcon} size={48} /> - </div> - <div>{t('feedback.success_sending')}</div> - </div> - ) : ( - <div className="fb-content-error"> - <div className="fb-icon"> - <StyledIcon icon={warningIcon} size={48} /> - </div> - <div>{t('feedback.warning')}</div> - <div>{error}</div> - </div> - )} - <MuiButton - onClick={validResult} - variant={'contained'} - classes={{ - root: 'btn-highlight', - label: 'text-16-bold', - }} - > - {t('feedback.ok')} - </MuiButton> - </div> - </div> - ) : ( - <div className="fb-root"> - <div className="fb-header text-18-bold">{t('feedback.title')}</div> - <div className="fb-content"> - <div className="fb-label text-16-bold">{t('feedback.type')}</div> - <div className="fb-selector"> - {selectorItem('bug')} - {selectorItem('idea')} - {selectorItem('other')} - </div> - <div className="fb-label text-16-bold"> - {t('feedback.description')} - </div> - <textarea - id="idFeedbackDescription" - className="fb-form fb-textarea" - aria-describedby="Feedbacks description" - placeholder={t('feedback.description_placeholder')} - name="description" - onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => - setDescription(e.target.value) - } - value={description} - ></textarea> - <MuiButton - onClick={sendEmail} - disabled={sending} - classes={{ - root: 'btn-highlight', - label: 'text-16-bold', - }} - > - {t('feedback.send')} - </MuiButton> - </div> - </div> - )} - </Modal> - ) -} - -export default FeedbackModal +import React, { useState } from 'react' +import { IuseI18n, useI18n } from 'cozy-ui/transpiled/react/I18n' +import { Client, useClient } from 'cozy-client' +import { detect } from 'detect-browser' + +import Modal from 'components/CommonKit/Modal/Modal' +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import MuiButton from '@material-ui/core/Button' +import StyledIconBorderedButton from 'components/CommonKit/IconButton/StyledIconBorderedButton' + +import BugOnIcon from 'assets/icons/visu/feedback/bug-on.svg' +import BugOffIcon from 'assets/icons/visu/feedback/bug-off.svg' +import IdeaOnIcon from 'assets/icons/visu/feedback/idea-on.svg' +import IdeaOffIcon from 'assets/icons/visu/feedback/idea-off.svg' +import OtherOnIcon from 'assets/icons/visu/feedback/other-on.svg' +import OtherOffIcon from 'assets/icons/visu/feedback/other-off.svg' +import warningIcon from 'assets/icons/ico/warning-yellow.svg' +import successIcon from 'assets/icons/visu/data-ok.svg' +import MailService from 'services/mail.service' +import './feedbackModal.scss' +import useExploration from 'components/Hooks/useExploration' +import { UserExplorationID } from 'enum/userExploration.enum' + +const FEEDBACK_EMAIL = 'ecolyo@grandlyon.com' +const browser = detect() + +interface FeedbackModalProps { + handleCloseClick: () => void +} + +const FeedbackModal: React.FC<FeedbackModalProps> = ({ + handleCloseClick, +}: FeedbackModalProps) => { + const { t }: IuseI18n = useI18n() + const client: Client = useClient() + const [type, setType] = useState<string>('bug') + const [description, setDescription] = useState<string>('') + const [sending, setSending] = useState<boolean>(false) + const [sent, setSent] = useState<boolean>(false) + const [error, setError] = useState<string>('') + + const [, setValidExploration] = useExploration() + + const resetInputs = () => { + setType('bug') + setDescription('') + setSent(false) + setError('') + } + + const closeModal = () => { + resetInputs() + handleCloseClick() + } + + const sendEmail = async () => { + setSending(true) + if (description === '') { + setError(t('feedback.error_empty_description')) + } else { + setValidExploration(UserExplorationID.EXPLORATION005) + const envInfo = + `Ecolyo` + + `\nv${client.appMetadata.version}` + + `\n\nLocation: ${window.location}` + + '\n\nBrowser:' + + `\nOn ${browser && browser.os}` + + `\nFrom ${browser && browser.name}` + + `\n${browser && browser.version}` + + '\n\nNavigator:' + + `\nOn ${navigator.platform}` + + `\nFrom ${navigator.vendor}` + + `\n${navigator.userAgent}` + + const mailContent = + 'Type: ' + + t('feedback.type_' + type) + + '\n\nDescription:\n' + + description + + '\n' + + envInfo + + const mailData = { + mode: 'from', + to: [{ name: 'Support', email: FEEDBACK_EMAIL }], + subject: '[Ecolyo] - Feedbacks - ' + t('feedback.type_' + type), + parts: [{ type: 'text/plain', body: mailContent }], + } + try { + const mailService = new MailService() + mailService.SendMail(client, mailData) + } catch (e) { + // eslint-disable-next-line no-console + console.error(e) + setError(t('feedback.error_sending')) + } + } + setSending(false) + setSent(true) + } + + const validResult = () => { + setSent(false) + setError('') + if (error === '') { + resetInputs() + closeModal() + } + } + + const getOnIcon = (_type: string) => { + switch (_type) { + case 'bug': + return BugOnIcon + case 'idea': + return IdeaOnIcon + case 'other': + return OtherOnIcon + default: + return BugOnIcon + } + } + const getOffIcon = (_type: string) => { + switch (_type) { + case 'bug': + return BugOffIcon + case 'idea': + return IdeaOffIcon + case 'other': + return OtherOffIcon + default: + return BugOffIcon + } + } + + const selectorItem = (itemType: string) => { + const selected = type === itemType + return ( + <div className="fb-selector-item"> + <StyledIconBorderedButton + title={`${t('feedback.accessibility.select_type')} ${t( + 'feedback.type_' + itemType + )}`} + icon={selected ? getOnIcon(itemType) : getOffIcon(itemType)} + selected={selected} + size={36} + onClick={() => setType(itemType)} + > + <div + className={ + selected + ? 'fb-selector-item-selectedlabel text-10-bold' + : 'fb-selector-item-label text-10-normal' + } + > + {t('feedback.type_' + itemType)} + </div> + </StyledIconBorderedButton> + </div> + ) + } + + return ( + <Modal yellowBorder={true} handleCloseClick={closeModal}> + {sent ? ( + <div className="fb-root"> + <div className="fb-content"> + {error === '' ? ( + <div className="fb-content-success"> + <div className="fb-icon"> + <StyledIcon icon={successIcon} size={48} /> + </div> + <div>{t('feedback.success_sending')}</div> + </div> + ) : ( + <div className="fb-content-error"> + <div className="fb-icon"> + <StyledIcon icon={warningIcon} size={48} /> + </div> + <div>{t('feedback.warning')}</div> + <div>{error}</div> + </div> + )} + <MuiButton + title={t('feedback.accessibility.ok')} + onClick={validResult} + variant={'contained'} + classes={{ + root: 'btn-highlight', + label: 'text-16-bold', + }} + > + {t('feedback.ok')} + </MuiButton> + </div> + </div> + ) : ( + <div className="fb-root"> + <div className="fb-header text-18-bold">{t('feedback.title')}</div> + <form className="fb-content"> + <label htmlFor="feedbackType" className="fb-label text-16-bold"> + {t('feedback.type')} + </label> + <fieldset id="feedbackType" className="fb-selector"> + {selectorItem('bug')} + {selectorItem('idea')} + {selectorItem('other')} + </fieldset> + <label + htmlFor="idFeedbackDescription" + className="fb-label text-16-bold" + > + {t('feedback.description')} + </label> + <textarea + id="idFeedbackDescription" + className="fb-form fb-textarea" + placeholder={t('feedback.description_placeholder')} + name="description" + onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => + setDescription(e.target.value) + } + value={description} + ></textarea> + <MuiButton + title={t('feedback.accessibility.send')} + onClick={sendEmail} + disabled={sending} + classes={{ + root: 'btn-highlight', + label: 'text-16-bold', + }} + type="submit" + > + {t('feedback.send')} + </MuiButton> + </form> + </div> + )} + </Modal> + ) +} + +export default FeedbackModal diff --git a/src/components/Feedback/feedbackModal.scss b/src/components/Feedback/feedbackModal.scss index 0c9e8a0e1abfd70da32f4fe070631743ba40ebfa..5c19e2b4b4669847d149b3009919b5d2cd48c19c 100644 --- a/src/components/Feedback/feedbackModal.scss +++ b/src/components/Feedback/feedbackModal.scss @@ -1,82 +1,84 @@ -@import 'src/styles/base/color'; - -.fb-root { - overflow-y: auto; - min-width: 70%; - .fb-header { - color: $gold-shadow; - padding: 1.5rem 1.5rem 0rem 1.5rem; - display: flex; - justify-content: center; - } - - .fb-content { - padding: 1rem 1.5rem 1.5rem 1.5rem; - display: flex; - flex-direction: column; - .fb-content-success { - color: $grey-bright; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - } - .fb-content-error { - color: $grey-bright; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - } - .fb-icon { - display: flex; - justify-content: center; - margin: 0 0 1.5rem 0; - } - .fb-label { - margin: 0.5rem 0 0 0; - } - .fb-selector { - margin: 0.5rem 0; - display: flex; - flex-direction: row; - align-content: space-around; - justify-content: space-around; - .fb-selector-item { - height: 3.125rem; - width: 3.125rem; - .fb-selector-item-label { - color: $grey-bright; - } - .fb-selector-item-selectedlabel { - color: $white; - } - } - } - .fb-form { - margin: 0.5rem 0; - border: 1px solid $grey-dark; - border-radius: 4px; - color: $grey-bright; - background: rgba(0, 0, 0, 0.3); - padding: 0 1rem; - outline: none; - } - .fb-form:focus { - border: 1px solid $gold-shadow; - } - .fb-textarea { - height: 8.725rem; - padding: 0.5rem 1rem; - resize: none; - } - .fb-input { - height: 2rem; - } - button.btn-highlight { - max-width: 9.375rem; - align-self: center; - margin-top: 1rem; - } - } -} +@import 'src/styles/base/color'; + +.fb-root { + overflow-y: auto; + min-width: 70%; + .fb-header { + color: $gold-shadow; + padding: 1.5rem 1.5rem 0rem 1.5rem; + display: flex; + justify-content: center; + } + + .fb-content { + padding: 1rem 1.5rem 1.5rem 1.5rem; + display: flex; + flex-direction: column; + .fb-content-success { + color: $grey-bright; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + } + .fb-content-error { + color: $grey-bright; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + } + .fb-icon { + display: flex; + justify-content: center; + margin: 0 0 1.5rem 0; + } + .fb-label { + margin: 0.5rem 0 0 0; + } + .fb-selector { + margin: 0.5rem 0; + display: flex; + flex-direction: row; + align-content: space-around; + justify-content: space-around; + padding: 0; + border: none; + .fb-selector-item { + height: 3.125rem; + width: 3.125rem; + .fb-selector-item-label { + color: $grey-bright; + } + .fb-selector-item-selectedlabel { + color: $white; + } + } + } + .fb-form { + margin: 0.5rem 0; + border: 1px solid $grey-dark; + border-radius: 4px; + color: $grey-bright; + background: rgba(0, 0, 0, 0.3); + padding: 0 1rem; + outline: none; + } + .fb-form:focus { + border: 1px solid $gold-shadow; + } + .fb-textarea { + height: 8.725rem; + padding: 0.5rem 1rem; + resize: none; + } + .fb-input { + height: 2rem; + } + button.btn-highlight { + max-width: 9.375rem; + align-self: center; + margin-top: 1rem; + } + } +} diff --git a/src/components/Header/CozyBar.tsx b/src/components/Header/CozyBar.tsx index 6abfa5911f80ea028c5ab5c3af93a7dd43884be4..a6cf8e6bc43c02b0e08370afa186f1158b0cc11b 100644 --- a/src/components/Header/CozyBar.tsx +++ b/src/components/Header/CozyBar.tsx @@ -1,72 +1,74 @@ -import React from 'react' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { useSelector, useDispatch } from 'react-redux' -import { AppStore } from 'store' -import { updateModalIsFeedbacksOpen } from 'store/modal/modal.actions' -import { useHistory } from 'react-router-dom' - -import { ScreenType } from 'enum/screen.enum' - -import BackArrowIcon from 'assets/icons/ico/back-arrow.svg' -import FeedbacksIcon from 'assets/icons/ico/feedbacks.svg' -import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' - -interface CozyBarProps { - titleKey?: string - displayBackArrow?: boolean -} - -const CozyBar = ({ - titleKey = 'COMMON.APP_TITLE', - displayBackArrow = false, -}: CozyBarProps) => { - const { t } = useI18n() - const history = useHistory() - const dispatch = useDispatch() - const { BarLeft, BarCenter, BarRight } = cozy.bar - const { screenType } = useSelector((state: AppStore) => state.ecolyo.global) - - const handleClickBack = () => { - history.goBack() - } - - const handleClickFeedbacks = () => { - dispatch(updateModalIsFeedbacksOpen(true)) - } - - const cozyBarCustom = (screen?: ScreenType) => { - if (screen === ScreenType.MOBILE) { - return ( - <React.Fragment> - {displayBackArrow && ( - <BarLeft> - <StyledIconButton - className="cv-button" - icon={BackArrowIcon} - onClick={() => handleClickBack()} - /> - </BarLeft> - )} - <BarCenter> - <div className="cozy-bar"> - <span className="app-title">{t(titleKey)}</span> - </div> - </BarCenter> - <BarRight> - <StyledIconButton - className="cv-button" - icon={FeedbacksIcon} - size={40} - onClick={() => handleClickFeedbacks()} - /> - </BarRight> - </React.Fragment> - ) - } - return null - } - - return cozyBarCustom(screenType) -} - -export default CozyBar +import React from 'react' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { useSelector, useDispatch } from 'react-redux' +import { AppStore } from 'store' +import { updateModalIsFeedbacksOpen } from 'store/modal/modal.actions' +import { useHistory } from 'react-router-dom' + +import { ScreenType } from 'enum/screen.enum' + +import BackArrowIcon from 'assets/icons/ico/back-arrow.svg' +import FeedbacksIcon from 'assets/icons/ico/feedbacks.svg' +import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' + +interface CozyBarProps { + titleKey?: string + displayBackArrow?: boolean +} + +const CozyBar = ({ + titleKey = 'COMMON.APP_TITLE', + displayBackArrow = false, +}: CozyBarProps) => { + const { t } = useI18n() + const history = useHistory() + const dispatch = useDispatch() + const { BarLeft, BarCenter, BarRight } = cozy.bar + const { screenType } = useSelector((state: AppStore) => state.ecolyo.global) + + const handleClickBack = () => { + history.goBack() + } + + const handleClickFeedbacks = () => { + dispatch(updateModalIsFeedbacksOpen(true)) + } + + const cozyBarCustom = (screen?: ScreenType) => { + if (screen === ScreenType.MOBILE) { + return ( + <React.Fragment> + {displayBackArrow && ( + <BarLeft> + <StyledIconButton + title={t('header.accessibility.back')} + className="cv-button" + icon={BackArrowIcon} + onClick={handleClickBack} + /> + </BarLeft> + )} + <BarCenter> + <div className="cozy-bar"> + <span className="app-title">{t(titleKey)}</span> + </div> + </BarCenter> + <BarRight> + <StyledIconButton + title={t('header.accessibility.open_feedbacks')} + className="cv-button" + icon={FeedbacksIcon} + size={40} + onClick={handleClickFeedbacks} + /> + </BarRight> + </React.Fragment> + ) + } + return null + } + + return cozyBarCustom(screenType) +} + +export default CozyBar diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 6fc25e105a761d99a59e0d05309112f49e8642cf..27471b557fb8cafb205fc66462faa73e8445a01d 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,119 +1,121 @@ -import React, { useCallback, useEffect, useRef } from 'react' -import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { useSelector, useDispatch } from 'react-redux' -import { AppStore } from 'store' -import { updateModalIsFeedbacksOpen } from 'store/modal/modal.actions' -import { useHistory } from 'react-router-dom' - -import { ScreenType } from 'enum/screen.enum' - -import BackArrowIcon from 'assets/icons/ico/back-arrow.svg' -import FeedbacksIcon from 'assets/icons/ico/feedbacks.svg' -import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' - -interface HeaderProps { - textKey?: string - desktopTitleKey?: string - displayBackArrow?: boolean - children?: React.ReactNode - setHeaderHeight(height: number): void -} - -const Header: React.FC<HeaderProps> = ({ - textKey, - desktopTitleKey, - displayBackArrow, - children, - setHeaderHeight, -}: HeaderProps) => { - const { t } = useI18n() - const history = useHistory() - const header = useRef(null) - const dispatch = useDispatch() - const { screenType } = useSelector((state: AppStore) => state.ecolyo.global) - - const cozyBarHeight = 48 - const headerBottomHeight = 8 - - const handleClickBack = useCallback(() => { - history.goBack() - }, [history]) - - const handleClickFeedbacks = () => { - dispatch(updateModalIsFeedbacksOpen(true)) - } - - useEffect(() => { - if (screenType === ScreenType.MOBILE) { - setHeaderHeight( - header.current - ? header.current.clientHeight - cozyBarHeight - headerBottomHeight - : 0 - ) - } else { - setHeaderHeight( - header.current ? header.current.clientHeight - headerBottomHeight : 0 - ) - } - }, [screenType, children, setHeaderHeight]) - - return ( - <div className="header" ref={header}> - <div className="header-top"> - <div className="header-content"> - <div - className={ - !textKey && !desktopTitleKey - ? 'header-content-top header-content-top-right' - : 'header-content-top' - } - > - {textKey && ( - <div - className={`header-text ${ - screenType === ScreenType.MOBILE - ? 'text-14-normal-uppercase' - : 'text-22-bold' - }`} - > - {t(textKey)} - </div> - )} - {desktopTitleKey && ( - <div - className={`header-text-desktop ${ - screenType === ScreenType.MOBILE - ? 'text-14-normal-uppercase' - : 'text-22-bold' - }`} - > - {displayBackArrow && ( - <StyledIconButton - className="header-back-button" - icon={BackArrowIcon} - onClick={handleClickBack} - /> - )} - {t(desktopTitleKey)} - </div> - )} - <StyledIconButton - className={ - !textKey && !desktopTitleKey - ? 'header-feedbacks-button right' - : 'header-feedbacks-button' - } - icon={FeedbacksIcon} - size={40} - onClick={handleClickFeedbacks} - /> - </div> - {children} - </div> - </div> - <div className="header-bar"></div> - </div> - ) -} - -export default Header +import React, { useCallback, useEffect, useRef } from 'react' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import { useSelector, useDispatch } from 'react-redux' +import { AppStore } from 'store' +import { updateModalIsFeedbacksOpen } from 'store/modal/modal.actions' +import { useHistory } from 'react-router-dom' + +import { ScreenType } from 'enum/screen.enum' + +import BackArrowIcon from 'assets/icons/ico/back-arrow.svg' +import FeedbacksIcon from 'assets/icons/ico/feedbacks.svg' +import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' + +interface HeaderProps { + textKey?: string + desktopTitleKey?: string + displayBackArrow?: boolean + children?: React.ReactNode + setHeaderHeight(height: number): void +} + +const Header: React.FC<HeaderProps> = ({ + textKey, + desktopTitleKey, + displayBackArrow, + children, + setHeaderHeight, +}: HeaderProps) => { + const { t } = useI18n() + const history = useHistory() + const header = useRef(null) + const dispatch = useDispatch() + const { screenType } = useSelector((state: AppStore) => state.ecolyo.global) + + const cozyBarHeight = 48 + const headerBottomHeight = 8 + + const handleClickBack = useCallback(() => { + history.goBack() + }, [history]) + + const handleClickFeedbacks = () => { + dispatch(updateModalIsFeedbacksOpen(true)) + } + + useEffect(() => { + if (screenType === ScreenType.MOBILE) { + setHeaderHeight( + header.current + ? header.current.clientHeight - cozyBarHeight - headerBottomHeight + : 0 + ) + } else { + setHeaderHeight( + header.current ? header.current.clientHeight - headerBottomHeight : 0 + ) + } + }, [screenType, children, setHeaderHeight]) + + return ( + <div className="header" ref={header}> + <div className="header-top"> + <div className="header-content"> + <div + className={ + !textKey && !desktopTitleKey + ? 'header-content-top header-content-top-right' + : 'header-content-top' + } + > + {textKey && ( + <div + className={`header-text ${ + screenType === ScreenType.MOBILE + ? 'text-14-normal-uppercase' + : 'text-22-bold' + }`} + > + {t(textKey)} + </div> + )} + {desktopTitleKey && ( + <div + className={`header-text-desktop ${ + screenType === ScreenType.MOBILE + ? 'text-14-normal-uppercase' + : 'text-22-bold' + }`} + > + {displayBackArrow && ( + <StyledIconButton + title={t('header.accessibility.back')} + className="header-back-button" + icon={BackArrowIcon} + onClick={handleClickBack} + /> + )} + {t(desktopTitleKey)} + </div> + )} + <StyledIconButton + title={t('header.accessibility.open_feedbacks')} + className={ + !textKey && !desktopTitleKey + ? 'header-feedbacks-button right' + : 'header-feedbacks-button' + } + icon={FeedbacksIcon} + size={40} + onClick={handleClickFeedbacks} + /> + </div> + {children} + </div> + </div> + <div className="header-bar"></div> + </div> + ) +} + +export default Header diff --git a/src/locales/fr.json b/src/locales/fr.json index 3b8e4e7a40b9b816e57a75b0427747c3a40a676a..a4b255992864b5ee317c02b2f5fcd956a44e60a5 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -34,6 +34,17 @@ "WELCOME_MODAL_CONNECT": "Vous pouvez maintenant connecter vos compteurs d’énergie à Ecolyo en toute sécurité.", "MODAL_OK": "Ok" }, + "header": { + "accessibility" : { + "back": "Retour à la page précédente", + "open_feedbacks": "Ouvrir le partage de retours" + } + }, + "modal": { + "accessibility": { + "close": "Fermer cette fenètre" + } + }, "LOADING": { "ERROR_LOADING": "Erreur pendant le chargement des données. Veuillez vérifier votre connexion internet.", "RELOAD": "Recharger" @@ -233,6 +244,12 @@ "text": "Vous pouvez le créer en vous munissant d'une facture d'eau." }, "create_account": "Je crée mon compte Eau du GL" + }, + "password_label": "Mot de passe", + "accessibility": { + "reveal_password": "Afficher le mot de passe", + "connect": "Se connecter", + "create_account" : "Se créer un compte" } }, "oauth": { @@ -261,7 +278,12 @@ "success_sending": "Nous vous remercions pour votre retour.", "warning": "Attention !", "error_empty_description": "Le champ de description est vide.", - "error_sending": "Erreur lors de l'envoi, veuillez essayer ultérieurement." + "error_sending": "Erreur lors de l'envoi, veuillez essayer ultérieurement.", + "accessibility": { + "select_type": "Sélectionner le motif", + "send": "Envoyer le retour", + "ok": "Valider" + } }, "modalOldData": { "errorTxt": "Aïe !",