diff --git a/src/assets/SCSS/custom.scss b/src/assets/SCSS/custom.scss index bc2ef94..1c7831c 100644 --- a/src/assets/SCSS/custom.scss +++ b/src/assets/SCSS/custom.scss @@ -50,7 +50,7 @@ body { overflow-y: scroll; } -.ff-fa{ +.ff-fa { font-family: "Font Awesome 5 Free", "TeleNeo"; } @@ -364,12 +364,30 @@ hr { align-self: center; padding-left: 0.5rem; } +.toast-container{ + display: flex; + position: absolute; + justify-content: center; + z-index: 99999; + bottom: 0px; + width: 100%; +} .qt-notification { + padding: 0.75rem 1.25rem; margin-bottom: 20%; border-radius: 6px; - border-color: #46a800; - color: black; - display: flex; + border: #46a800 1px solid; + background: #d4edda; + display: inline-flex !important; + flex-basis: auto; + max-width: none; +} +.qt-notification-header { + border: none; + background: #d4edda; + padding: 0%; + color: #155724; + } .qt-notification-text { @@ -470,7 +488,17 @@ td { border: 1px solid #7e7e7e; font-size: 1rem; } -.rat-list-info-link{ +.rat-list-info-link { text-decoration: underline !important; font-weight: bolder; } +.disclaimer-text{ + text-align: justify; + white-space: pre-line; + margin: 0%; +} +.speech-bubble{ + display: flex; + align-self: baseline; + margin-left: 0.1rem; +} \ No newline at end of file diff --git a/src/assets/i18n/de/translation.json b/src/assets/i18n/de/translation.json index 4b4e4e5..4432fb2 100644 --- a/src/assets/i18n/de/translation.json +++ b/src/assets/i18n/de/translation.json @@ -131,10 +131,17 @@ "searchPortalAppointmentRequired": "Terminvereinbarung notwendig", "RAT-list-info": "Die in der DCC-Auswahl gelisteten Tests beruhen auf einer auf EU-Ebene abgestimmten Liste und beinhalten auch Tests, die in Deutschland nicht erstattungsfähig sind. Die Erstattungsfähigkeit muss vom Betreiber selbst geprüft werden. Abgleich mit ", "RAT-list-info-link": "https://antigentest.bfarm.de/ords/f?p=110:100:12807215185840:::::&tz=2:00", - "RAT-list-info-link-text":"BfArM-Liste", + "RAT-list-info-link-text": "BfArM-Liste", "no-group-tooltip": "Dieser Benutzer*in kann das Schnelltestportal nicht mehr verwenden, da diesem Benutzer*in keine Teststelle zugewiesen wurde.", "no-group-error": "Sie sind keiner Teststelle zugeordnet. Bitte kontaktieren Sie ihren Teststellen Administrator.", "bsnr": "Betriebsstätten- nummer", "bsnr-tooltip": "Die Betriebsstättennummer, kurz BSNR, ist eine neunstellige Nummer, die im Rahmen der vertragsärztlichen bzw. vertragspsychotherapeutischen Versorgung den Ort der Leistungserbringung (Betriebsstätte) eindeutig identifiziert.", - "bsnr-placeholder": "Betriebsstättennummer" + "bsnr-placeholder": "Betriebsstättennummer", + "disclaimer-title": "Benachrichtigung", + "disclaimer-text1-part1": "Bitte schalten Sie Ihre Teststellen im Bereich Benutzerverwaltung für unser Schnelltestsuchportal frei.\n\nSo ermöglichen Sie Ihren Kunden, die Teststelle einfach unter ", + "disclaimer-text1-part2": " zu finden.", + "disclaimer-link": "https://map.schnelltestportal.de", + "disclaimer-text2-part1": "Um eine Teststelle für unser Schnelltestsuchportal freizuschalten, klicken Sie auf das Bearbeiten-Icon und aktivieren Sie das Kästchen Im Schnelltestsuchportal anzeigen.\n\nSo ermöglichen Sie Ihren Kunden, die Teststelle einfach unter ", + "disclaimer-text2-part2": " zu finden.\n\nSie können auch zusätzliche Informationen wie Öffnungszeiten oder Ihre Website hinterlegen.", + "disclaimer-do-not-show": "Benachrichtigung nicht mehr anzeigen." } \ No newline at end of file diff --git a/src/assets/i18n/en/translation.json b/src/assets/i18n/en/translation.json index 530f448..8bb09f3 100644 --- a/src/assets/i18n/en/translation.json +++ b/src/assets/i18n/en/translation.json @@ -116,10 +116,17 @@ "searchPortalAppointmentRequired": "Appointment Required", "RAT-list-info": "The tests listed in the DCC selection are based on a list agreed at EU level and also include tests that are not reimbursable in Germany. The operator himself has to check the eligibility for reimbursement. Comparison with ", "RAT-list-info-link": "https://antigentest.bfarm.de/ords/f?p=110:100:12807215185840:::::&tz=2:00", - "RAT-list-info-link-text":"BfArM list", + "RAT-list-info-link-text": "BfArM list", "no-group-tooltip": "This user can no longer use Schnelltestportal until a new testing facility has been assigned.", "no-group-error": "User has no testing facility assigned", "bsnr": "Permanent establishment number", "bsnr-tooltip": "The permanent establishment number, or BSNR for short, is a nine-digit number that uniquely identifies the place where the service is provided (permanent establishment) within the framework of contractual medical or contractual psychotherapeutic care.", - "bsnr-placeholder": "Permanent establishment number" + "bsnr-placeholder": "Permanent establishment number", + "disclaimer-title": "Notification", + "disclaimer-text1-part1": "Please activate your test sites in the user administration area for our quick test search portal.\n\nThis enables your customers to easily find the test site at ", + "disclaimer-text1-part2": ".", + "disclaimer-link": "https://map.schnelltestportal.de", + "disclaimer-text2-part1": "To activate a test site for our rapid test search portal, click on the edit icon and activate the Show in the rapid test search portal box\n\nThis enables your customers to easily find the test site at ", + "disclaimer-text2-part2":".\n\nYou can also store additional information such as opening times or your website.", + "disclaimer-do-not-show": "Don't show notification again." } \ No newline at end of file diff --git a/src/assets/images/Sprechblase_i.svg b/src/assets/images/Sprechblase_i.svg new file mode 100644 index 0000000..f77b3c2 --- /dev/null +++ b/src/assets/images/Sprechblase_i.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/landing-page.component.tsx b/src/components/landing-page.component.tsx index fcdf3d1..ce9e145 100644 --- a/src/components/landing-page.component.tsx +++ b/src/components/landing-page.component.tsx @@ -25,45 +25,72 @@ import { Button, Container, Fade } from 'react-bootstrap' import '../i18n'; import { useTranslation } from 'react-i18next'; -import useNavigation from '../misc/useNavigation'; import CwaSpinner from './spinner/spinner.component'; import { useKeycloak } from '@react-keycloak/web'; +import DisclamerButton from './modules/disclamer-btn.component'; +import AppContext from '../misc/appContext'; +import useLocalStorage from '../misc/useLocalStorage'; const LandingPage = (props: any) => { - const navigation = useNavigation(); + const context = React.useContext(AppContext); const { t } = useTranslation(); const { keycloak } = useKeycloak(); const [isInit, setIsInit] = React.useState(false) + const [storedLandingDisclaimerShow, setStoredLandingDisclaimerShow] = useLocalStorage('landingDisclaimerShow', true); React.useEffect(() => { - if (navigation) + if (context) { setIsInit(true); - }, [navigation]) + } + }, [context]) const hasRole = (role: string) => keycloak && (keycloak.hasRealmRole(role) || keycloak.hasRealmRole(role)); - return (!isInit ? : + return (!(isInit && context && context.navigation) ? : -

{t('translation:welcome')}

+

+ {t('translation:welcome')} + {hasRole('c19_quick_test_admin') + ? { props.setDisclaimerShow(false) }} + onCheckChange={(checked: boolean) => { setStoredLandingDisclaimerShow(!checked) }} + disclaimerText={ + <> + {t('translation:disclaimer-text1-part1')} + + {t('translation:disclaimer-link')} + + {t('translation:disclaimer-text1-part2')} + + } + /> + : <> + } +

{hasRole('c19_quick_test_counter') ? - : null} + : null} {hasRole('c19_quick_test_lab') ? - : null} + : null} {hasRole('c19_quick_test_counter') ? - : null} + : null} {hasRole('c19_quick_test_lab') ? - <> - : null} + <> + : null} {hasRole('c19_quick_test_admin') ? - : null} + : null}
-
+ ) } diff --git a/src/components/modals/group-modal.component.tsx b/src/components/modals/group-modal.component.tsx index fe78915..bbd1880 100644 --- a/src/components/modals/group-modal.component.tsx +++ b/src/components/modals/group-modal.component.tsx @@ -36,7 +36,7 @@ const emptyGroup: IGroupDetails = { pocId: '', name: '', pocDetails: '', - searchPortalConsent: false, + searchPortalConsent: true, parentGroup: '', website: '', email: '', diff --git a/src/components/modals/notification-toast.component.tsx b/src/components/modals/notification-toast.component.tsx new file mode 100644 index 0000000..dd80176 --- /dev/null +++ b/src/components/modals/notification-toast.component.tsx @@ -0,0 +1,54 @@ +/* + * Corona-Warn-App / cwa-quick-test-frontend + * + * (C) 2021, T-Systems International GmbH + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { Image, Toast, ToastHeader } from 'react-bootstrap' + +import '../../i18n'; +import { useTranslation } from 'react-i18next'; + +import successIcon from '../../assets/images/icon_success.svg'; + + +const NotificationToast = (props: any) => { + + const { t } = useTranslation(); + + return ( +
+ props.setNotificationShow(false)} + > + + +

+ {t('translation:successfull-transferred')} +

+
+
+
+ ) +} + +export default NotificationToast; \ No newline at end of file diff --git a/src/components/modules/card-header.component.tsx b/src/components/modules/card-header.component.tsx index 9179ea0..23bf80f 100644 --- a/src/components/modules/card-header.component.tsx +++ b/src/components/modules/card-header.component.tsx @@ -3,6 +3,7 @@ import { Card, Row, Col } from "react-bootstrap"; import '../../i18n'; import { useTranslation } from 'react-i18next'; +import DisclamerButton from "./disclamer-btn.component"; const CardHeader = (props: any) => { @@ -12,7 +13,17 @@ const CardHeader = (props: any) => { - {props.title} + {props.title} + {props.disclaimerText + ? + : <>} + {!props.idCard ? <> diff --git a/src/components/modules/disclamer-btn.component.tsx b/src/components/modules/disclamer-btn.component.tsx new file mode 100644 index 0000000..71be02d --- /dev/null +++ b/src/components/modules/disclamer-btn.component.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { Button, Card, Col, Modal, Row, Image, Container } from 'react-bootstrap'; + +import '../../i18n'; +import { Trans, useTranslation } from 'react-i18next'; + +import SpeechBubbleImage from '../../assets/images/Sprechblase_i.svg'; +import { FormGroupConsentCkb } from './form-group.component'; + + +const DisclamerButton = (props: any) => { + + const { t } = useTranslation(); + const [show, setShow] = React.useState(false); + + React.useEffect(() => { + setShow(props.firstTimeShow); + props.onInit(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( + <> + { setShow(true) }} + /> + + { setShow(false) }} + keyboard={false} + centered + > + + + + + {t('translation:disclaimer-title')} + + + + + +
+
+ {props.disclaimerText} +
+ +
+ + props.onCheckChange(evt.currentTarget.checked)} + type='checkbox' + checked={props.checked} + /> +
+ + + + + + + + + + +
+ + ) +} + +export default DisclamerButton; \ No newline at end of file diff --git a/src/components/user-management.component.tsx b/src/components/user-management.component.tsx index 74024a7..7a81fa6 100644 --- a/src/components/user-management.component.tsx +++ b/src/components/user-management.component.tsx @@ -31,6 +31,7 @@ import CardHeader from './modules/card-header.component'; import GroupTable from './modules/group-table.component'; import UserTable from './modules/user-table.component'; import { IGroupNode } from '../misc/user'; +import useLocalStorage from '../misc/useLocalStorage'; const UserManagement = (props: any) => { @@ -40,6 +41,7 @@ const UserManagement = (props: any) => { const [isInit, setIsInit] = React.useState(true); const [groupNodes, setGroupNodes] = React.useState(); const [userReload, setUserReload] = React.useState(false); + const [storedUserManagementDisclaimerShow, setStoredUserManagementDisclaimerShow] = useLocalStorage('userManagementDisclaimerShow', true); const handleError = (error: any, message?: string, onCancel?: () => void) => { @@ -68,7 +70,25 @@ const UserManagement = (props: any) => { : - + { props.setDisclaimerShow(false) }} + onCheckChange={(checked: boolean) => { setStoredUserManagementDisclaimerShow(!checked) }} + disclaimerText={ + <> + {t('translation:disclaimer-text2-part1')} + + {t('translation:disclaimer-link')} + + {t('translation:disclaimer-text2-part2')} + + } + /> diff --git a/src/routing.component.tsx b/src/routing.component.tsx index ed23ee9..1bfba15 100644 --- a/src/routing.component.tsx +++ b/src/routing.component.tsx @@ -43,13 +43,14 @@ import UserManagement from './components/user-management.component'; import PrivateRoute from './components/modules/private-route.component'; import IError from './misc/error'; import ErrorPage from './components/modals/error-page.component'; -import NotificationPage from './components/modals/notification-page.component'; import DataprivacyPage from './components/modals/dataprivacy.component'; import ImprintPage from './components/modals/imprint.component'; import AppContext, { IAppContext } from './misc/appContext'; import utils from './misc/utils'; import CwaSpinner from './components/spinner/spinner.component'; import { useGetValueSets } from './misc/useValueSet'; +import NotificationToast from './components/modals/notification-toast.component'; +import useLocalStorage from './misc/useLocalStorage'; const Routing = () => { @@ -62,6 +63,10 @@ const Routing = () => { const [dataPrivacyShow, setDataPrivacyShow] = React.useState(false); const [imprintShow, setImprintShow] = React.useState(false); const [isInit, setIsInit] = React.useState(false); + const [storedLandingDisclaimerShow] = useLocalStorage('landingDisclaimerShow', true); + const [storedUserManagementDisclaimerShow] = useLocalStorage('userManagementDisclaimerShow', true); + const [landingDisclaimerShow, setLandingDisclaimerShow] = React.useState(storedLandingDisclaimerShow); + const [userManagementDisclaimerShow, setUserManagementDisclaimerShow] = React.useState(storedUserManagementDisclaimerShow); const context: IAppContext = { @@ -97,7 +102,7 @@ const Routing = () => {
setErrorShow(false)} onExit={errorOnExit} /> - + @@ -112,7 +117,10 @@ const Routing = () => { exact path={context.navigation.routes.landing} > - + { setLandingDisclaimerShow(show) }} + setNotificationShow={setNotificationShow} /> @@ -171,7 +179,13 @@ const Routing = () => { path={context.navigation.routes.userManagement} roles={['c19_quick_test_admin']} component={UserManagement} - render={(props) => } + render={(props) => + { setUserManagementDisclaimerShow(show) }} + />} />