From 2341ffa4dd684fd894c5bfa106a53394a1571db5 Mon Sep 17 00:00:00 2001 From: Laura Ketola <52139145+lket@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:47:36 +0300 Subject: [PATCH 01/72] YKI(Frontend): Add footer links (#560) OPHYKIKEH-248 * YKI(Frontend): Copy VKT accessibility page as template * YKI(Frontend): Update footer links * YKI(Frontend): Keep test meaning * YKI(Frontend): Restore normal link styling * YKI(Frontend): Split footer into three columns, add titles * YKI(Frontend): Change footer button links to normal links * YKI(Frontend): Align footer links --- .../yki/public/i18n/en-GB/common.json | 1 + .../yki/public/i18n/en-GB/public.json | 3 +- .../yki/public/i18n/fi-FI/accessibility.json | 52 +++++ .../yki/public/i18n/fi-FI/common.json | 1 + .../yki/public/i18n/fi-FI/public.json | 8 +- .../yki/public/i18n/sv-SE/common.json | 1 + .../yki/src/components/layouts/Footer.tsx | 76 +++---- frontend/packages/yki/src/configs/i18n.ts | 12 ++ frontend/packages/yki/src/enums/app.ts | 1 + .../src/pages/AccessibilityStatementPage.tsx | 196 ++++++++++++++++++ .../packages/yki/src/routers/AppRouter.tsx | 5 + .../styles/components/layouts/_footer.scss | 16 +- .../pages/_accessibility-statement-page.scss | 31 +++ frontend/packages/yki/src/styles/styles.scss | 1 + .../PublicExamSessionListing.test.tsx.snap | 2 +- .../yki/src/tests/msw/fixtures/examSession.ts | 2 +- 16 files changed, 360 insertions(+), 48 deletions(-) create mode 100644 frontend/packages/yki/public/i18n/fi-FI/accessibility.json create mode 100644 frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx create mode 100644 frontend/packages/yki/src/styles/pages/_accessibility-statement-page.scss diff --git a/frontend/packages/yki/public/i18n/en-GB/common.json b/frontend/packages/yki/public/i18n/en-GB/common.json index 467754e95..249c06d59 100644 --- a/frontend/packages/yki/public/i18n/en-GB/common.json +++ b/frontend/packages/yki/public/i18n/en-GB/common.json @@ -5,6 +5,7 @@ "appNameAbbreviation": "YKI", "appTitle": "National Certificates of Language Proficiency (YKI) | Finnish National Agency for Education", "back": "Back", + "backToHomePage": "Back to front page", "cancel": "Cancel", "component": { "table": { diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index e3a136fd1..e8d1de1d4 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -70,7 +70,8 @@ "title": "Feedback and development suggestions" }, "privacy": { - "text": "Privacy policy statement (in Finnish)" + "text": "Privacy policy statement (oph.fi)", + "url": "https://www.oph.fi/en/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-information-on-the-processing-of-personal-data" }, "ykiHomepage": { "text": "Website of the National Certificates of Language Proficiency (YKI) (oph.fi)" diff --git a/frontend/packages/yki/public/i18n/fi-FI/accessibility.json b/frontend/packages/yki/public/i18n/fi-FI/accessibility.json new file mode 100644 index 000000000..0c823925e --- /dev/null +++ b/frontend/packages/yki/public/i18n/fi-FI/accessibility.json @@ -0,0 +1,52 @@ +{ + "yki": { + "accessibility": { + "content": { + "administrativeAgency": { + "description": "Saavutettavuuden valvonnan yksikkö", + "email": "saavutettavuus@avi.fi", + "link": { + "label": "Saavutettavuusvaatimukset.fi", + "url": "https://saavutettavuusvaatimukset.fi" + }, + "phone": "0295 016 000", + "switchboard": "Vaihde", + "title": "Valvontaviranomaisen yhteystiedot" + }, + "caveats": [ + ], + "composition": { + "description": "", + "title": "Tämän saavutettavuusselosteen laatiminen" + }, + "enforcement": { + "description": "", + "title": "Täytäntöönpanomenettely" + }, + "feedback": { + "description1": "", + "description2": "", + "title": "Palaute ja yhteystiedot" + }, + "furtherImprove": { + "description1": "", + "description2": "", + "title": "Teemme jatkuvasti työtä saavutettavuuden parantamiseksi" + }, + "nonAccessible": { + "description1": "", + "description2": "", + "title": "Ei-saavutettava sisältö" + }, + "status": { + "description": "", + "title": "Saavutettavuuden tila" + } + }, + "heading": { + "description": "", + "title": "Saavutettavuusseloste" + } + } + } +} diff --git a/frontend/packages/yki/public/i18n/fi-FI/common.json b/frontend/packages/yki/public/i18n/fi-FI/common.json index b5ec95196..5b44be29a 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/common.json +++ b/frontend/packages/yki/public/i18n/fi-FI/common.json @@ -5,6 +5,7 @@ "appNameAbbreviation": "YKI", "appTitle": "Yleiset kielitutkinnot | Opetushallitus", "back": "Takaisin", + "backToHomePage": "Takaisin etusivulle", "cancel": "Peruuta", "component": { "table": { diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 746100423..582b2de5f 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -62,6 +62,11 @@ "street": "Hakaniemenranta 6", "zipCity": "PL 380, 00531 Helsinki" }, + "headings": { + "statements": "Selosteet", + "info": "Lisätietoa YKIstä", + "contacts": "Yhteystiedot" + }, "links": { "accessibility": { "text": "Saavutettavuusseloste" @@ -70,7 +75,8 @@ "title": "Palaute ja kehitysideat" }, "privacy": { - "text": "Tietosuojaseloste" + "text": "Tietosuojaseloste (oph.fi)", + "url": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-tietoa-henkilotietojen-kasittelysta" }, "ykiHomepage": { "text": "Yleisten kielitutkintojen verkkosivu (oph.fi)" diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index b55abbbad..e342c123e 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -4,6 +4,7 @@ "actions": "Funktioner", "appNameAbbreviation": "YKI", "appTitle": "Allmänna språkexamina | Utbildningsstyrelsen", + "backToHomePage": "Tillbaka till framsidan", "contactEmail": "kielitutkinnot@oph.fi", "dates": { "dateTimeFormat": "l [kl.] HH:mm" diff --git a/frontend/packages/yki/src/components/layouts/Footer.tsx b/frontend/packages/yki/src/components/layouts/Footer.tsx index 465bbde68..58f944e3c 100644 --- a/frontend/packages/yki/src/components/layouts/Footer.tsx +++ b/frontend/packages/yki/src/components/layouts/Footer.tsx @@ -1,14 +1,8 @@ import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Divider, Paper } from '@mui/material'; -import { - CustomButtonLink, - ExtLink, - H3, - OPHLogoViewer, - Svg, - Text, -} from 'shared/components'; -import { Direction, Variant } from 'shared/enums'; +import { Link } from 'react-router-dom'; +import { H3, OPHLogoViewer, Svg, Text } from 'shared/components'; +import { Direction } from 'shared/enums'; import { FooterWave } from 'shared/statics'; import { @@ -27,47 +21,59 @@ export const Footer = () => {
-
- - {t('links.accessibility.text')} - - +

{t('headings.statements')}

+ + {t('links.accessibility.text')} + + - {t('links.privacy.text')} -
- + +
+
+

{t('headings.info')}

+ } aria-label={translateCommon('ykiHomepage.ariaLabel')} - /> + className="columns gapped-xxs" + target="_blank" + rel="noreferrer" + > + {t('links.ykiHomepage.text')} + +
-
+
+

{t('headings.contacts')}

{t('address.name')}

-
{t('address.street')} {t('address.zipCity')} -
{t('address.phone.title')} - + target="_blank" + rel="noreferrer" + > + {t('address.phone.number')} +
-
+

{t('links.contact.title')}:

- + target="_blank" + rel="noreferrer" + > + {translateCommon('contactEmail')} +
diff --git a/frontend/packages/yki/src/configs/i18n.ts b/frontend/packages/yki/src/configs/i18n.ts index bd723014b..1c42f69b5 100644 --- a/frontend/packages/yki/src/configs/i18n.ts +++ b/frontend/packages/yki/src/configs/i18n.ts @@ -10,6 +10,7 @@ import { DateUtils } from 'shared/utils'; import commonEN from 'public/i18n/en-GB/common.json'; import publicEN from 'public/i18n/en-GB/public.json'; +import accessibilityFI from 'public/i18n/fi-FI/accessibility.json'; import commonFI from 'public/i18n/fi-FI/common.json'; import publicFI from 'public/i18n/fi-FI/public.json'; import commonSV from 'public/i18n/sv-SE/common.json'; @@ -24,6 +25,7 @@ const supportedLangs = [langFI, langSV, langEN]; const resources = { [langFI]: { + [I18nNamespace.Accessibility]: accessibilityFI, [I18nNamespace.Common]: commonFI, [I18nNamespace.Public]: publicFI, }, @@ -78,6 +80,16 @@ const useAppTranslation = ( return useTranslation(ns, options); }; +export const useAccessibilityTranslation = () => { + const { t } = useAppTranslation( + { + keyPrefix: 'yki.accessibility', + }, + I18nNamespace.Accessibility + ); + + return t; +}; export const usePublicTranslation = ( options: UseTranslationOptions ) => { diff --git a/frontend/packages/yki/src/enums/app.ts b/frontend/packages/yki/src/enums/app.ts index af6c44fcc..9dfb385f1 100644 --- a/frontend/packages/yki/src/enums/app.ts +++ b/frontend/packages/yki/src/enums/app.ts @@ -3,6 +3,7 @@ export enum AppConstants { } export enum AppRoutes { + AccessibilityStatementPage = '/yki/saavutettavuus', Registration = '/yki/ilmoittautuminen', RegistrationPaymentStatus = '/yki/ilmoittautuminen/maksu/tila', ExamSessionRegistration = '/yki/ilmoittautuminen/tutkintotilaisuus/:examSessionId', diff --git a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx new file mode 100644 index 000000000..d50dba3ee --- /dev/null +++ b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx @@ -0,0 +1,196 @@ +import { ArrowBackIosOutlined as ArrowBackIosOutlinedIcon } from '@mui/icons-material'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { Grid, Paper, Typography } from '@mui/material'; +import { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; +import { + CustomButtonLink, + H1, + H2, + H3, + HeaderSeparator, + Text, + WebLink, +} from 'shared/components'; +import { Variant } from 'shared/enums'; +import { CommonUtils } from 'shared/utils'; + +import { + useAccessibilityTranslation, + useCommonTranslation, +} from 'configs/i18n'; +import { AppRoutes } from 'enums/app'; +import accessibilityFI from 'public/i18n/fi-FI/accessibility.json'; + +const BackButton = () => { + const translateCommon = useCommonTranslation(); + + return ( + } + className="color-secondary-dark" + > + {translateCommon('backToHomePage')} + + ); +}; + +const ItemBulletList = ({ + item, + bulletPoints, +}: { + item: string; + bulletPoints: Array; +}) => ( + +
    +
  • {item}
  • +
      + {bulletPoints.map((bulletPoint, i) => ( +
    • {bulletPoint}
    • + ))} +
    +
+
+); + +export const AccessibilityStatementPage = () => { + const translateAccessibility = useAccessibilityTranslation(); + const translateCommon = useCommonTranslation(); + const { pathname } = useLocation(); + + const caveats = Object.keys( + accessibilityFI.yki.accessibility.content.caveats + ); + + useEffect(() => { + CommonUtils.scrollToTop(); + }, [pathname]); + + return ( + + + + + +

{translateAccessibility('heading.title')}

+ + {translateAccessibility('heading.description')} +
+ + +
+
+

{translateAccessibility('content.status.title')}

+ + {translateAccessibility('content.status.description')} + +
+
+

{translateAccessibility('content.nonAccessible.title')}

+ + {translateAccessibility('content.nonAccessible.description1')} + {':'} + + + {translateAccessibility('content.nonAccessible.description2')} + + {caveats.map(({}, i) => ( + + ))} +
+
+

{translateAccessibility('content.composition.title')}

+ + {translateAccessibility('content.composition.description')} + +
+
+

{translateAccessibility('content.feedback.title')}

+ + {translateAccessibility('content.feedback.description1')} + + + {translateAccessibility('content.feedback.description2')} + {': '} + + +
+
+
+

{translateAccessibility('content.enforcement.title')}

+ + {translateAccessibility('content.enforcement.description')} + +
+
+

+ {translateAccessibility('content.administrativeAgency.title')} +

+ + {translateAccessibility( + 'content.administrativeAgency.description' + )} + + + } + /> + + + + + + {translateAccessibility( + 'content.administrativeAgency.switchboard' + )} + {': '} + {translateAccessibility('content.administrativeAgency.phone')} + +
+
+
+

{translateAccessibility('content.furtherImprove.title')}

+ + {translateAccessibility('content.furtherImprove.description1')} + + + {translateAccessibility('content.furtherImprove.description2')} + +
+
+
+
+
+ ); +}; diff --git a/frontend/packages/yki/src/routers/AppRouter.tsx b/frontend/packages/yki/src/routers/AppRouter.tsx index d769f0d35..a6f90d7f3 100644 --- a/frontend/packages/yki/src/routers/AppRouter.tsx +++ b/frontend/packages/yki/src/routers/AppRouter.tsx @@ -7,6 +7,7 @@ import { Header } from 'components/layouts/Header'; import { useCommonTranslation } from 'configs/i18n'; import { AppRoutes } from 'enums/app'; import { useAPIErrorToast } from 'hooks/useAPIErrorToast'; +import { AccessibilityStatementPage } from 'pages/AccessibilityStatementPage'; import { EvaluationOrderPage } from 'pages/EvaluationOrderPage'; import { EvaluationOrderStatusPage } from 'pages/EvaluationOrderStatusPage'; import { ExamDetailsPage } from 'pages/ExamDetailsPage'; @@ -60,6 +61,10 @@ export const AppRouter: FC = () => { path={AppRoutes.ReassessmentOrderStatus} element={} /> + } + />
diff --git a/frontend/packages/yki/src/styles/components/layouts/_footer.scss b/frontend/packages/yki/src/styles/components/layouts/_footer.scss index 1f3867e7e..a188f68fb 100644 --- a/frontend/packages/yki/src/styles/components/layouts/_footer.scss +++ b/frontend/packages/yki/src/styles/components/layouts/_footer.scss @@ -42,6 +42,7 @@ padding: 1.5rem; &__links, + &__info, &__contact-details { margin-top: 3rem; @@ -70,27 +71,24 @@ } } + & h2 { + font-size: 2rem; + } + & a, & > a > button { color: $color-secondary-dark; font-size: 1.6rem; - font-weight: 700; + font-weight: normal; letter-spacing: 0; line-height: 1.9rem; margin: 0; - padding: 6px 0; - text-decoration: none; + text-decoration: underline; text-transform: initial; @include phone { font-size: 1.5rem; } } - - &:last-of-type { - @include phone { - order: -1; - } - } } } diff --git a/frontend/packages/yki/src/styles/pages/_accessibility-statement-page.scss b/frontend/packages/yki/src/styles/pages/_accessibility-statement-page.scss new file mode 100644 index 000000000..042d06306 --- /dev/null +++ b/frontend/packages/yki/src/styles/pages/_accessibility-statement-page.scss @@ -0,0 +1,31 @@ +.accessibility-statement-page { + flex: 1; + + & &__back-button { + @include phone { + margin-top: 2rem; + padding: 2rem 2rem 0; + } + } + + & &__heading { + @include phone { + padding: 2rem 2rem 0; + } + } + + & &__content { + padding: 4rem 3rem; + + @include phone { + @include phone-divider-border-top; + @include phone-divider-border-bottom; + gap: 1.5rem; + padding: 1rem 2rem; + } + } + + & &__item-bullet-list { + margin-block-end: 0; + } +} diff --git a/frontend/packages/yki/src/styles/styles.scss b/frontend/packages/yki/src/styles/styles.scss index f98941b10..f352a40c2 100644 --- a/frontend/packages/yki/src/styles/styles.scss +++ b/frontend/packages/yki/src/styles/styles.scss @@ -11,6 +11,7 @@ @import 'components/registration/public-registration'; // Pages +@import 'pages/accessibility-statement-page'; @import 'pages/evaluation-order-page'; @import 'pages/evaluation-order-status-page'; @import 'pages/public-exam-details-page'; diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap index b3fb0cc98..eb54f3405 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap @@ -287,7 +287,7 @@ Array [ postAdmission :
- yki.co00on.2amte0.2amteTi0eFor0amt — yki.co00on.5pmte0.5pmteTi0eFor0pmt + yki.co00on.2amte0.2amteTi0eFor0amt — yki.co00on.0pmte0.0pmteTi0eFor0pmt Date: Mon, 11 Sep 2023 14:12:46 +0300 Subject: [PATCH 02/72] OTR(Frontend): OPHOTRKEH-247: allow current date for qualification begin (#561) * OTR(Frontend): OPHOTRKEH-247: allow current date for qualification begin * OTR(Frontend): OPHOTRKEH-247: oops, duplicate code here --- frontend/packages/otr/src/redux/selectors/meetingDate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/otr/src/redux/selectors/meetingDate.ts b/frontend/packages/otr/src/redux/selectors/meetingDate.ts index 354a66009..28a26a3f8 100644 --- a/frontend/packages/otr/src/redux/selectors/meetingDate.ts +++ b/frontend/packages/otr/src/redux/selectors/meetingDate.ts @@ -38,7 +38,7 @@ const filterMeetingDateByStatus = ( status: MeetingDateStatus, currentDate: Dayjs ) => { - const isBefore = DateUtils.isDatePartBefore(date, currentDate); + const isBefore = DateUtils.isDatePartBeforeOrEqual(date, currentDate); return status === MeetingDateStatus.Upcoming ? !isBefore : isBefore; }; From b9a94a4fd33dfddc30186c64b8bc5bf6eba8ed1a Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 13 Sep 2023 13:47:26 +0300 Subject: [PATCH 03/72] SHARED(Frontend): Try to match email validation logic with YKI backend --- frontend/packages/shared/CHANGELOG.MD | 12 ++++++ frontend/packages/shared/package.json | 2 +- .../shared/src/utils/inputField/inputField.ts | 38 ++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/frontend/packages/shared/CHANGELOG.MD b/frontend/packages/shared/CHANGELOG.MD index de9fc215e..129ff3867 100644 --- a/frontend/packages/shared/CHANGELOG.MD +++ b/frontend/packages/shared/CHANGELOG.MD @@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Released] +## [1.9.24] - 2023-09-13 + +### Changed + +- More stringent email validation. Uses same validation logic as YKI backend. + +## [1.9.23] - 203-08-25 + +### Added + +- Optional `title` property for OPPHLogoViewer + ## [1.9.22] - 2023-08-16 ### Changed diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index 0db530e51..807fd41ee 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@opetushallitus/kieli-ja-kaantajatutkinnot.shared", - "version": "1.9.23", + "version": "1.9.24", "description": "Shared Frontend Package", "exports": { "./components": "./src/components/index.tsx", diff --git a/frontend/packages/shared/src/utils/inputField/inputField.ts b/frontend/packages/shared/src/utils/inputField/inputField.ts index 9d44af84f..7e2a53af4 100644 --- a/frontend/packages/shared/src/utils/inputField/inputField.ts +++ b/frontend/packages/shared/src/utils/inputField/inputField.ts @@ -134,7 +134,7 @@ export class InputFieldUtils { switch (type) { case TextFieldTypes.Email: - if (!InputFieldUtils.EMAIL_REG_EXR.test(trimmedValue)) { + if (!InputFieldUtils.isValidEmail(trimmedValue)) { return CustomTextFieldErrors.EmailFormat; } break; @@ -157,6 +157,40 @@ export class InputFieldUtils { return ''; } - private static EMAIL_REG_EXR = /^.+@.+\..+$/; + static isValidEmail(email: string) { + const emailParts = email.split('@'); + if (emailParts.length != 2) { + return false; + } + + const [localPart, domainPart] = emailParts; + if (!InputFieldUtils.EMAIL_LOCAL_PART_REGEX.test(localPart)) { + return false; + } + if (localPart.indexOf('..') > -1) { + return false; + } + if (localPart.startsWith('.')) { + return false; + } + if (localPart.endsWith('.')) { + return false; + } + + if (!InputFieldUtils.EMAIL_DOMAIN_PART_REGEX.test(domainPart)) { + return false; + } + + return true; + } + + private static EMAIL_LOCAL_PART_REGEX = new RegExp( + /^[\p{Letter}0-9!#$%&'+\-\/=\?\^_`\.\{|\}~]{1,64}$/, + 'u' + ); + private static EMAIL_DOMAIN_PART_REGEX = new RegExp( + /^[\p{Letter}0-9\-\.]{1,255}$/, + 'u' + ); private static TEL_REG_EXR = /\d{7,14}$/; } From 2198a473982226ac59c618abfdf33add19030dae Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 13 Sep 2023 13:53:41 +0300 Subject: [PATCH 04/72] YKI(Frontend): Use more strict email validation logic [deploy] --- frontend/packages/yki/package.json | 2 +- frontend/yarn.lock | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frontend/packages/yki/package.json b/frontend/packages/yki/package.json index d7960f5b1..9c0e58e7a 100644 --- a/frontend/packages/yki/package.json +++ b/frontend/packages/yki/package.json @@ -26,7 +26,7 @@ "yki:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" }, "devDependencies": { "multer": "^1.4.5-lts.1" diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 932dec103..225ffdb22 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2990,7 +2990,7 @@ __metadata: languageName: unknown linkType: soft -"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23": +"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24": version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared" languageName: unknown @@ -3010,7 +3010,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.yki@workspace:packages/yki" dependencies: multer: ^1.4.5-lts.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" languageName: unknown linkType: soft @@ -12606,6 +12606,13 @@ __metadata: languageName: node linkType: hard +"shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23": + version: 1.9.23 + resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@npm:1.9.23::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40Opetushallitus%2Fkieli-ja-kaantajatutkinnot.shared%2F1.9.23%2F2aac238a8650ec36d668d15f4a72af861ab728e3" + checksum: 30cc03dac0c24683729f0e3a3f2b0c49e7918f3544d8fdeb0b003a4f6c31fd67372ccb11d432d6ef1b57c938dd222f2a02d5c315a76a7e3bd861201de3cd0c73 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" From f41ea1a0ccd4bbb3781c106f7c3faba6c4bb42b5 Mon Sep 17 00:00:00 2001 From: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:20:44 +0300 Subject: [PATCH 05/72] AKR(Frontend) OPHAKRKEH-509: allow current day for registration begin (#566) --- frontend/packages/akr/src/redux/selectors/meetingDate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/akr/src/redux/selectors/meetingDate.ts b/frontend/packages/akr/src/redux/selectors/meetingDate.ts index 354a66009..28a26a3f8 100644 --- a/frontend/packages/akr/src/redux/selectors/meetingDate.ts +++ b/frontend/packages/akr/src/redux/selectors/meetingDate.ts @@ -38,7 +38,7 @@ const filterMeetingDateByStatus = ( status: MeetingDateStatus, currentDate: Dayjs ) => { - const isBefore = DateUtils.isDatePartBefore(date, currentDate); + const isBefore = DateUtils.isDatePartBeforeOrEqual(date, currentDate); return status === MeetingDateStatus.Upcoming ? !isBefore : isBefore; }; From e4b2f4a1be949618ea2a11adb0bb4bb6a7f6e51d Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Thu, 14 Sep 2023 14:32:54 +0300 Subject: [PATCH 06/72] YKI(Frontend): Re-enable dateOfBirth placeholder text OPHYKIKEH-253 --- .../registration/steps/register/EmailRegistrationDetails.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/packages/yki/src/components/registration/steps/register/EmailRegistrationDetails.tsx b/frontend/packages/yki/src/components/registration/steps/register/EmailRegistrationDetails.tsx index e89720e7b..699f6570e 100644 --- a/frontend/packages/yki/src/components/registration/steps/register/EmailRegistrationDetails.tsx +++ b/frontend/packages/yki/src/components/registration/steps/register/EmailRegistrationDetails.tsx @@ -250,7 +250,6 @@ export const EmailRegistrationDetails = () => { From 753cd76144c8176d84c4d80c5af73ac36e3c5b46 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 14 Sep 2023 16:49:41 +0300 Subject: [PATCH 07/72] SHARED(Frontend): Require domain part of email to consist of at least two subdomains --- .../shared/src/utils/inputField/inputField.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/packages/shared/src/utils/inputField/inputField.ts b/frontend/packages/shared/src/utils/inputField/inputField.ts index 7e2a53af4..b32ee9354 100644 --- a/frontend/packages/shared/src/utils/inputField/inputField.ts +++ b/frontend/packages/shared/src/utils/inputField/inputField.ts @@ -177,9 +177,18 @@ export class InputFieldUtils { return false; } - if (!InputFieldUtils.EMAIL_DOMAIN_PART_REGEX.test(domainPart)) { + if (domainPart.length > 255) { return false; } + const domainParts = domainPart.split('.'); + if (domainParts.length < 2) { + return false; + } + for (const subdomain of domainParts) { + if (!InputFieldUtils.EMAIL_SUBDOMAIN_REGEX.test(subdomain)) { + return false; + } + } return true; } @@ -188,8 +197,8 @@ export class InputFieldUtils { /^[\p{Letter}0-9!#$%&'+\-\/=\?\^_`\.\{|\}~]{1,64}$/, 'u' ); - private static EMAIL_DOMAIN_PART_REGEX = new RegExp( - /^[\p{Letter}0-9\-\.]{1,255}$/, + private static EMAIL_SUBDOMAIN_REGEX = new RegExp( + /^[\p{Letter}0-9\-]{1,63}$/, 'u' ); private static TEL_REG_EXR = /\d{7,14}$/; From a123fdfefb2c474192e82b7e67c6eeba652fb821 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 14 Sep 2023 16:52:14 +0300 Subject: [PATCH 08/72] AKR:OTR:VKT(Frontend): Take new email validation into use --- frontend/packages/akr/package.json | 2 +- frontend/packages/otr/package.json | 2 +- frontend/packages/vkt/package.json | 2 +- frontend/yarn.lock | 13 +++---------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/frontend/packages/akr/package.json b/frontend/packages/akr/package.json index 5e300edeb..898c18b26 100644 --- a/frontend/packages/akr/package.json +++ b/frontend/packages/akr/package.json @@ -22,6 +22,6 @@ "akr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" } } diff --git a/frontend/packages/otr/package.json b/frontend/packages/otr/package.json index 19becf5b8..e86c52ecb 100644 --- a/frontend/packages/otr/package.json +++ b/frontend/packages/otr/package.json @@ -25,6 +25,6 @@ "otr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" } } diff --git a/frontend/packages/vkt/package.json b/frontend/packages/vkt/package.json index 833ab635a..03d962b81 100644 --- a/frontend/packages/vkt/package.json +++ b/frontend/packages/vkt/package.json @@ -26,6 +26,6 @@ }, "dependencies": { "reduxjs-toolkit-persist": "^7.2.1", - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" } } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 225ffdb22..c892cb9f3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2890,7 +2890,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.akr@workspace:packages/akr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" languageName: unknown linkType: soft @@ -2898,7 +2898,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.otr@workspace:packages/otr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" languageName: unknown linkType: soft @@ -3001,7 +3001,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.vkt@workspace:packages/vkt" dependencies: reduxjs-toolkit-persist: ^7.2.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" languageName: unknown linkType: soft @@ -12606,13 +12606,6 @@ __metadata: languageName: node linkType: hard -"shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.23": - version: 1.9.23 - resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@npm:1.9.23::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40Opetushallitus%2Fkieli-ja-kaantajatutkinnot.shared%2F1.9.23%2F2aac238a8650ec36d668d15f4a72af861ab728e3" - checksum: 30cc03dac0c24683729f0e3a3f2b0c49e7918f3544d8fdeb0b003a4f6c31fd67372ccb11d432d6ef1b57c938dd222f2a02d5c315a76a7e3bd861201de3cd0c73 - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" From 08b94938aae8f2335acf26649304f02444ff159d Mon Sep 17 00:00:00 2001 From: Laura Ketola <52139145+lket@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:18:27 +0300 Subject: [PATCH 09/72] YKI(Frontend): Add loading indicator to registration submit (#562) OPHYKIKEH-258 --- .../PublicRegistrationControlButtons.tsx | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationControlButtons.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationControlButtons.tsx index a2078c2c3..9dabbf8f9 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationControlButtons.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationControlButtons.tsx @@ -1,4 +1,9 @@ -import { CustomButton, CustomButtonLink, Text } from 'shared/components'; +import { + CustomButton, + CustomButtonLink, + LoadingProgressIndicator, + Text, +} from 'shared/components'; import { APIResponseStatus, Color, Severity, Variant } from 'shared/enums'; import { useDialog } from 'shared/hooks'; @@ -39,11 +44,16 @@ const SubmitButton = () => { keyPrefix: 'yki.component.registration', }); const translateCommon = useCommonTranslation(); - const { activeStep } = useAppSelector(registrationSelector); + const { + activeStep, + submitRegistration: { status: submitRegistrationStatus }, + } = useAppSelector(registrationSelector); const { showDialog } = useDialog(); const dispatch = useAppDispatch(); const getRegistrationErrors = usePublicRegistrationErrors(true); + const isSubmitInProgress = + submitRegistrationStatus === APIResponseStatus.InProgress; const handleSubmitBtnClick = () => { if (activeStep === PublicRegistrationFormStep.Register) { dispatch(setShowErrors(true)); @@ -82,17 +92,23 @@ const SubmitButton = () => { }; return ( - - {t('controlButtons.confirm')} - + + {t('controlButtons.confirm')} + + ); }; From 2fa6d818ddbfa3e4ce4ed90b90d07133d946dc69 Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Fri, 15 Sep 2023 12:22:45 +0300 Subject: [PATCH 10/72] Docs: Add GitHub conventions to README --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 1f04c7895..fd42341a9 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,8 @@ For example: AKR(Frontend): Added new translations ``` +Consider writing a detailed commit body when the change is extensive or the reasons behind it are intricate. + ### Branching naming conventions Jira ticket numbers are used as branch names with possible suffix indicating what the branch is for. @@ -192,6 +194,15 @@ feature/ ----> feature/OPHAKRKEH-250 hotfix/ ----> hotfix/akr ``` +### GitHub conventions + +Ensure the ticket number is included in the pull request title. This enables the association of a commit with its corresponding Jira ticket. + +Merging pull requests: + +- "Create a merge commit" and "Squash and merge": These options preserve a reference to the pull request and its associated branch. +- "Rebase and Merge": If you choose this option, make sure that commit messages explicitly mention the relevant ticket number. + ### Releases Production releases for different applications are marked with tags. For example for AKR, tags are named as `AKR-ga-1234` where `ga-1234` stands for the Github Action workflow number. The name of the tag also matches the name of the release in Jira. From 08069a3a50175f68b817a189317897116a0a9643 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 15 Sep 2023 14:22:41 +0300 Subject: [PATCH 11/72] YKI(Frontend): Fetch session details from backend and store into redux state --- frontend/packages/yki/src/enums/api.ts | 1 + .../packages/yki/src/interfaces/session.ts | 32 +++++++++++++ .../yki/src/redux/reducers/session.ts | 46 +++++++++++++++++++ .../packages/yki/src/redux/sagas/index.ts | 2 + .../packages/yki/src/redux/sagas/session.ts | 27 +++++++++++ .../yki/src/redux/selectors/session.ts | 3 ++ .../packages/yki/src/redux/store/index.ts | 2 + .../packages/yki/src/routers/AppRouter.tsx | 13 ++++++ 8 files changed, 126 insertions(+) create mode 100644 frontend/packages/yki/src/interfaces/session.ts create mode 100644 frontend/packages/yki/src/redux/reducers/session.ts create mode 100644 frontend/packages/yki/src/redux/sagas/session.ts create mode 100644 frontend/packages/yki/src/redux/selectors/session.ts diff --git a/frontend/packages/yki/src/enums/api.ts b/frontend/packages/yki/src/enums/api.ts index 2c1e9dcee..81ad65fde 100644 --- a/frontend/packages/yki/src/enums/api.ts +++ b/frontend/packages/yki/src/enums/api.ts @@ -12,6 +12,7 @@ export enum APIEndpoints { Logout = '/yki/auth/logout', SubmitRegistration = '/yki/api/registration/:registrationId/submit', SuomiFiAuthRedirect = '/yki/auth/', + User = '/yki/auth/user', } export enum PaymentStatus { diff --git a/frontend/packages/yki/src/interfaces/session.ts b/frontend/packages/yki/src/interfaces/session.ts new file mode 100644 index 000000000..99fbe255e --- /dev/null +++ b/frontend/packages/yki/src/interfaces/session.ts @@ -0,0 +1,32 @@ +export interface EmailAuthenticatedSession { + 'auth-method': 'EMAIL'; + identity: { + 'external-user-id': string; + }; +} + +export interface SuomiFiAuthenticatedSession { + 'auth-method': 'SUOMIFI'; + identity: { + first_name: string; + last_name: string; + ssn: string; + }; +} + +export interface CasAuthenticatedClerkSession { + 'auth-method': 'CAS'; + identity: { + username: string; + }; +} + +interface UnauthenticatedSession { + identity: null; +} + +export type SessionResponse = + | EmailAuthenticatedSession + | SuomiFiAuthenticatedSession + | CasAuthenticatedClerkSession + | UnauthenticatedSession; diff --git a/frontend/packages/yki/src/redux/reducers/session.ts b/frontend/packages/yki/src/redux/reducers/session.ts new file mode 100644 index 000000000..35d5bc4cf --- /dev/null +++ b/frontend/packages/yki/src/redux/reducers/session.ts @@ -0,0 +1,46 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { APIResponseStatus } from 'shared/enums'; + +import { + CasAuthenticatedClerkSession, + EmailAuthenticatedSession, + SessionResponse, + SuomiFiAuthenticatedSession, +} from 'interfaces/session'; + +interface SessionState { + status: APIResponseStatus; + loggedInSession?: + | EmailAuthenticatedSession + | SuomiFiAuthenticatedSession + | CasAuthenticatedClerkSession; +} + +const initialState: SessionState = { + status: APIResponseStatus.NotStarted, +}; + +const sessionSlice = createSlice({ + name: 'session', + initialState, + reducers: { + acceptSession(state, action: PayloadAction) { + state.status = APIResponseStatus.Success; + if (!action.payload.identity) { + state.loggedInSession = undefined; + } else { + state.loggedInSession = action.payload; + } + }, + loadSession(state) { + state.status = APIResponseStatus.InProgress; + }, + rejectSession(state) { + state.status = APIResponseStatus.Error; + }, + }, +}); + +export const sessionReducer = sessionSlice.reducer; +export const { acceptSession, loadSession, rejectSession } = + sessionSlice.actions; diff --git a/frontend/packages/yki/src/redux/sagas/index.ts b/frontend/packages/yki/src/redux/sagas/index.ts index 56bb177de..5419b59c6 100644 --- a/frontend/packages/yki/src/redux/sagas/index.ts +++ b/frontend/packages/yki/src/redux/sagas/index.ts @@ -7,6 +7,7 @@ import { watchNationalities } from 'redux/sagas/nationalities'; import { watchPublicIdentification } from 'redux/sagas/publicIdentification'; import { watchRegistration } from 'redux/sagas/registration'; import { watchReservationRequest } from 'redux/sagas/reservation'; +import { watchSession } from 'redux/sagas/session'; export default function* rootSaga() { yield all([ @@ -17,5 +18,6 @@ export default function* rootSaga() { watchPublicIdentification(), watchRegistration(), watchReservationRequest(), + watchSession(), ]); } diff --git a/frontend/packages/yki/src/redux/sagas/session.ts b/frontend/packages/yki/src/redux/sagas/session.ts new file mode 100644 index 000000000..d55bd3f14 --- /dev/null +++ b/frontend/packages/yki/src/redux/sagas/session.ts @@ -0,0 +1,27 @@ +import { call, put, takeLatest } from '@redux-saga/core/effects'; +import { AxiosResponse } from 'axios'; + +import axiosInstance from 'configs/axios'; +import { APIEndpoints } from 'enums/api'; +import { SessionResponse } from 'interfaces/session'; +import { + acceptSession, + loadSession, + rejectSession, +} from 'redux/reducers/session'; + +function* loadSessionSaga() { + try { + const response: AxiosResponse = yield call( + axiosInstance.get, + APIEndpoints.User + ); + yield put(acceptSession(response.data)); + } catch (error) { + yield put(rejectSession()); + } +} + +export function* watchSession() { + yield takeLatest(loadSession.type, loadSessionSaga); +} diff --git a/frontend/packages/yki/src/redux/selectors/session.ts b/frontend/packages/yki/src/redux/selectors/session.ts new file mode 100644 index 000000000..7342e7ed5 --- /dev/null +++ b/frontend/packages/yki/src/redux/selectors/session.ts @@ -0,0 +1,3 @@ +import { RootState } from 'configs/redux'; + +export const sessionSelector = (state: RootState) => state.session; diff --git a/frontend/packages/yki/src/redux/store/index.ts b/frontend/packages/yki/src/redux/store/index.ts index 1facf59aa..ab2249120 100644 --- a/frontend/packages/yki/src/redux/store/index.ts +++ b/frontend/packages/yki/src/redux/store/index.ts @@ -10,6 +10,7 @@ import { nationalitiesReducer } from 'redux/reducers/nationalities'; import { publicIdentificationReducer } from 'redux/reducers/publicIdentification'; import { registrationReducer } from 'redux/reducers/registration'; import { reservationReducer } from 'redux/reducers/reservation'; +import { sessionReducer } from 'redux/reducers/session'; import rootSaga from 'redux/sagas/index'; const saga = createSagaMiddleware(); @@ -25,6 +26,7 @@ const store = configureStore({ publicIdentification: publicIdentificationReducer, registration: registrationReducer, reservation: reservationReducer, + session: sessionReducer, }, middleware: [saga], }); diff --git a/frontend/packages/yki/src/routers/AppRouter.tsx b/frontend/packages/yki/src/routers/AppRouter.tsx index a6f90d7f3..9deeef210 100644 --- a/frontend/packages/yki/src/routers/AppRouter.tsx +++ b/frontend/packages/yki/src/routers/AppRouter.tsx @@ -1,10 +1,12 @@ import { FC, useEffect } from 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { Notifier, ScrollToTop } from 'shared/components'; +import { APIResponseStatus } from 'shared/enums'; import { Footer } from 'components/layouts/Footer'; import { Header } from 'components/layouts/Header'; import { useCommonTranslation } from 'configs/i18n'; +import { useAppDispatch, useAppSelector } from 'configs/redux'; import { AppRoutes } from 'enums/app'; import { useAPIErrorToast } from 'hooks/useAPIErrorToast'; import { AccessibilityStatementPage } from 'pages/AccessibilityStatementPage'; @@ -15,13 +17,24 @@ import { InitRegistrationPage } from 'pages/InitRegistrationPage'; import { ReassessmentPage } from 'pages/ReassessmentPage'; import { RegistrationPage } from 'pages/RegistrationPage'; import { RegistrationPaymentStatusPage } from 'pages/RegistrationPaymentStatusPage'; +import { loadSession } from 'redux/reducers/session'; +import { sessionSelector } from 'redux/selectors/session'; export const AppRouter: FC = () => { const translateCommon = useCommonTranslation(); + const sessionStatus = useAppSelector(sessionSelector).status; + const dispatch = useAppDispatch(); useEffect(() => { document.title = translateCommon('appTitle'); }, [translateCommon]); + + useEffect(() => { + if (sessionStatus === APIResponseStatus.NotStarted) { + dispatch(loadSession()); + } + }, [dispatch, sessionStatus]); + useAPIErrorToast(); return ( From 8e51a30809b1ceab0788ad0f9bd49d49e5a23fa0 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 19 Sep 2023 11:34:50 +0300 Subject: [PATCH 12/72] YKI(Frontend): Add some example user session responses to dev-proxy --- frontend/packages/yki/setupProxy.js | 68 +++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index f264305ae..8e6027f3e 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -283,24 +283,54 @@ const adminUser = { }, }; -// const organizerUser = { -// identity: { -// username: 'ykijarjestaja', -// oid: '1.2.246.562.24.62800798482', -// organizations: [ -// { -// oid: '1.2.246.562.10.28646781493', -// permissions: [{ palvelu: 'YKI', oikeus: 'JARJESTAJA' }], -// }, -// ], -// lang: 'fi', -// }, -// }; - -// const unauthenticatedUser = { -// identity: null, -// }; +/* +const organizerUser = { + identity: { + username: 'ykijarjestaja', + oid: '1.2.246.562.24.62800798482', + organizations: [ + { + oid: '1.2.246.562.10.28646781493', + permissions: [{ palvelu: 'YKI', oikeus: 'JARJESTAJA' }], + }, + ], + lang: 'fi', + }, +}; +*/ + +/* +const loginLinkAuthenticatedUser = { + identity: { + 'external-user-id': 'testikaeyttaejae@test.invalid', + }, + 'auth-method': 'EMAIL', +}; +*/ +/* +const suomiFiAuthenticatedUser = { + identity: { + first_name: 'Susanna', + nick_name: 'Susanna', + ssn: '020502E902X', + nationalities: ['784'], + 'external-user-id': '1.2.246.562.24.00123456789', + oid: '1.2.246.562.24.00123456789', + zip: null, + last_name: 'Uusivälimerkki', + street_address: null, + post_office: null, + }, + 'auth-method': 'SUOMIFI', +}; +*/ + +/* +const unauthenticatedUser = { + identity: null, +}; +*/ const getNumberBetween = (min, max) => Math.trunc(Math.random() * (max - min) + min); @@ -1202,7 +1232,9 @@ module.exports = function (app) { app.get('/yki/api/evaluation/:id', (req, res) => { const mockCall = () => { try { - evaluationPeriod = evaluationPeriods.evaluation_periods.find(ep => ep.id === req.params.id); + evaluationPeriod = evaluationPeriods.evaluation_periods.find( + (ep) => ep.id === req.params.id + ); if (evaluationPeriod) { res.send(evaluationPeriod); } else { From 00d97e1f4259ba0982705cb3e447ecb1bdf58d25 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 19 Sep 2023 11:40:19 +0300 Subject: [PATCH 13/72] YKI(Frontend): Add auth-method to clerk responses --- frontend/packages/yki/setupProxy.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index 8e6027f3e..3ae1609b9 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -281,6 +281,7 @@ const adminUser = { ], lang: 'fi', }, + 'auth-method': 'CAS' }; /* @@ -296,6 +297,7 @@ const organizerUser = { ], lang: 'fi', }, + 'auth-method': 'CAS' }; */ From 1e514d3708d9ea14528568002d40c389fbbb1f12 Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Fri, 22 Sep 2023 20:43:28 +0300 Subject: [PATCH 14/72] YKI(Frontend): Add new translations --- .../yki/public/i18n/en-GB/common.json | 1 + .../yki/public/i18n/en-GB/public.json | 150 +++--- .../yki/public/i18n/fi-FI/public.json | 81 ++-- .../yki/public/i18n/sv-SE/common.json | 1 + .../yki/public/i18n/sv-SE/public.json | 455 +++++++++++++++++- .../page-objects/publicRegistrationPage.ts | 2 +- 6 files changed, 570 insertions(+), 120 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/common.json b/frontend/packages/yki/public/i18n/en-GB/common.json index 249c06d59..a697cdaf1 100644 --- a/frontend/packages/yki/public/i18n/en-GB/common.json +++ b/frontend/packages/yki/public/i18n/en-GB/common.json @@ -66,6 +66,7 @@ }, "header": { "accessibility": { + "continueToMain": "Skip to content", "langSelectorAriaLabel": "Kieli / Språk / Language" }, "lang": { diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index e8d1de1d4..cc8f0ec1a 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -16,13 +16,13 @@ "noExaminationPartsSelected": "Select at least one subtest" }, "fixErrors": "Check and correct the following information:", - "title": "Errors on the form!" + "title": "Invalid information!" }, "fillParticipantDetails": { "errors": { "invalidBirthdate": "Invalid date of birth. Fill in as dd.mm.yyyy." }, - "instructions": "Fill in the information below for the participant who requests reassessment. You will receive a summary of the request for reassessment to your email.", + "instructions": "Fill in the information below for the participant who requests reassessment", "heading": "Participant information", "labels": { "birthdate": "Date of birth *", @@ -32,14 +32,14 @@ }, "placeholders": { "birthdate": "Fill in as dd.mm.yyyy", - "email": "E.g. nimi@sahkoposti.fi", + "email": "For example, nimi@sahkoposti.fi", "firstNames": "", "lastName": "" } }, "info": { - "refundIfChangeInEvaluation": "The reassessment fee will be refunded if the reassessment changes the original grading of the subtest.", - "requiredFields": "Sections marked with a star (*) are mandatory." + "refundIfChangeInEvaluation": "The fee will be refunded if the reassessment changes the assessment of the subtest.", + "requiredFields": "Sections marked with an asterisk (*) are mandatory." }, "renderEvaluationDetails": { "heading": "Test details", @@ -62,16 +62,20 @@ "street": "Hakaniemenranta 6", "zipCity": "P.O. Box 380, 00531 Helsinki" }, + "headings": { + "contacts": "Contact information", + "info": "More information about the National Certificates of Language Proficiency (YKI)", + "statements": "Statements" + }, "links": { "accessibility": { "text": "Accessibility statement (in Finnish)" }, "contact": { - "title": "Feedback and development suggestions" + "title": "" }, "privacy": { - "text": "Privacy policy statement (oph.fi)", - "url": "https://www.oph.fi/en/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-information-on-the-processing-of-personal-data" + "text": "Privacy policy statement" }, "ykiHomepage": { "text": "Website of the National Certificates of Language Proficiency (YKI) (oph.fi)" @@ -83,65 +87,65 @@ "header": { "evaluationPeriod": "Deadline for the request" }, - "heading": "Tutkinnot", + "heading": "Select the test for whch you wish to request a reassessment", "requestReassessment": "Request a reassessment" }, "registration": { "controlButtons": { - "abortRegistration": "Abort registration", + "abortRegistration": "Quit registration", "confirm": "Send" }, "enrollToQueue": { "dialog": { "emailAlreadyQueued": { - "description": "Olet jo aiemmin tilannut ilmoituksen peruutuspaikoista sähköpostiisi.", - "title": "Ilmoituksen tilaaminen ei onnistu" + "description": "You have previously subscribed for notifications of available seats.", + "title": "Subscription failed" }, "genericError": { - "description": "Tapahtui odottamaton virhe. Voit yrittää uudelleen.", - "title": "Ilmoituksen tilaaminen ei onnistunut" + "description": "An unexpected error occurred. Please try again.", + "title": "Subscription failed" }, "inputError": { - "description": "Tarkista antamasi sähköpostiosoite ja sen vahvistus.", - "title": "Virheitä lomakkeella" + "description": "Verify your email address", + "title": "Invalid information!" } }, - "header": "Tilaa ilmoitus peruutuspaikoista", + "header": "Order a notification of available seats", "info": { - "part1": "Tässä testissä ei ole juuri nyt vapaita paikkoja. Voit tilata ilmoituksen peruutuspaikoista.", - "part2": "Anna sähköpostiosoitteesi, niin saat ilmoituksen, jos paikkoja vapautuu." + "part1": "There are currently no available seats for this test. You can subscribe to receive notifications of possible cancellations.", + "part2": "Enter your email address, and we will notify you if any seats become available during the registration period." }, "inputs": { "confirmEmail": { - "description": "Kirjoita sähköpostiosoitteesi uudelleen", - "heading": "Vahvista sähköpostiosoitteesi" + "description": "Type in your email address again.", + "heading": "Confirm your email address." }, "email": { - "description": "Esim. nimi@sahkoposti.fi", - "heading": "Syötä sähköpostiosoitteesi" + "description": "For example, nimi@sahkoposti.fi", + "heading": "Enter your email address." }, "submit": { - "label": "Tilaa ilmoitus" + "label": "Subscribe to notifications" } }, "success": { - "heading": "Ilmoituksen tilaaminen onnistui!", - "info": "Jos paikkoja tutkintoon vapautuu, saat ilmoituksen antamaasi sähköpostiosoitteeseen" + "heading": "Subscription confirmed!", + "info": "You will receive a notification at the address [sähköpostiosoite] if any seats become available for the test." } }, "examSessionDetails": { "exam": "Test", "examFee": "Test fee", - "openings": "Available places", + "openings": "Available seats", "registrationTime": "Registration period" }, "header": "Register for a National Certificates of Language Proficiency (YKI) test", "paymentSum": { - "title": "Tutkintomaksu" + "title": "Test fee" }, "registrationButtonLabels": { "full": "Full", - "orderCancellationNotification": "Order a notification of cancellations", + "orderCancellationNotification": "Order a notification of available seats", "periodNotOpen": "Registration has not started", "register": "Register" }, @@ -151,13 +155,13 @@ "examSessionIsFull": "Test is full" }, "registrationDetails": { - "certificateLanguage": "In which language do you wish to receive the degree certificate? *", + "certificateLanguage": "In which language do you wish to receive the certificate? *", "description1": "Fill in the YKI registration form using the participant’s own personal information.", "description2": "Write the first and last name exactly as they appear on the official identity card.", "errors": { "fields": { "address": "Street address", - "certificateLanguage": "Select language of the certificate", + "certificateLanguage": "Select the language of the certificate", "dateOfBirth": "Date of birth", "email": "Email address", "emailConfirmation": "Confirm email address", @@ -169,13 +173,13 @@ "nationality": "Nationality", "phoneNumber": "Telephone number", "postNumber": "Postal code", - "postOffice": "Post office", + "postOffice": "Town/city", "privacyStatementConfirmation": "Accept the terms and conditions for processing personal data", "ssn": "Personal identity code", "termsAndConditionsAgreed": "Accept the terms and conditions for registration" }, "fixErrors": "Check and correct the following information:", - "title": "Errors on the form" + "title": "Invalid information!" }, "labels": { "address": "Street address", @@ -186,31 +190,31 @@ "gender": "Gender", "lastName": "Last name", "nationality": "Nationality", - "phoneNumber": "Phone number", + "phoneNumber": "Telephone number", "postNumber": "Postal code", - "postOffice": "Post office", + "postOffice": "Town/city", "ssn": "Personal identity code" }, "placeholders": { "address": "", "dateOfBirth": "Fill in as dd.mm.yyyy", - "email": "E.g. nimi@sahkoposti.fi", - "emailConfirmation": "Type in your email address again", + "email": "For example, nimi@sahkoposti.fi", + "emailConfirmation": "Fill in your email address again", "firstNames": "", "gender": "Select gender", "lastName": "", "nationality": "Select nationality", - "phoneNumber": "Input in the format +358 50 123 4567", + "phoneNumber": "Use the format +358 50 123 4567", "postNumber": "", "postOffice": "" }, "finnishSSN": "Do you have a Finnish personal identity code? *", - "instructionLanguage": "In which language do you wish to complete the exercises? *", - "requiredFields": "Sections marked with a star (*) are mandatory. ", + "instructionLanguage": "In which language do you wish to read the instructions of the tasks? *", + "requiredFields": "Sections marked with an asterisk (*) are mandatory.", "ssn": "Personal identity code", "termsAndConditions": { "description1": "The registration for an YKI test is binding. Once you have confirmed your registration, you can no longer change it.", - "description2": "Check that you are registering for a test of the correct language and level. ", + "description2": "Check that you are registering for a test of the correct language and level.", "label": "I accept the terms and conditions for registration *", "title": "Terms and conditions for registration" }, @@ -238,7 +242,7 @@ "stepper": { "active": "Active", "completed": "Completed", - "paymentAborted": "Payment was aborted", + "paymentAborted": "Payment was cancelled", "phase": "Phase", "step": { "Done": "Done", @@ -246,19 +250,19 @@ "Payment": "Payment", "Register": "Register" }, - "welcomeToExam": "Welcome to the test!" + "welcomeToExam": "Welcome to the YKI test!" }, "steps": { "identify": { - "caption": "You must identify yourself to register for a YKI test. Use the personal information of the person taking the YKI test for the identification. ", + "caption": "You must identify yourself to register for a YKI test. Use the personal information of the person taking the YKI test for the identification.", "emailButtonText": "identify with email", "emailError": "Invalid email address", "emailInput": { "label": "Fill in your email address", - "placeholder": "E.g. nimi@sahkoposti.fi" + "placeholder": "For example, nimi@sahkoposti.fi" }, "emailLink": { - "error": "Requesting a login link failed. You may try again.", + "error": "Identification failed. You may try again.", "incorrectEmailDialog": { "title": "Request failed", "description": "Check your email address." @@ -280,12 +284,12 @@ }, "payment": { "cancel": { - "description": "The registration is only confirmed after paying the test fee.", - "heading": "Payment canceled", - "title": "You canceled the payment" + "description": "Your registration will be confirmed after you have paid the test fee.", + "heading": "Payment was cancelled", + "title": "You cancelled the payment" }, "error": { - "description": "You can try to pay again. The registration is only confirmed after successfully paying the test fee.", + "description": "You may try to pay again. Your registration will be confirmed after you have paid the test fee.", "heading": "Payment error", "title": "An error occurred when paying." }, @@ -294,7 +298,7 @@ "beforeYkiTest": { "description": "To prepare for the YKI test, you can read the instructions provided by the Finnish National Agency for Education on their website:", "label": "Before the YKI test (oph.fi)", - "url": "https://www.oph.fi/en/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ennen-testia" + "url": "https://www.oph.fi/en/education-and-qualifications/yki-test" }, "part1": "Your registration has been confirmed! You will receive a confirmation email shortly.", "part2": "Your test centre will send you an email closer to the test date with detailed instructions on how to get there, including the exact location and start time.", @@ -302,7 +306,7 @@ "specialArrangements": { "description": "If you require special arrangements, send your application as soon as possible according to the instructions provided by the Finnish National Agency for Education. The instructions can be found on their website:", "label": "Do you need special arrangements? (oph.fi)", - "url": "https://www.oph.fi/en/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-do-you-need-special-arrangements" + "url": "https://www.oph.fi/en/education-and-qualifications/registering-yki-test#anchor-do-you-need-special-arrangements" }, "title": "Welcome to the YKI test!" }, @@ -311,7 +315,7 @@ }, "register": { "inProgress": { - "heading": "Register to test" + "heading": "Register for a test" }, "success": { "heading": "Registration form sent" @@ -320,16 +324,16 @@ }, "unavailable": { "full": { - "description": "Ilmoittautuminen ei ole mahdollista juuri nyt. Tilaisuuteen ei ole vapaita paikkoja.", - "title": "Tilaisuus on täynnä" + "description": "Registration is not possible at the moment. There are no available seats for this test.", + "title": "The test is full." }, "past": { - "description": "Tilaisuuteen ilmoittautuminen ei ole enää mahdollista. Ilmoittautumisaika on päättynyt.", - "title": "Ilmoittautumisaika päättynyt" + "description": "Registration is not possible at the moment. The registration period has ended.", + "title": "Registration period has ended" }, "upcoming": { - "description": "Tilaisuuteen ilmoittautunen ei ole mahdollista. Ilmoittautumisaika ei ole vielä alkanut.", - "title": "Ilmoittautumisaika ei ole alkanut" + "description": "Registration is not possible at the moment. The registration period has not started yet.", + "title": "Registration has not started" } } } @@ -337,7 +341,7 @@ "pages": { "evaluationOrderPage": { "notFound": "Tarkistusarvioinnin tietoja ei löytynyt!", - "title": "Pyydä tarkistusarviointia", + "title": "Request a reassessment", "toasts": { "loadingError": "Tarkistusarvioinnin tietoja ei voitu ladata!" } @@ -399,11 +403,11 @@ "registrationPage": { "abortDialog": { "actions": { - "cancel": "I don't want to abort", - "confirm": "Yes, I want to abort the registration" + "cancel": "I don't want to quit", + "confirm": "Yes, I want to quit the registration" }, - "description": "Do you want to abort the registration? Any information filled will be lost.", - "title": "Abort registration" + "description": "Do you want to quit and leave this page? Any information filled will be lost.", + "title": "Quit registration" }, "description": { "part1": { @@ -415,36 +419,36 @@ }, "part2": { "link": { - "label": "National Certificates of Language Proficiency (YKI)", - "url": "https://www.oph.fi/en/national-certificates-language-proficiency-yki" + "label": "Do you need special arrangements?", + "url": "https://www.oph.fi/en/education-and-qualifications/registering-yki-test#anchor-do-you-need-special-arrangements" }, "general": "The National Certificates of Language Proficiency (YKI) test measures functional language proficiency in everyday situations at the basic, intermediate, or advanced levels. All YKI tests consist of four subtests: speaking, listening comprehension, writing, and reading comprehension. You will get a separate grade for each subtest.", - "languages": "You can take the test in the following languages: English, Finnish, French, German, Italian, Northern Sámi, Russian, Spanish, and Swedish. For more information on YKI tests, please see the website of the Finnish National Agency for Education:" + "languages": "If you require special arrangements in YKI test, please read the instructions on the website of the Finnish National Agency for Education:" } }, "filters": { "buttons": { - "empty": "Empty selection", + "empty": "Clear selection", "showResults": "Show results ({{count}})" }, "errorDialog": { - "description": "To view results, select the desired language and proficiency level. Then press again the SHOW RESULTS button.", - "title": "Select exam language and proficiency level" + "description": "To view results, select the language and proficiency level. Click the SHOW RESULTS button.", + "title": "Select the language and proficiency level" }, "errors": { "required": "Selection is required" }, "heading": "Search for tests", - "information": "Search for YKI-tests by choosing the language and proficiency level. Press the SHOW RESULTS button to see the results.", + "information": "Search for YKI-tests by choosing the language and proficiency level. Click the SHOW RESULTS button to see the results.", "selectExamDetails": { "prompt": "Choose test", "required": "(required)" } }, "labels": { - "excludeFullSessions": "Only show tests that have free places", + "excludeFullSessions": "Only show tests that have available seats", "excludeNonOpenSessions": "Only show tests that are open for registration now", - "filterExamSessions": "Filter test sessions", + "filterExamSessions": "Filter results", "selectLanguage": "Choose language", "selectLevel": "Choose proficiency level", "selectMunicipality": "Choose municipality" diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 582b2de5f..ea758bcb9 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -13,16 +13,16 @@ "email": "Sähköpostiosoite", "firstNames": "Etunimet", "lastName": "Sukunimi", - "noExaminationPartsSelected": "Valitse ainakin yksi tarkistettava osakoe" + "noExaminationPartsSelected": "Valitse vähintään yksi osakoe" }, "fixErrors": "Korjaa puuttuvat tai virheelliset tiedot:", - "title": "Virheitä lomakkeella!" + "title": "Tiedoissa on korjattavaa!" }, "fillParticipantDetails": { "errors": { - "invalidBirthdate": "Virheellinen syntymäaika. Syötä muodossa pp.kk.vvvv." + "invalidBirthdate": "Virheellinen syntymäaika. Kirjoita muodossa pp.kk.vvvv." }, - "instructions": "Täytä alle YKI-testin osallistujan tiedot. Saat tarkistusarviointipyynnöstä yhteenvedon sähköpostiisi.", + "instructions": "Täytä alle sen osallistujan tiedot, joka hakee tarkistusarviointia.", "heading": "Osallistujan tiedot", "labels": { "birthdate": "Syntymäaika *", @@ -31,7 +31,7 @@ "lastName": "Sukunimi *" }, "placeholders": { - "birthdate": "Syötä muodossa pp.kk.vvvv", + "birthdate": "Kirjoita muodossa pp.kk.vvvv", "email": "Esim. nimi@sahkoposti.fi", "firstNames": "", "lastName": "" @@ -43,10 +43,10 @@ }, "renderEvaluationDetails": { "heading": "Tutkinnon tiedot", - "info": "Olet pyytämässä tarkistusarviointia seuraavasta tutkinnosta:" + "info": "Pyydä tarkistusarviointia seuraavasta tutkinnosta:" }, "selectExaminationParts": { - "heading": "Osakokeiden valinta", + "heading": "Valitse osakokeet", "selectAtLeastOne": "Valitse vähintään yksi osakoe", "selectParts": "Valitse ne osakokeet, joista haluat pyytää tarkistusarviointia *", "sumTotal": "Tarkistusarvioinnin hinta yhteensä" @@ -63,9 +63,9 @@ "zipCity": "PL 380, 00531 Helsinki" }, "headings": { - "statements": "Selosteet", - "info": "Lisätietoa YKIstä", - "contacts": "Yhteystiedot" + "contacts": "Yhteystiedot", + "info": "Lisätietoa yleisistä kielitutkinnoista (YKI)", + "statements": "Selosteet" }, "links": { "accessibility": { @@ -75,8 +75,7 @@ "title": "Palaute ja kehitysideat" }, "privacy": { - "text": "Tietosuojaseloste (oph.fi)", - "url": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-tietoa-henkilotietojen-kasittelysta" + "text": "Tietosuojaseloste" }, "ykiHomepage": { "text": "Yleisten kielitutkintojen verkkosivu (oph.fi)" @@ -86,9 +85,9 @@ "publicEvaluationPeriodListing": { "evaluationPeriodNotYetOpen": "Pyydä tarkistusarviointia {{- startDate}} alkaen", "header": { - "evaluationPeriod": "Tarkistusarvioinnin pyyntöaika" + "evaluationPeriod": "Aika, jolloin voit pyytää tarkistusarviointia:" }, - "heading": "Tutkinnot", + "heading": "Valitse tutkinto, josta haluat pyytää tarkistusarviointia", "requestReassessment": "Pyydä tarkistusarviointia" }, "registration": { @@ -100,21 +99,21 @@ "dialog": { "emailAlreadyQueued": { "description": "Olet jo aiemmin tilannut ilmoituksen peruutuspaikoista sähköpostiisi.", - "title": "Ilmoituksen tilaaminen ei onnistu" + "title": "Ilmoituksen tilaaminen ei onnistunut" }, "genericError": { "description": "Tapahtui odottamaton virhe. Voit yrittää uudelleen.", "title": "Ilmoituksen tilaaminen ei onnistunut" }, "inputError": { - "description": "Tarkista antamasi sähköpostiosoite ja sen vahvistus.", - "title": "Virheitä lomakkeella" + "description": "Tarkista antamasi sähköpostiosoite.", + "title": "Tiedoissa on korjattavaa!" } }, "header": "Tilaa ilmoitus peruutuspaikoista", "info": { "part1": "Tässä testissä ei ole juuri nyt vapaita paikkoja. Voit tilata ilmoituksen peruutuspaikoista.", - "part2": "Anna sähköpostiosoitteesi, niin saat ilmoituksen, jos paikkoja vapautuu." + "part2": "Anna sähköpostiosoitteesi, niin saat ilmoituksen, jos paikkoja vapautuu ilmoittautumisaikana." }, "inputs": { "confirmEmail": { @@ -123,7 +122,7 @@ }, "email": { "description": "Esim. nimi@sahkoposti.fi", - "heading": "Syötä sähköpostiosoitteesi" + "heading": "Kirjoita sähköpostiosoitteesi" }, "submit": { "label": "Tilaa ilmoitus" @@ -180,7 +179,7 @@ "termsAndConditionsAgreed": "Hyväksy ilmoittautumisen ehdot" }, "fixErrors": "Korjaa puuttuvat tai virheelliset tiedot:", - "title": "Virheitä lomakkeella" + "title": "Tiedoissa on korjattavaa!" }, "labels": { "address": "Katuosoite", @@ -198,19 +197,19 @@ }, "placeholders": { "address": "", - "dateOfBirth": "Syötä muodossa pp.kk.vvvv", + "dateOfBirth": "Kirjoita muodossa pp.kk.vvvv", "email": "Esim. nimi@sahkoposti.fi", "emailConfirmation": "Kirjoita sähköpostiosoite uudelleen", "firstNames": "", "gender": "Valitse sukupuoli", "lastName": "", "nationality": "Valitse kansalaisuus", - "phoneNumber": "Syötä muodossa +358 50 123 4567", + "phoneNumber": "Kirjoita muodossa +358 50 123 4567", "postNumber": "", "postOffice": "" }, "finnishSSN": "Onko sinulla suomalainen henkilötunnus? *", - "instructionLanguage": "Millä kielellä haluat tehdä tehtävät? *", + "instructionLanguage": "Millä kielellä haluat tehtävien ohjeet? *", "requiredFields": "Tähdellä (*) merkityt osiot ovat pakollisia.", "ssn": "Henkilötunnus", "termsAndConditions": { @@ -219,7 +218,7 @@ "label": "Hyväksyn ilmoittautumisen ehdot *", "title": "Ilmoittautumisen ehdot" }, - "title": "Syötä tietosi", + "title": "Täytä tietosi", "whatsNext": { "description": "Kun olet lähettänyt ilmoittautumislomakkeen, saat sähköpostiisi linkin tutkintomaksun maksamista varten. Ilmoittautuminen vahvistuu vasta, kun olet maksanut tutkintomaksun.", "title": "Mitä tapahtuu seuraavaksi?" @@ -259,11 +258,11 @@ "emailButtonText": "tunnistaudu sähköpostilla", "emailError": "Virheellinen sähköpostiosoite", "emailInput": { - "label": "Syötä sähköpostiosoitteesi", + "label": "Kirjoita sähköpostiosoitteesi", "placeholder": "Esim. nimi@sahkoposti.fi" }, "emailLink": { - "error": "Kirjautumislinkin tilaaminen epäonnistui. Voit yrittää uudestaan.", + "error": "Tunnistautuminen epäonnistui. Voit yrittää uudestaan.", "incorrectEmailDialog": { "title": "Pyyntö epäonnistui", "description": "Tarkista sähköpostiosoite." @@ -285,12 +284,12 @@ }, "payment": { "cancel": { - "description": "Ilmoittautuminen astuu voimaan vasta kun maksat tutkintomaksun.", + "description": "Ilmoittautuminen vahvistuu vasta, kun olet maksanut tutkintomaksun.", "heading": "Maksutapahtuma keskeytyi", "title": "Keskeytit maksutapahtuman" }, "error": { - "description": "Voit yrittää maksamista uudestaan. Ilmoittautuminen astuu voimaan vasta kun maksat tutkintomaksun.", + "description": "Voit yrittää maksamista uudestaan. Ilmoittautuminen vahvistuu vasta, kun olet maksanut tutkintomaksun.", "heading": "Virhe maksutapahtumassa", "title": "Maksutapahtuman käsittelyssä tapahtui virhe" }, @@ -325,15 +324,15 @@ }, "unavailable": { "full": { - "description": "Ilmoittautuminen ei ole mahdollista juuri nyt. Tilaisuuteen ei ole vapaita paikkoja.", - "title": "Tilaisuus on täynnä" + "description": "Ilmoittautuminen ei ole mahdollista juuri nyt. Tutkinnossa ei ole vapaita paikkoja.", + "title": "Tutkinto on täynnä" }, "past": { - "description": "Tilaisuuteen ilmoittautuminen ei ole enää mahdollista. Ilmoittautumisaika on päättynyt.", + "description": "Ilmoittautuminen ei ole enää mahdollista. Ilmoittautumisaika on päättynyt.", "title": "Ilmoittautumisaika päättynyt" }, "upcoming": { - "description": "Tilaisuuteen ilmoittautunen ei ole mahdollista. Ilmoittautumisaika ei ole vielä alkanut.", + "description": "Ilmoittautuminen ei ole vielä mahdollista. Ilmoittautumisaika ei ole alkanut.", "title": "Ilmoittautumisaika ei ole alkanut" } } @@ -397,7 +396,7 @@ }, "introduction": { "fee": "Tarkistusarviointien käsittelystä on tullut maksullista keväästä 2021 alkaen.", - "info": "Tutkinnon suorittajalla on oikeus pyytää tarkistusarviointia, jos hän ei ole tyytyväinen saamaansa arviointiin. Tarkistusarviointia tulee hakea 14 vuorokauden kuluessa todistuksen saamisesta." + "info": "Tutkinnon suorittajalla on oikeus pyytää tarkistusarviointia, jos hän ei ole tyytyväinen saamaansa arviointiin. Tarkistusarviointia tulee hakea 14 vuorokauden kuluessa todistuksen saamisesta.\nVoit pyytää tarkistusarviointia 14 vuorokauden sisällä siitä, kun sinulla on ollut tilaisuus saada YKI-testisi tulos." }, "title": "Yleiset kielitutkinnot (YKI) - Tarkistusarviointi" }, @@ -420,16 +419,16 @@ }, "part2": { "link": { - "label": "Yleiset kielitutkinnot (YKI)", - "url": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki" + "label": "Tarvitsetko erityisjärjestelyjä?", + "url": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/ilmoittautuminen-yki-testiin#anchor-tarvitsetko-erityisjarjestelyja" }, - "general": "Yleisissä kielitutkinnoissa (YKI) arvioidaan yleiskielen osaamista käytännön tilanteissa perustasolla, keskitasolla tai ylimmällä tasolla. Kaikissa YKI-tutkinnoissa on neljä osakoetta: puhuminen, puheen ymmärtäminen, kirjoittaminen ja tekstin ymmärtäminen. Saat jokaisesta osakokeesta oman arvosanan.", - "languages": "Tutkinnon voit suorittaa seuraavissa kielissä: englanti, espanja, italia, pohjoissaame, ranska, ruotsi, saksa, suomi ja venäjä. Lue lisää yleisistä kielitutkinnoista Opetushallituksen verkkosivuilta:" + "general": "Yleisissä kielitutkinnoissa (YKI) arvioidaan yleiskielen osaamista käytännön tilanteissa perustasolla, keskitasolla tai ylimmällä tasolla. Kaikissa YKI-testeissä on neljä osakoetta: puhuminen, puheen ymmärtäminen, kirjoittaminen ja tekstin ymmärtäminen. Saat jokaisesta osakokeesta oman arvosanan.", + "languages": "Jos tarvitset erityisjärjestelyjä YKI-testissä, lue ohjeet erityisjärjestelyjen hakemiseen Opetushallituksen verkkosivuilta:" } }, "filters": { "buttons": { - "empty": "Tyhjennä kaikki", + "empty": "Tyhjennä valinnat", "showResults": "Näytä tulokset ({{count}})" }, "errorDialog": { @@ -443,14 +442,14 @@ "information": "Hae tästä sopivaa YKI-testiä. Valitse kieli ja taitotaso. Paina NÄYTÄ TULOKSET -painiketta.", "selectExamDetails": { "prompt": "Valitse tutkinto", - "required": "(pakollinen)", - "optional": "(valinnainen)" + "optional": "(valinnainen)", + "required": "(pakollinen)" } }, "labels": { "excludeFullSessions": "Näytä vain kielitutkinnot, joissa on tilaa", "excludeNonOpenSessions": "Näytä vain kielitutkinnot, joihin voi ilmoittautua nyt", - "filterExamSessions": "Suodata tutkintotilaisuuksia", + "filterExamSessions": "Rajaa tuloksia", "selectLanguage": "Valitse kieli", "selectLevel": "Valitse taitotaso", "selectMunicipality": "Valitse paikkakunta" diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index e342c123e..f02437574 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -18,6 +18,7 @@ }, "header": { "accessibility": { + "continueToMain": "Fortsätt till innehållet", "langSelectorAriaLabel": "Kieli / Språk / Language" }, "lang": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index e515464dc..77c0971c3 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -1,15 +1,460 @@ { "yki": { + "component": { + "evaluationOrderForm": { + "actionButtons": { + "cancel": "Annulera", + "pay": "Betala" + }, + "errorDialog": { + "errors": { + "acceptConditions": "Godkänn villkoren för behandling av personuppgifter", + "birthdate": "Födelsedatum", + "email": "E-postadress", + "firstNames": "Förnamn", + "lastName": "Efternamn", + "noExaminationPartsSelected": "Välj minst ett delprov." + }, + "fixErrors": "Fyll i uppgifter som saknas eller korrigera felaktiga uppgifter.", + "title": "Felaktiga uppgifter!" + }, + "fillParticipantDetails": { + "errors": { + "invalidBirthdate": "Felaktig födelsedatum. Ange i formatet dd.mm.åååå." + }, + "instructions": "Fyll i uppgifterna om deltagaren som begär om kontrollbedömning.", + "heading": "Deltagarens uppgifter", + "labels": { + "birthdate": "Födelsedatum", + "email": "E-postadress", + "firstNames": "Förnamn", + "lastName": "Efternamn" + }, + "placeholders": { + "birthdate": "Skriv i formatet dd.mm.åååå.", + "email": "Till exempel nimi@sahkoposti.fi", + "firstNames": "", + "lastName": "" + } + }, + "info": { + "refundIfChangeInEvaluation": "Avgiften återbetalas om bedömningen av delprovet ändrar i samband med kontrollbedömningen.", + "requiredFields": "Fält markerade med en asterisk (*) är obligatoriska." + }, + "renderEvaluationDetails": { + "heading": "Uppgifter om examen", + "info": null + }, + "selectExaminationParts": { + "heading": null, + "selectAtLeastOne": null, + "selectParts": "Välj de delprov för vilka du vill att bedömningen kontrolleras", + "sumTotal": null + } + }, + "footer": { + "address": { + "name": "Utbildningsstyrelsen", + "phone": { + "number": "+358 29 533 1000", + "title": "Telefon" + }, + "street": "Hagnäskajen 6", + "zipCity": "PB 380, 00531 Helsingfors" + }, + "headings": { + "contacts": "Kontaktuppgifter", + "info": "Mera information om Allmänna språkexamina (YKI)", + "statements": "Utlåtanden och direktiv" + }, + "links": { + "accessibility": { + "text": "Tillgänglighetsdirektiv" + }, + "contact": { + "title": "" + }, + "privacy": { + "text": "Information om behandlingen av personuppgifter" + }, + "ykiHomepage": { + "text": "Webbplatsen för allmänna språkexamina (YKI) (oph.fi)" + } + } + }, + "publicEvaluationPeriodListing": { + "evaluationPeriodNotYetOpen": null, + "header": { + "evaluationPeriod": null + }, + "heading": null, + "requestReassessment": "Gör en begäran om kontrollbedömning" + }, + "registration": { + "controlButtons": { + "abortRegistration": "Avbryt anmälan", + "confirm": "Skicka" + }, + "enrollToQueue": { + "dialog": { + "emailAlreadyQueued": { + "description": "Du har redan tidigare beställt en avisering om lediga platser i testet till din e-post.", + "title": "Beställning missllyckades" + }, + "genericError": { + "description": "Ett oväntat fel har uppstått. Vänligen försök på nytt.", + "title": "Beställning missllyckades" + }, + "inputError": { + "description": "Kontrollera din e-postadress", + "title": "Felaktiga uppgifter!" + } + }, + "header": "Beställ en avisering om lediga platser", + "info": { + "part1": "Det finns inga lediga platser i det här testet just nu. Du kan beställa en avisering om lediga platser.", + "part2": "Ange din e-postadress om du vill beställa en avisering om lediga platser i testet." + }, + "inputs": { + "confirmEmail": { + "description": "Skriv din e-postadress på nytt", + "heading": "Bekräfta din e-postadress" + }, + "email": { + "description": "Till exempel nimi@sahkoposti.fi", + "heading": "Skriv din e-postadress" + }, + "submit": { + "label": "Beställ en avisering" + } + }, + "success": { + "heading": "Beställning lyckades!", + "info": "Du får en avisering till adressen [sähköpostiosoite] i fall det blir lediga platser." + } + }, + "examSessionDetails": { + "exam": "Test", + "examFee": "Examensavgift", + "openings": "Lediga platser", + "registrationTime": "Anmälningstiden" + }, + "header": "Anmäl dig till en allmän språkexamen (YKI)", + "paymentSum": { + "title": "Examensavgift" + }, + "registrationButtonLabels": { + "full": "Fullbokat", + "orderCancellationNotification": "Beställ en avisering om lediga platser", + "periodNotOpen": "Anmälan har inte börjat", + "register": "Anmäl dig" + }, + "registrationUnavailable": { + "admissionOpensOn": "Anmälan börjar {{- startDate}}", + "admissionPeriodIsClosed": "Anmälan har slutat", + "examSessionIsFull": "Testet är fullbokat" + }, + "registrationDetails": { + "certificateLanguage": "På vilket språk vill du ha intyget? *", + "description1": "Fyll i YKI- anmälningsblankketten med testdeltagarens uppgifter.", + "description2": "För- och efternamn måste stavas på samma sätt som på deltagarens officiella identitetsbevis.", + "errors": { + "fields": { + "address": "Gatuadress", + "certificateLanguage": "Välj språket på intyget", + "dateOfBirth": "Födelsedatum", + "email": "E-postadress", + "emailConfirmation": "Bekräfta din e-postadress", + "firstNames": "Förnamn", + "gender": "Kön", + "hasSSN": "Har du en finsk personbeteckning?", + "instructionLanguage": "Välj språket på uppgifter", + "lastName": "Efternamn", + "nationality": "Nationalitet", + "phoneNumber": "Telefonnummer", + "postNumber": "Postnummer", + "postOffice": "Postanstalt", + "privacyStatementConfirmation": "Godkänn villkoren för behandling av personuppgifter", + "ssn": "Personbeteckning", + "termsAndConditionsAgreed": "Godkänn villkoren för anmälan" + }, + "fixErrors": "Fyll i uppgifter som saknas eller korrigera felaktiga uppgifter:", + "title": "Felaktiga uppgifter!" + }, + "labels": { + "address": "Gatuadress", + "dateOfBirth": "Födelsedatum", + "email": "E-postadress", + "emailConfirmation": "Bekräfta din e-postadress", + "firstNames": "Förnamn", + "gender": "Kön", + "lastName": "Efternamn", + "nationality": "Nationalitet", + "phoneNumber": "Telefonnummer", + "postNumber": "Postnummer", + "postOffice": "Postanstalt", + "ssn": "Personbeteckning" + }, + "placeholders": { + "address": "", + "dateOfBirth": "Skriv i formatet dd.mm.åååå.", + "email": "Till exempel nimi@sahkoposti.fi", + "emailConfirmation": "Skriv din e-postadress på nytt", + "firstNames": "", + "gender": "Välj kön", + "lastName": "", + "nationality": "Välj nationalitet", + "phoneNumber": "Skriv i formatet +358 50 123 4567", + "postNumber": "", + "postOffice": "" + }, + "finnishSSN": "Har du en finsk personbeteckning? *", + "instructionLanguage": "På vilket språk vill du ha anvisningarna för uppgifterna?", + "requiredFields": "Fält markerade med en asterisk (*) är obligatoriska.", + "ssn": "Personbeteckning", + "termsAndConditions": { + "description1": "Anmälan till YKI-testet är bindande. Du kan inte ändra din anmälan efter att du har bekräftat den.", + "description2": "Kontrollera att du anmäler dig till ett YKI-test på rätt språk och nivå.", + "label": "Jag godkänner villkoren för anmälan *", + "title": "Villkoren för anmälan" + }, + "title": "Skriv dina uppgifter", + "whatsNext": { + "description": "Efter att du skickat din anmälningsblankett får du ett e-postmeddelande med en länk till betalningen av examensavgift. Anmälning är bekräftat först då du har betalat examensavgiften.", + "title": "Vad händer sen?" + } + }, + "registrationFormSubmitted": { + "proceedToPayment": { + "confirmation": "Du har skickat anmälningsblanketten för YKI test", + "dueDateReminder": { + "text1": "Important! E-postmeddelandet innehåller även den sista dagen du kan betala examensavgiften.", + "text2": "Betala i tid så du kan delta i YKI-testet." + }, + "paymentLinkEmail": { + "text1": "Du får ett e-postmeddelande med en betalningslänk.", + "text2": "Meddelandet kommer från adressen noreply@opintopolku.fi.", + "text3": "Anmälan är bekräftad först då du har betalt examensavgiften." + }, + "title": "Bekräfta anmälan genom att betala examensavgiften." + } + }, + "stepper": { + "active": "Aktiv", + "completed": "Färdig", + "paymentAborted": "Betalningen kunde inte genomföras", + "phase": "Fas", + "step": { + "Done": "Färdig", + "Identify": "Identifiera dig", + "Payment": "Betalning", + "Register": "Anmäl dig" + }, + "welcomeToExam": "Välkommen till YKI-test" + }, + "steps": { + "identify": { + "caption": "Du ska identifiera dig för att anmäla dig till YKI-test. Använd den persons uppgifter som deltar i YKI-testet.", + "emailButtonText": "Identifiera dig med din e-post", + "emailError": "Felaktig e-postadress", + "emailInput": { + "label": "Skriv din e-postadress", + "placeholder": "Till exempel nimi@sahkoposti.fi" + }, + "emailLink": { + "error": "Identifieringen misslyckades. Du kan försöka på nytt.", + "incorrectEmailDialog": { + "title": "Begäran misslyckades", + "description": "Kontrollera din e-postadress" + }, + "success": "Anmälningslänken har skickats till:" + }, + "registrationIsBindingAdvisory": "Kontrollera att du anmäler dig till ett YKI-test på rätt språk och nivå. Anmälan till YKI-testet är bindande. Du kan inte ändra din anmälan efter att du har bekräftat den.", + "selectIdentificationMethod": "Välj din identifieringsmetod", + "suomiFiButtonText": "Identifiera dig via suomi.fi -tjänsten", + "title": "Identifiera dig för anmälan", + "withFinnishSSN": { + "description": "Identifiera dig via suomi.fi -tjänsten", + "info": "Efter indentifiering styrs du automatiskt till anmälningsblanketten" + }, + "withoutFinnishSSN": { + "description": "Om du inte kan använda Suomi.fi-identifikationen, identiera dig med din e-post.", + "info": "Efter att du har identifierat dig får du en länk till anmälningsblanketten till din e-post" + } + }, + "payment": { + "cancel": { + "description": "Anmälning är bekräftat först då du har betalat examensavgiften.", + "heading": "Betalningen kunde inte genomföras", + "title": "Du har avbrytit betalningen" + }, + "error": { + "description": "Du kan försöka betala på nytt. Anmälning är bekräftat först då du har betalat examensavgiften.", + "heading": "Betalningsfel", + "title": "Det har uppstått ett fel i betalningen" + }, + "success": { + "whatsNext": { + "beforeYkiTest": { + "description": "Förbereda dig till YKI-testet genom att läsa deltagarens anvisningar på Utbildningsstyrelsens webbsidor:", + "label": "Före YKI-testdagen (oph.fi)", + "url": "https://www.oph.fi/sv/utbildning-och-examina/fore-yki-testdagen" + }, + "part1": "Anmälan lyckades! Du får snart en bekräftelse om din anmälan per e-post.", + "part2": "Din testställe skickar senare per e-post närmare anvisningar om testdagen.", + "part3": "I anvisningarna får du veta bland annat testplatsens exakta läge och klockslaget då testet börjar.", + "specialArrangements": { + "description": "Om du behöver specialarrangemang, skicka en ansökan enligt Utbildningsstyrelsens anvisningar så fort som möjligt.", + "label": "Behöver du specialarrangemang? (oph.fi)", + "url": "https://www.oph.fi/sv/utbildning-och-examina/anmalan-till-yki-test#anchor-behover-du-specialarrangemang" + }, + "title": "Välkommen till YKI-test" + }, + "heading": "Anmälan lyckades!" + } + }, + "register": { + "inProgress": { + "heading": "Anmäl dig till ett test" + }, + "success": { + "heading": "Anmälningsblanketten har skickats" + } + } + }, + "unavailable": { + "full": { + "description": "Det är inte möjligt att anmäla sig just nu. Det finns inga lediga platser i testet.", + "title": "Testet är fullbokat" + }, + "past": { + "description": "Det är inte längre möjligt att anmäla sig. Anmälningstiden har slutat.", + "title": "Anmälningstiden har slutat" + }, + "upcoming": { + "description": "Det är inte ännu möjligt att anmäla sig. Anmälningstiden har inte börjat", + "title": "Anmälningstiden har inte börjat" + } + } + } + }, "pages": { + "evaluationOrderPage": { + "notFound": null, + "title": "Gör en begäran om kontrollbedömning", + "toasts": { + "loadingError": null + } + }, + "evaluationOrderStatusPage": { + "cancel": { + "heading": null, + "info": null + }, + "error": { + "heading": null, + "info": null + }, + "success": { + "heading": null, + "body1": null, + "body2": null + } + }, + "examDetailsPage": { + "toasts": { + "notFound": null + } + }, + "initRegistrationPage": { + "toasts": { + "notFound": null + } + }, + "reassessmentPage": { + "info": { + "general": { + "body1": null, + "body2": null, + "body3": null, + "heading": null + }, + "pricing": { + "body1": null, + "body2": null, + "body3": null, + "body4": null, + "heading": null + }, + "schedule": { + "body1": null, + "body2": null, + "body3": null, + "body4": null, + "heading": null + } + }, + "introduction": { + "fee": null, + "info": null + }, + "title": null + }, "registrationPage": { + "abortDialog": { + "actions": { + "cancel": "Jag vill inte avbryta", + "confirm": "Ja, jag vill avbryta min anmälan" + }, + "description": "Vill du avbryta anmälan? Du förlorar uppgifterna som du har fyllt i.", + "title": "Avbryt anmälan" + }, + "description": { + "part1": { + "link": { + "label": "Att välja ett lämpligt YKI-test och testdagarna", + "url": "https://www.oph.fi/sv/utbildning-och-examina/att-valja-ett-lampligt-yki-test-testdagarna" + }, + "text": "Den allmänna språkexamen (YKI-examen) är en språkexamen som är avsedd för vuxna. Med examensintyg kan du officiellt visa dina språkkunskaper i olika språk. Före du anmäler dig till YKI-testet, ta reda på när och på vilket nivå du behöver ett examensintyg. Läs mer om att välja ett lämpligt YKI-test på Utbildningsstyrelsens webbsidor:" + }, + "part2": { + "link": { + "label": "Behöver du specialarrangemang", + "url": "https://www.oph.fi/sv/utbildning-och-examina/anmalan-till-yki-test#anchor-behover-du-specialarrangemang" + }, + "general": "I YKI-examen bedöms kunskaperna i standardspråket i praktiska situationer på grundnivå, mellannivå eller högsta nivå. Alla YKI-examina har fyra delprov: tal, talförståelse, skrivning och textförståelse. Varje delprov bedöms separat.", + "languages": "Om du behöver specialarrangemang i YKI-test, läs anvisningar på Utbildningsstyrelsens webbsidor:" + } + }, + "filters": { + "buttons": { + "empty": "Radera allt", + "showResults": "Visa resultat ({{count}})" + }, + "errorDialog": { + "description": "För att se resultat, välj först testets språk och färdighetsnivå. Tryck på VISA RESULTAT -knappen.", + "title": "Välj språk och färdighetsnivå" + }, + "errors": { + "required": "Oblikatoriskt att välja" + }, + "heading": "Sök almänna språkexamina (YKI)", + "information": "Sök här för ett lämpligt YKI-test. Välj språk och färdighetsnivå. Tryck på VISA RESULTAT -knappen.", + "selectExamDetails": { + "prompt": "Välj examen", + "required": "(oblikatorisk)" + } + }, "labels": { - "filterExamSessions": "Filtrera examenstillfällen", - "excludeFullSessions": "Visa endast examenstillfällen med ledigt utrymme", - "excludeNonOpenSessions": "Visa endast de examenstillfällen som nu är lediga" + "excludeFullSessions": "Visa endast examenstillfällen med lediga platser", + "excludeNonOpenSessions": "Visa endast examenstillfällen vars anmälan har börjat", + "filterExamSessions": "Filtrera resultat", + "selectLanguage": "Välj språk", + "selectLevel": "Välj färdighetsnivå", + "selectMunicipality": "Välj ort" }, - "title": "Allmänna språkexamina (YKI) - Anmälning", "register": "Anmäl dig", - "searchResults": "Sökresultat" + "title": "Allmänna språkexamina (YKI) - Anmälan" } } } diff --git a/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts b/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts index 8c8b6e78b..e747305da 100644 --- a/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts +++ b/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts @@ -2,7 +2,7 @@ import { selectComboBoxOptionByName } from 'tests/cypress/support/utils/comboBox class PublicRegistrationPage { elements = { - clearAllButton: () => cy.contains('Tyhjennä kaikki'), + clearAllButton: () => cy.contains('Tyhjennä valinnat'), filterByLanguage: () => cy.findByRole('combobox', { name: /Valitse kieli/ }), filterByLevel: () => From 85736b9e57e6fadb2892c31bc3cf9d0b4e384734 Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Fri, 22 Sep 2023 21:57:45 +0300 Subject: [PATCH 15/72] YKI(Frontend): Add missing en/sv privacy policy texts --- frontend/packages/yki/public/i18n/en-GB/common.json | 2 +- frontend/packages/yki/public/i18n/sv-SE/common.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/common.json b/frontend/packages/yki/public/i18n/en-GB/common.json index a697cdaf1..cc42b0d2a 100644 --- a/frontend/packages/yki/public/i18n/en-GB/common.json +++ b/frontend/packages/yki/public/i18n/en-GB/common.json @@ -108,7 +108,7 @@ "grantApproval": "I accept the terms and conditions for processing personal data *", "link": { "label": "Information on the processing of personal data (oph.fi)", - "url": "https://www.oph.fi/en/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-information-on-the-processing-of-personal-data" + "url": "https://www.oph.fi/en/education-and-qualifications/registering-yki-test#anchor-information-on-the-processing-of-personal-data" }, "readConditions": "Read the terms and conditions of processing personal information on the website of the Finnish National Agency for Education:", "title": "Terms and conditions for processing personal data" diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index f02437574..7d4e2a263 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -49,6 +49,16 @@ "level": "Nivå", "ophLogoAlt": "Logo: Opetushallitus/Utbildningsstyrelsen.", "price": "Pris", + "privacyStatement": { + "description": "Vi följer EUs allmänna dataskyddsförordningen i behandling av personuppgifter.", + "grantApproval": "Jag godkänner villkoren för anmälan. *", + "link": { + "label": "Information om behandlingen av personuppgifter (oph.fi)", + "url": "https://www.oph.fi/sv/utbildning-och-examina/anmalan-till-yki-test#anchor-information-om-behandlingen-av-personuppgifter" + }, + "readConditions": "Läs villkoren för behandling av personuppgifter på Utbildningsstyrelsens webbsidor:", + "title": "Villkoren för behandling av personuppgifter" + }, "reassessment": "Kontrollbedömning", "registration": "Anmälning" } From 11f4bcbc42a5245f53306b253968b45e57f73a6c Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Fri, 22 Sep 2023 22:46:51 +0300 Subject: [PATCH 16/72] YKI(Frontend): Change reassessment page texts --- .../yki/public/i18n/en-GB/public.json | 34 ++-- .../yki/public/i18n/fi-FI/public.json | 30 ++-- .../yki/public/i18n/sv-SE/common.json | 9 ++ .../yki/public/i18n/sv-SE/public.json | 44 +++--- .../yki/src/pages/ReassessmentPage.tsx | 148 +++--------------- 5 files changed, 73 insertions(+), 192 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index cc8f0ec1a..67b016018 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -85,7 +85,7 @@ "publicEvaluationPeriodListing": { "evaluationPeriodNotYetOpen": "Request a reassessment starting from {{- startDate}}", "header": { - "evaluationPeriod": "Deadline for the request" + "evaluationPeriod": "Requesting period for the reassessment" }, "heading": "Select the test for whch you wish to request a reassessment", "requestReassessment": "Request a reassessment" @@ -374,31 +374,25 @@ "reassessmentPage": { "info": { "general": { - "body1": "Tarkistusarviointi on Yleisiä kielitutkintoja koskevan lain (964/2004, 13 §) tarjoama mahdollisuus pyytää suorituksesi uudelleen arviointia siinä tapauksessa, että et ole tyytyväinen siihen, miten tekemäsi tutkintosuoritus on arvioitu.", - "body2": "Tarkistusarviointi on ainoa toimenpide, jolla voit ehkä saada muutettua suorituksesi tulosta.", - "body3": "Tarkistusarvioinnin tekevät eri arvioijat kuin ne, jotka ovat arvioineet suorituksesi ensimmäisellä kerralla. Tarkistusarvioinnissa arvioidaan vain suoritusta, eikä muita seikkoja oteta huomioon. Tarkistusarviointia koskevaan päätökseen ei saa hakea muutosta valittamalla.", - "heading": "Tarkistusarvioinnista" + "body1": "In the reassessment, your test answers will be evaluated again. The reassessment will be conducted by different assessors than those who assessed your YKI test the first time.", + "body2": "The reassessment rarely changes the grade of a subtest. See previous statistics on the website of the Finnish National Agency for Education: ", + "body3": "The reassessment only focuses on your YKI test performance, and no other considerations will be taken into account. The decision on a reassessment may not be appealed.", + "heading": "What happens in the reassessment?", + "link": "https://www.oph.fi/en/education-and-qualifications/after-yki-test#anchor-reassessment" }, "pricing": { - "body1": "Tarkistusarviointia voi pyytää joko yhdestä tai useammasta osakokeesta.", - "body2": "Jokaisen osakokeen tarkistusarviointi maksaa 50 euroa:", - "body3": "Maksu suoritetaan, kun tarkistusarviointipyyntö tehdään, ja vain maksetut pyynnöt käsitellään.", - "body4": "Maksu palautetaan, jos osakokeen arviointi muuttuu tarkistusarvioinnissa.", - "heading": "Hinnasto" - }, - "schedule": { - "body1": "Voit pyytää suorituksesi tarkistusarviointia 14 päivän kuluessa siitä ajankohdasta, kun sinulla on ollut tilaisuus saada arvioinnin tulos.", - "body2": "Tarkasta aika, milloin voit pyytää tutkintosuorituksesi tarkistusarviointia, alla olevasta Tutkinnot-taulukosta.", - "body3": "Käsittelyaika on keskimäärin kolme kuukautta.", - "body4": "Päätös lähetetään sähköpostitse.", - "heading": "Aikataulu" + "body1": "You can request a reassessment of one or more subtests. A fee of EUR 50 will be charged for the reassessment of each subtest. The fee must be paid as the reassessment request is submitted. The request will only be processed if the fee has been paid.", + "body2": "You can request a reassessment up to 14 days after you have had the opportunity to receive the assessment result. The list below shows the test days for which you can currently submit a request for reassessment.", + "body3": "The average processing time is approximately three months. The results of the reassessment will be sent to you by e-mail.", + "body4": "The fee will be refunded if the reassessment changes the assessment of the subtest.", + "heading": "How to request and pay for the reassessment?" } }, "introduction": { - "fee": "Tarkistusarviointien käsittelystä on tullut maksullista keväästä 2021 alkaen.", - "info": "Tutkinnon suorittajalla on oikeus pyytää tarkistusarviointia, jos hän ei ole tyytyväinen saamaansa arviointiin. Tarkistusarviointia tulee hakea 14 vuorokauden kuluessa todistuksen saamisesta." + "info": "You can submit a claim for correction if you are not satisfied with the assessment of your YKI test. A claim for correction means a reassessment of your YKI test. You can submit a request for a reassessment on this page.", + "timeLimit": "You can request a reassessment within 14 days after you have had the opportunity to receive your YKI certificate." }, - "title": "Yleiset kielitutkinnot (YKI) - Tarkistusarviointi" + "title": "National Certificates of Language Proficiency (YKI) - Reassessment" }, "registrationPage": { "abortDialog": { diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index ea758bcb9..4bd51c813 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -374,29 +374,23 @@ "reassessmentPage": { "info": { "general": { - "body1": "Tarkistusarviointi on Yleisiä kielitutkintoja koskevan lain (964/2004, 13 §) tarjoama mahdollisuus pyytää suorituksesi uudelleen arviointia siinä tapauksessa, että et ole tyytyväinen siihen, miten tekemäsi tutkintosuoritus on arvioitu.", - "body2": "Tarkistusarviointi on ainoa toimenpide, jolla voit ehkä saada muutettua suorituksesi tulosta.", - "body3": "Tarkistusarvioinnin tekevät eri arvioijat kuin ne, jotka ovat arvioineet suorituksesi ensimmäisellä kerralla. Tarkistusarvioinnissa arvioidaan vain suoritusta, eikä muita seikkoja oteta huomioon. Tarkistusarviointia koskevaan päätökseen ei saa hakea muutosta valittamalla.", - "heading": "Tarkistusarvioinnista" + "body1": "Tarkistusarvioinnissa testivastauksesi arvioidaan uudelleen. Tarkistusarvioinnin tekevät eri arvioijat kuin ne, jotka ovat arvioineet testisi ensimmäisellä kerralla.", + "body2": "Tarkistusarviointi muuttaa osakokeen taitotasoarviota vain harvoin. Katso tarkistusarviointitilastot Opetushallituksen verkkosivuilta: ", + "body3": "Tarkistusarvioinnissa arvioidaan vain testivastauksiasi. Siinä ei oteta huomioon mitään muita asioita, jotka ovat voineet vaikuttaa testin tulokseen. Et voi valittaa tarkistusarvioinnin päätöksestä.", + "heading": "Mitä tarkistusarvioinnissa tehdään?", + "link": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/yki-testin-jalkeen#anchor-tarkistusarviointi" }, "pricing": { - "body1": "Tarkistusarviointia voi pyytää joko yhdestä tai useammasta osakokeesta.", - "body2": "Jokaisen osakokeen tarkistusarviointi maksaa 50 euroa:", - "body3": "Maksu suoritetaan, kun tarkistusarviointipyyntö tehdään, ja vain maksetut pyynnöt käsitellään.", - "body4": "Maksu palautetaan, jos osakokeen arviointi muuttuu tarkistusarvioinnissa.", - "heading": "Hinnasto" - }, - "schedule": { - "body1": "Voit pyytää suorituksesi tarkistusarviointia 14 päivän kuluessa siitä ajankohdasta, kun sinulla on ollut tilaisuus saada arvioinnin tulos.", - "body2": "Tarkasta aika, milloin voit pyytää tutkintosuorituksesi tarkistusarviointia, alla olevasta Tutkinnot-taulukosta.", - "body3": "Käsittelyaika on keskimäärin kolme kuukautta.", - "body4": "Päätös lähetetään sähköpostitse.", - "heading": "Aikataulu" + "body1": "Voit pyytää tarkistusarviointia yhdestä tai useammasta osakokeesta. Yhden osakokeen tarkistusarviointi maksaa 50 euroa. Sinun täytyy maksaa tarkistusarviointimaksu heti kun teet tarkistusarviointipyynnön. Käsittelemme vain maksetut pyynnöt.", + "body2": "Voit pyytää tarkistusarviointia 14 vuorokauden sisällä siitä, kun sinulla on ollut tilaisuus saada YKI-testisi tulos. Tämän sivun lopussa on lista testipäivistä, joista voit pyytää tarkistusarviointia tällä hetkellä.", + "body3": "Tarkistusarviointien käsittelyaika on noin kolme kuukautta. Lähetämme päätöksen tarkistusarvioinnista sähköpostitse.", + "body4": "Jos osakokeen arviointi muuttuu tarkistusarvioinnissa, palautamme tarkistusarviointimaksun.", + "heading": "Tarkistusarvioinnin pyytäminen ja maksaminen" } }, "introduction": { - "fee": "Tarkistusarviointien käsittelystä on tullut maksullista keväästä 2021 alkaen.", - "info": "Tutkinnon suorittajalla on oikeus pyytää tarkistusarviointia, jos hän ei ole tyytyväinen saamaansa arviointiin. Tarkistusarviointia tulee hakea 14 vuorokauden kuluessa todistuksen saamisesta.\nVoit pyytää tarkistusarviointia 14 vuorokauden sisällä siitä, kun sinulla on ollut tilaisuus saada YKI-testisi tulos." + "info": "Voit tehdä oikaisuvaatimuspyynnön, jos et ole tyytyväinen YKI-testisi arviointiin. Oikaisuvaatimuspyyntö tarkoittaa YKI-testisi tarkistusarviointia. Voit jättää tarkistusarviointipyynnön tällä sivulla.", + "timeLimit": "Voit pyytää tarkistusarviointia 14 vuorokauden sisällä siitä, kun sinulla on ollut tilaisuus saada YKI-testisi tulos." }, "title": "Yleiset kielitutkinnot (YKI) - Tarkistusarviointi" }, diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index 7d4e2a263..525c46ba3 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -10,6 +10,15 @@ "dateTimeFormat": "l [kl.] HH:mm" }, "error": "Ett oväntat fel uppstod.", + "examDate": "Testdag", + "examParts": { + "readingComprehension": "Textförståelse", + "speaking": "Tal", + "speechComprehension": "Talförståelse", + "writing": "Skrivning" + }, + "examSession": "Test", + "examination": "Test", "gender": { "female": "Kvinna", "male": "Man", diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 77c0971c3..116ae3c67 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -46,10 +46,10 @@ "info": null }, "selectExaminationParts": { - "heading": null, + "heading": "Välj delprov ", "selectAtLeastOne": null, - "selectParts": "Välj de delprov för vilka du vill att bedömningen kontrolleras", - "sumTotal": null + "selectParts": "Välj de delprov för vilka du vill att bedömningen kontrolleras*", + "sumTotal": "Priset på kontrollbedömningen totalt" } }, "footer": { @@ -83,11 +83,11 @@ } }, "publicEvaluationPeriodListing": { - "evaluationPeriodNotYetOpen": null, + "evaluationPeriodNotYetOpen": "Begär om kontrollbedömning från och med {{- startDate}}", "header": { - "evaluationPeriod": null + "evaluationPeriod": "Möjligt att begära om kontrollbedömning" }, - "heading": null, + "heading": "Välj testet som du vill begära om kontrollbedömning av", "requestReassessment": "Gör en begäran om kontrollbedömning" }, "registration": { @@ -374,31 +374,25 @@ "reassessmentPage": { "info": { "general": { - "body1": null, - "body2": null, - "body3": null, - "heading": null + "body1": "Vid kontrollbedömning bedöms din examensprestation om. Kontrollbedömningen genomförs av andra bedömare än de som genomförde den första bedömningen.", + "body2": "Kontrollbedömningen ändrar sällan vitsordet för delprovet. Läs mer på Utbildningsstyrelsens webbsidor: ", + "body3": "I kontrollbedömningen bedöms endast prestationen, inga andra faktorer tas i beaktande. Beslut som gäller kontrollbedömningen får inte överklagas genom besvär.", + "heading": "Vad är en kontrollbedömning?", + "link": "https://www.oph.fi/sv/utbildning-och-examina/efter-yki-testet#anchor-kontrollbedomning" }, "pricing": { - "body1": null, - "body2": null, - "body3": null, - "body4": null, - "heading": null - }, - "schedule": { - "body1": null, - "body2": null, - "body3": null, - "body4": null, - "heading": null + "body1": "Det är möjligt att begära om kontrollbedömning av ett eller flera delprov. Avgiften för kontrollbedömningen är 50 euro per delprov. Avgiften ska betalas i samband med att begäran om kontrollbedömning görs. Endast betalda begäranden om kontrollbedömning behandlas.", + "body2": "Du kan ansöka om kontrollbedömning inom 14 dagar från den tidpunkt då du har haft möjlighet att få bedömningsresultatet. I tabellen nedan kan du kontrollera när du kan begära om kontrollbedömning av din examensprestation. I slutet av sidan finns en lista på testdagar som du kan begära om kontrollbedömning för tillfället.", + "body3": "Behandlingstiden för ärendet är i genomsnitt tre månader. Beslutet skickas per e-post.", + "body4": "Avgiften återbetalas om bedömningen av delprovet ändrar i samband med kontrollbedömningen.", + "heading": "Att begära om och betala kontrollbedömning" } }, "introduction": { - "fee": null, - "info": null + "info": "Kontrollbedömning ger dig möjligheten att begära om omprövning om du inte är nöjd med bedömningen av din examensprestation. Begäran om omprövning betyder kontrollbedömning av ditt YKI-test. Du kan skicka begäran om kontrollbedömning på den här sidan.", + "timeLimit": "Du kan ansöka om kontrollbedömning inom 14 dagar från den tidpunkt då du har haft möjlighet att få bedömningsresultatet." }, - "title": null + "title": "Allmänna språkexamina (YKI) - Kontrollbedömning" }, "registrationPage": { "abortDialog": { diff --git a/frontend/packages/yki/src/pages/ReassessmentPage.tsx b/frontend/packages/yki/src/pages/ReassessmentPage.tsx index 0452f2535..a65ecd9bb 100644 --- a/frontend/packages/yki/src/pages/ReassessmentPage.tsx +++ b/frontend/packages/yki/src/pages/ReassessmentPage.tsx @@ -1,131 +1,21 @@ -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import { - Accordion, - AccordionDetails, - AccordionSummary, - Grid, - Paper, -} from '@mui/material'; +import { Grid, Paper } from '@mui/material'; import { Box } from '@mui/system'; import { FC, useEffect } from 'react'; import { H1, H2, HeaderSeparator, Text } from 'shared/components'; import { APIResponseStatus } from 'shared/enums'; -import { useWindowProperties } from 'shared/hooks'; import { PublicEvaluationPeriodListing } from 'components/reassessment/PublicEvaluationPeriodListing'; import { PublicEvaluationPeriodListingSkeleton } from 'components/skeletons/PublicEvaluationPeriodListingSkeleton'; -import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { loadEvaluationPeriods } from 'redux/reducers/evaluationPeriods'; import { evaluationPeriodsSelector } from 'redux/selectors/evaluationPeriods'; -const PricingBulletList = () => { - const translateCommon = useCommonTranslation(); - const examParts = [ - 'readingComprehension', - 'speechComprehension', - 'writing', - 'speaking', - ]; - - return ( -
    - - {examParts.map((key, i) => ( -
  • {translateCommon(`examParts.${key}`)} 50 €
  • - ))} -
    -
- ); -}; - -const PricingAccordion = () => { - const { t } = usePublicTranslation({ - keyPrefix: 'yki.pages.reassessmentPage.info.pricing', - }); - - return ( - - }> - {t('heading')} - - - {t('body1')} - {t('body2')} - - {t('body3')} - {t('body4')} - - - ); -}; - -const PricingSection = () => { - const { t } = usePublicTranslation({ - keyPrefix: 'yki.pages.reassessmentPage.info.pricing', - }); - - return ( - <> -

- {t('heading')} -

- {t('body1')} - {t('body2')} - - {t('body3')} - {t('body4')} - - ); -}; - -const ScheduleAccordion = () => { - const { t } = usePublicTranslation({ - keyPrefix: 'yki.pages.reassessmentPage.info.schedule', - }); - - return ( - - }> - {t('heading')} - - - {t('body1')} - {t('body2')} -
- {t('body3')} - {t('body4')} -
-
- ); -}; - -const ScheduleSection = () => { - const { t } = usePublicTranslation({ - keyPrefix: 'yki.pages.reassessmentPage.info.schedule', - }); - - return ( - <> -

- {t('heading')} -

- {t('body1')} - {t('body2')} -
- - {t('body3')} {t('body4')} - - - ); -}; - export const ReassessmentPage: FC = () => { const { t } = usePublicTranslation({ keyPrefix: 'yki.pages.reassessmentPage', }); - const { isPhone } = useWindowProperties(); const dispatch = useAppDispatch(); const { status } = useAppSelector(evaluationPeriodsSelector); @@ -153,7 +43,7 @@ export const ReassessmentPage: FC = () => { {t('introduction.info')}
- {t('introduction.fee')} + {t('introduction.timeLimit')} { {t('info.general.heading')} {t('info.general.body1')} - +
+ {t('info.general.body2')} + {t('info.general.link')} +
{t('info.general.body3')}
- {isPhone ? ( -
- - -
- ) : ( - <> -
- -
-
- -
- - )} +
+

+ {t('info.pricing.heading')} +

+ {t('info.pricing.body1')} +
+ {t('info.pricing.body2')} +
+ {t('info.pricing.body3')} +
+ {t('info.pricing.body4')} +
Date: Wed, 27 Sep 2023 13:33:59 +0300 Subject: [PATCH 17/72] YKI(Frontend): Fix reassessment page link styling --- .../packages/yki/src/pages/ReassessmentPage.tsx | 13 +++++++++++-- .../yki/src/styles/pages/_reassessment-page.scss | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/frontend/packages/yki/src/pages/ReassessmentPage.tsx b/frontend/packages/yki/src/pages/ReassessmentPage.tsx index a65ecd9bb..fc4a3dd75 100644 --- a/frontend/packages/yki/src/pages/ReassessmentPage.tsx +++ b/frontend/packages/yki/src/pages/ReassessmentPage.tsx @@ -1,3 +1,4 @@ +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Grid, Paper } from '@mui/material'; import { Box } from '@mui/system'; import { FC, useEffect } from 'react'; @@ -58,12 +59,20 @@ export const ReassessmentPage: FC = () => {
{t('info.general.body2')} - {t('info.general.link')} + + {t('info.general.link')} + +
{t('info.general.body3')} -
+

{t('info.pricing.heading')}

diff --git a/frontend/packages/yki/src/styles/pages/_reassessment-page.scss b/frontend/packages/yki/src/styles/pages/_reassessment-page.scss index f53ab6cb2..ae0cfc65f 100644 --- a/frontend/packages/yki/src/styles/pages/_reassessment-page.scss +++ b/frontend/packages/yki/src/styles/pages/_reassessment-page.scss @@ -51,4 +51,14 @@ padding-bottom: 2rem; } } + + & a { + color: $color-secondary-dark; + font-weight: normal; + letter-spacing: 0; + line-height: 1.9rem; + margin: 0; + text-decoration: underline; + text-transform: initial; + } } From 800163f98a4474065112b0942c463cfad9538285 Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Thu, 28 Sep 2023 11:20:06 +0300 Subject: [PATCH 18/72] YKI(Frontend): Reassessment page link text, other adjustments --- frontend/packages/yki/public/i18n/en-GB/common.json | 4 ++++ frontend/packages/yki/public/i18n/en-GB/public.json | 5 +++-- frontend/packages/yki/public/i18n/fi-FI/public.json | 3 ++- frontend/packages/yki/public/i18n/sv-SE/common.json | 4 ++++ frontend/packages/yki/public/i18n/sv-SE/public.json | 7 ++++--- frontend/packages/yki/src/pages/ReassessmentPage.tsx | 2 +- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/common.json b/frontend/packages/yki/public/i18n/en-GB/common.json index cc42b0d2a..8b18c3cad 100644 --- a/frontend/packages/yki/public/i18n/en-GB/common.json +++ b/frontend/packages/yki/public/i18n/en-GB/common.json @@ -97,6 +97,10 @@ }, "level": "Level", "municipality": "Municipality", + "navigationProtection": { + "description": "You will lose any changes you have made.", + "header": "Are you sure you want to leave this page?" + }, "next": "Next", "no": "No", "ophLogoAlt": "Logo: Finnish National Agency for Education.", diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index 67b016018..8485d6572 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -72,7 +72,7 @@ "text": "Accessibility statement (in Finnish)" }, "contact": { - "title": "" + "title": "Feedback and development suggestions" }, "privacy": { "text": "Privacy policy statement" @@ -378,7 +378,8 @@ "body2": "The reassessment rarely changes the grade of a subtest. See previous statistics on the website of the Finnish National Agency for Education: ", "body3": "The reassessment only focuses on your YKI test performance, and no other considerations will be taken into account. The decision on a reassessment may not be appealed.", "heading": "What happens in the reassessment?", - "link": "https://www.oph.fi/en/education-and-qualifications/after-yki-test#anchor-reassessment" + "link": "https://www.oph.fi/en/education-and-qualifications/after-yki-test#anchor-reassessment", + "linkText": "After the YKI test: Reassessment (oph.fi)" }, "pricing": { "body1": "You can request a reassessment of one or more subtests. A fee of EUR 50 will be charged for the reassessment of each subtest. The fee must be paid as the reassessment request is submitted. The request will only be processed if the fee has been paid.", diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 4bd51c813..dc701928e 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -378,7 +378,8 @@ "body2": "Tarkistusarviointi muuttaa osakokeen taitotasoarviota vain harvoin. Katso tarkistusarviointitilastot Opetushallituksen verkkosivuilta: ", "body3": "Tarkistusarvioinnissa arvioidaan vain testivastauksiasi. Siinä ei oteta huomioon mitään muita asioita, jotka ovat voineet vaikuttaa testin tulokseen. Et voi valittaa tarkistusarvioinnin päätöksestä.", "heading": "Mitä tarkistusarvioinnissa tehdään?", - "link": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/yki-testin-jalkeen#anchor-tarkistusarviointi" + "link": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/yki-testin-jalkeen#anchor-tarkistusarviointi", + "linkText": "YKI-testin jälkeen: Tarkistusarviointi (oph.fi)" }, "pricing": { "body1": "Voit pyytää tarkistusarviointia yhdestä tai useammasta osakokeesta. Yhden osakokeen tarkistusarviointi maksaa 50 euroa. Sinun täytyy maksaa tarkistusarviointimaksu heti kun teet tarkistusarviointipyynnön. Käsittelemme vain maksetut pyynnöt.", diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index 525c46ba3..bbd2f684c 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -56,6 +56,10 @@ "swe": "svenska" }, "level": "Nivå", + "navigationProtection": { + "description": "Du förlorar ändringar du gjort.", + "header": "Är du säker på att du vill lämna den här sidan?" + }, "ophLogoAlt": "Logo: Opetushallitus/Utbildningsstyrelsen.", "price": "Pris", "privacyStatement": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 116ae3c67..3c6ad79fb 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -69,10 +69,10 @@ }, "links": { "accessibility": { - "text": "Tillgänglighetsdirektiv" + "text": "Tillgänglighetsdirektiv (på finska)" }, "contact": { - "title": "" + "title": "Respons och utvecklingsidéer" }, "privacy": { "text": "Information om behandlingen av personuppgifter" @@ -378,7 +378,8 @@ "body2": "Kontrollbedömningen ändrar sällan vitsordet för delprovet. Läs mer på Utbildningsstyrelsens webbsidor: ", "body3": "I kontrollbedömningen bedöms endast prestationen, inga andra faktorer tas i beaktande. Beslut som gäller kontrollbedömningen får inte överklagas genom besvär.", "heading": "Vad är en kontrollbedömning?", - "link": "https://www.oph.fi/sv/utbildning-och-examina/efter-yki-testet#anchor-kontrollbedomning" + "link": "https://www.oph.fi/sv/utbildning-och-examina/efter-yki-testet#anchor-kontrollbedomning", + "linkText": "Efter YKI-testet: Kontrollbedömning (oph.fi)" }, "pricing": { "body1": "Det är möjligt att begära om kontrollbedömning av ett eller flera delprov. Avgiften för kontrollbedömningen är 50 euro per delprov. Avgiften ska betalas i samband med att begäran om kontrollbedömning görs. Endast betalda begäranden om kontrollbedömning behandlas.", diff --git a/frontend/packages/yki/src/pages/ReassessmentPage.tsx b/frontend/packages/yki/src/pages/ReassessmentPage.tsx index fc4a3dd75..ca2b083c8 100644 --- a/frontend/packages/yki/src/pages/ReassessmentPage.tsx +++ b/frontend/packages/yki/src/pages/ReassessmentPage.tsx @@ -65,7 +65,7 @@ export const ReassessmentPage: FC = () => { rel="noreferrer" target="_black" > - {t('info.general.link')} + {t('info.general.linkText')} From 1be23431027e6686ea30fd04ee87b1f6d84f10cd Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 19 Sep 2023 21:33:19 +0300 Subject: [PATCH 19/72] YKI(Frontend): Aborting registration should also cancel the registration --- frontend/packages/yki/setupProxy.js | 12 ++++++++++ frontend/packages/yki/src/enums/api.ts | 1 + .../yki/src/hooks/useNavigationProtection.ts | 10 ++++++-- .../yki/src/redux/reducers/registration.ts | 18 ++++++++++++++ .../yki/src/redux/sagas/registration.ts | 24 +++++++++++++++++++ 5 files changed, 63 insertions(+), 2 deletions(-) diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index 3ae1609b9..b1e05b2e9 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -1356,4 +1356,16 @@ module.exports = function (app) { }; useLocalProxy ? proxyGetCall(req, res) : mockCall(); }); + + app.post('/yki/api/registration/:id/cancel', (req, res) => { + const mockCall = () => { + try { + res.send({ success: true }); + } catch (err) { + printError(req, err); + res.status(404).send(err.message); + } + }; + useLocalProxy ? proxyPostCall(req, res) : mockCall(); + }); }; diff --git a/frontend/packages/yki/src/enums/api.ts b/frontend/packages/yki/src/enums/api.ts index 81ad65fde..d576488fd 100644 --- a/frontend/packages/yki/src/enums/api.ts +++ b/frontend/packages/yki/src/enums/api.ts @@ -1,4 +1,5 @@ export enum APIEndpoints { + CancelRegistration = '/yki/api/registration/:registrationId/cancel', CountryCodes = '/yki/api/code/maatjavaltiot2', ExamSessions = '/yki/api/exam-session', ExamSession = '/yki/api/exam-session/:examSessionId', diff --git a/frontend/packages/yki/src/hooks/useNavigationProtection.ts b/frontend/packages/yki/src/hooks/useNavigationProtection.ts index 9aafe244c..8ba06dbb5 100644 --- a/frontend/packages/yki/src/hooks/useNavigationProtection.ts +++ b/frontend/packages/yki/src/hooks/useNavigationProtection.ts @@ -6,12 +6,15 @@ import { } from 'shared/hooks'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { useAppDispatch } from 'configs/redux'; +import { cancelRegistration } from 'redux/reducers/registration'; export const useRegistrationNavigationProtection = (when: boolean) => { const { t } = usePublicTranslation({ keyPrefix: 'yki.pages.registrationPage.abortDialog', }); const { showDialog } = useDialog(); + const dispatch = useAppDispatch(); const showConfirmationDialog = useCallback( (confirmNavigation: () => void, cancelNavigation: () => void) => { @@ -23,7 +26,10 @@ export const useRegistrationNavigationProtection = (when: boolean) => { { title: t('actions.confirm'), variant: Variant.Outlined, - action: confirmNavigation, + action: () => { + dispatch(cancelRegistration()); + confirmNavigation(); + }, }, { title: t('actions.cancel'), @@ -34,7 +40,7 @@ export const useRegistrationNavigationProtection = (when: boolean) => { onClose: cancelNavigation, }); }, - [showDialog, t] + [dispatch, showDialog, t] ); useCommonNavigationProtection(when, showConfirmationDialog); diff --git a/frontend/packages/yki/src/redux/reducers/registration.ts b/frontend/packages/yki/src/redux/reducers/registration.ts index b95bef9fa..47964331b 100644 --- a/frontend/packages/yki/src/redux/reducers/registration.ts +++ b/frontend/packages/yki/src/redux/reducers/registration.ts @@ -23,6 +23,9 @@ export interface RegistrationState { status: APIResponseStatus; error?: PublicRegistrationFormSubmitError; }; + cancelRegistration: { + status: APIResponseStatus; + }; isEmailRegistration?: boolean; hasSuomiFiNationalityData: boolean; registration: Partial; @@ -35,6 +38,9 @@ const initialState: RegistrationState = { initRegistration: { status: APIResponseStatus.NotStarted, }, + cancelRegistration: { + status: APIResponseStatus.NotStarted, + }, hasSuomiFiNationalityData: false, submitRegistration: { status: APIResponseStatus.NotStarted }, registration: { @@ -146,6 +152,15 @@ const registrationSlice = createSlice({ setActiveStep(state, action: PayloadAction) { state.activeStep = action.payload; }, + cancelRegistration(state) { + state.cancelRegistration.status = APIResponseStatus.InProgress; + }, + acceptCancelRegistration(state) { + state.cancelRegistration.status = APIResponseStatus.Success; + }, + rejectCancelRegistration(state) { + state.cancelRegistration.status = APIResponseStatus.Error; + }, }, }); @@ -162,4 +177,7 @@ export const { setShowErrors, submitPublicRegistration, updatePublicRegistration, + cancelRegistration, + acceptCancelRegistration, + rejectCancelRegistration, } = registrationSlice.actions; diff --git a/frontend/packages/yki/src/redux/sagas/registration.ts b/frontend/packages/yki/src/redux/sagas/registration.ts index 680aae654..3d4715350 100644 --- a/frontend/packages/yki/src/redux/sagas/registration.ts +++ b/frontend/packages/yki/src/redux/sagas/registration.ts @@ -12,12 +12,16 @@ import { } from 'interfaces/publicRegistration'; import { storeExamSession } from 'redux/reducers/examSession'; import { + acceptCancelRegistration, acceptPublicRegistrationInit, acceptPublicRegistrationSubmission, + cancelRegistration, initRegistration, RegistrationState, + rejectCancelRegistration, rejectPublicRegistrationInit, rejectPublicRegistrationSubmission, + resetPublicRegistration, submitPublicRegistration, } from 'redux/reducers/registration'; import { nationalitiesSelector } from 'redux/selectors/nationalities'; @@ -94,7 +98,27 @@ function* submitRegistrationFormSaga() { } } +function* cancelRegistrationSaga() { + try { + const { registration }: RegistrationState = yield select( + registrationSelector + ); + yield call( + axiosInstance.post, + APIEndpoints.CancelRegistration.replace( + /:registrationId/, + `${registration.id}` + ) + ); + yield put(acceptCancelRegistration()); + yield put(resetPublicRegistration()); + } catch (error) { + yield put(rejectCancelRegistration()); + } +} + export function* watchRegistration() { yield takeLatest(initRegistration.type, initRegistrationSaga); yield takeLatest(submitPublicRegistration.type, submitRegistrationFormSaga); + yield takeLatest(cancelRegistration.type, cancelRegistrationSaga); } From 14d4866fbeee6c8fa01efaf7a16c1418281cc97d Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 21 Sep 2023 17:13:09 +0300 Subject: [PATCH 20/72] YKI(Frontend): Align with backend changes --- frontend/packages/yki/setupProxy.js | 2 +- frontend/packages/yki/src/enums/api.ts | 2 +- frontend/packages/yki/src/redux/sagas/registration.ts | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index b1e05b2e9..7a898cbe3 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -1357,7 +1357,7 @@ module.exports = function (app) { useLocalProxy ? proxyGetCall(req, res) : mockCall(); }); - app.post('/yki/api/registration/:id/cancel', (req, res) => { + app.delete('/yki/api/registration/:id', (req, res) => { const mockCall = () => { try { res.send({ success: true }); diff --git a/frontend/packages/yki/src/enums/api.ts b/frontend/packages/yki/src/enums/api.ts index d576488fd..9e948076e 100644 --- a/frontend/packages/yki/src/enums/api.ts +++ b/frontend/packages/yki/src/enums/api.ts @@ -1,5 +1,4 @@ export enum APIEndpoints { - CancelRegistration = '/yki/api/registration/:registrationId/cancel', CountryCodes = '/yki/api/code/maatjavaltiot2', ExamSessions = '/yki/api/exam-session', ExamSession = '/yki/api/exam-session/:examSessionId', @@ -11,6 +10,7 @@ export enum APIEndpoints { InitRegistration = '/yki/api/registration/init', LoginLink = '/yki/api/login-link', Logout = '/yki/auth/logout', + Registration = '/yki/api/registration/:registrationId', SubmitRegistration = '/yki/api/registration/:registrationId/submit', SuomiFiAuthRedirect = '/yki/auth/', User = '/yki/auth/user', diff --git a/frontend/packages/yki/src/redux/sagas/registration.ts b/frontend/packages/yki/src/redux/sagas/registration.ts index 3d4715350..ed2a0a3ba 100644 --- a/frontend/packages/yki/src/redux/sagas/registration.ts +++ b/frontend/packages/yki/src/redux/sagas/registration.ts @@ -104,11 +104,8 @@ function* cancelRegistrationSaga() { registrationSelector ); yield call( - axiosInstance.post, - APIEndpoints.CancelRegistration.replace( - /:registrationId/, - `${registration.id}` - ) + axiosInstance.delete, + APIEndpoints.Registration.replace(/:registrationId/, `${registration.id}`) ); yield put(acceptCancelRegistration()); yield put(resetPublicRegistration()); From 9289553b8ce0cc4ba0b9cd1ec42e6a347826c34b Mon Sep 17 00:00:00 2001 From: Laura Ketola Date: Thu, 28 Sep 2023 11:00:22 +0300 Subject: [PATCH 21/72] YKI(Frontend): Add session state header and logout support --- .../yki/public/i18n/en-GB/common.json | 3 + .../yki/public/i18n/fi-FI/common.json | 3 + .../yki/public/i18n/fi-FI/public.json | 4 ++ .../yki/public/i18n/sv-SE/common.json | 3 + .../yki/src/components/layouts/Header.tsx | 2 + .../components/layouts/SessionStateHeader.tsx | 60 +++++++++++++++++++ frontend/packages/yki/src/enums/app.ts | 1 + .../packages/yki/src/pages/LogoutSuccess.tsx | 53 ++++++++++++++++ .../packages/yki/src/routers/AppRouter.tsx | 5 ++ .../styles/components/layouts/_header.scss | 4 +- .../components/layouts/_session-header.scss | 18 ++++++ .../styles/pages/_logout-success-page.scss | 36 +++++++++++ frontend/packages/yki/src/styles/styles.scss | 2 + 13 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 frontend/packages/yki/src/components/layouts/SessionStateHeader.tsx create mode 100644 frontend/packages/yki/src/pages/LogoutSuccess.tsx create mode 100644 frontend/packages/yki/src/styles/components/layouts/_session-header.scss create mode 100644 frontend/packages/yki/src/styles/pages/_logout-success-page.scss diff --git a/frontend/packages/yki/public/i18n/en-GB/common.json b/frontend/packages/yki/public/i18n/en-GB/common.json index 8b18c3cad..86dde4e26 100644 --- a/frontend/packages/yki/public/i18n/en-GB/common.json +++ b/frontend/packages/yki/public/i18n/en-GB/common.json @@ -69,6 +69,9 @@ "continueToMain": "Skip to content", "langSelectorAriaLabel": "Kieli / Språk / Language" }, + "sessionState": { + "logOut": "Log out" + }, "lang": { "en": "In English", "fi": "Suomeksi", diff --git a/frontend/packages/yki/public/i18n/fi-FI/common.json b/frontend/packages/yki/public/i18n/fi-FI/common.json index 5b44be29a..a13c1fbba 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/common.json +++ b/frontend/packages/yki/public/i18n/fi-FI/common.json @@ -69,6 +69,9 @@ "continueToMain": "Jatka sisältöön", "langSelectorAriaLabel": "Kieli / Språk / Language" }, + "sessionState": { + "logOut": "Kirjaudu ulos" + }, "lang": { "en": "In English", "fi": "Suomeksi", diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index dc701928e..aa6e25bc1 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -371,6 +371,10 @@ "notFound": "Kielitutkintoa ei löytynyt" } }, + "logoutSuccessPage": { + "heading": "Uloskirjautuminen onnnistui", + "info": "Olet kirjautunut ulos. Suljethan vielä kaikki selainikkunat." + }, "reassessmentPage": { "info": { "general": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index bbd2f684c..fd6d3bf91 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -30,6 +30,9 @@ "continueToMain": "Fortsätt till innehållet", "langSelectorAriaLabel": "Kieli / Språk / Language" }, + "sessionState": { + "logOut": "Logga ut" + }, "lang": { "en": "In English", "fi": "Suomeksi", diff --git a/frontend/packages/yki/src/components/layouts/Header.tsx b/frontend/packages/yki/src/components/layouts/Header.tsx index 272f2e852..cd38eca32 100644 --- a/frontend/packages/yki/src/components/layouts/Header.tsx +++ b/frontend/packages/yki/src/components/layouts/Header.tsx @@ -5,6 +5,7 @@ import { AppLanguage, Direction } from 'shared/enums'; import { useWindowProperties } from 'shared/hooks'; import { PublicNavTabs } from 'components/layouts/publicHeader/PublicNavTabs'; +import { SessionStateHeader } from 'components/layouts/SessionStateHeader'; import { changeLang, getCurrentLang, @@ -45,6 +46,7 @@ export const Header = (): JSX.Element => { /> )} +
diff --git a/frontend/packages/yki/src/components/layouts/SessionStateHeader.tsx b/frontend/packages/yki/src/components/layouts/SessionStateHeader.tsx new file mode 100644 index 000000000..34919efb3 --- /dev/null +++ b/frontend/packages/yki/src/components/layouts/SessionStateHeader.tsx @@ -0,0 +1,60 @@ +import { + LogoutOutlined as LogoutIcon, + Person as PersonIcon, +} from '@mui/icons-material'; +import Button from '@mui/material/Button'; +import { FC } from 'react'; +import { Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; + +import { useCommonTranslation } from 'configs/i18n'; +import { useAppSelector } from 'configs/redux'; +import { APIEndpoints } from 'enums/api'; +import { AppRoutes } from 'enums/app'; +import { + CasAuthenticatedClerkSession, + EmailAuthenticatedSession, + SuomiFiAuthenticatedSession, +} from 'interfaces/session'; +import { sessionSelector } from 'redux/selectors/session'; + +const getUserName = ( + user: + | EmailAuthenticatedSession + | SuomiFiAuthenticatedSession + | CasAuthenticatedClerkSession +): string => { + switch (user['auth-method']) { + case 'SUOMIFI': + return `${user.identity.first_name} ${user.identity.last_name}`; + case 'EMAIL': + return user.identity['external-user-id']; + case 'CAS': + return user.identity.username; + } +}; + +const generateLogoutURL = () => { + return `${APIEndpoints.Logout}?redirect=${window.location.origin}${AppRoutes.LogoutSuccess}`; +}; + +export const SessionStateHeader: FC = () => { + const translateCommon = useCommonTranslation(); + const session = useAppSelector(sessionSelector).loggedInSession; + if (!session || session.identity === null) return <>; + + return ( + + ); +}; diff --git a/frontend/packages/yki/src/enums/app.ts b/frontend/packages/yki/src/enums/app.ts index 9dfb385f1..8a690fc70 100644 --- a/frontend/packages/yki/src/enums/app.ts +++ b/frontend/packages/yki/src/enums/app.ts @@ -11,6 +11,7 @@ export enum AppRoutes { ReassessmentOrder = '/yki/tarkistusarviointi/:evaluationId', ReassessmentOrderStatus = '/yki/tarkistusarviointi/maksu/tila', ExamSession = '/yki/tutkintotilaisuus/:examSessionId', + LogoutSuccess = '/yki/uloskirjautuminen-onnistui', NotFoundPage = '*', } diff --git a/frontend/packages/yki/src/pages/LogoutSuccess.tsx b/frontend/packages/yki/src/pages/LogoutSuccess.tsx new file mode 100644 index 000000000..18c6beb0e --- /dev/null +++ b/frontend/packages/yki/src/pages/LogoutSuccess.tsx @@ -0,0 +1,53 @@ +import { ArrowBackIosOutlined as ArrowBackIosOutlinedIcon } from '@mui/icons-material'; +import { Grid, Paper } from '@mui/material'; +import { CustomButtonLink, H1, HeaderSeparator, Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; + +import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { AppRoutes } from 'enums/app'; + +export const LogoutSuccess: React.FC = () => { + const translateCommon = useCommonTranslation(); + const { t } = usePublicTranslation({ + keyPrefix: 'yki.pages.logoutSuccessPage', + }); + + return ( + + + } + className="color-secondary-dark" + > + {translateCommon('backToHomePage')} + + + +

{t('heading')}

+ +
+ + + {t('info')} + + {translateCommon('backToHomePage')} + + + +
+ ); +}; diff --git a/frontend/packages/yki/src/routers/AppRouter.tsx b/frontend/packages/yki/src/routers/AppRouter.tsx index 9deeef210..755c37d65 100644 --- a/frontend/packages/yki/src/routers/AppRouter.tsx +++ b/frontend/packages/yki/src/routers/AppRouter.tsx @@ -14,6 +14,7 @@ import { EvaluationOrderPage } from 'pages/EvaluationOrderPage'; import { EvaluationOrderStatusPage } from 'pages/EvaluationOrderStatusPage'; import { ExamDetailsPage } from 'pages/ExamDetailsPage'; import { InitRegistrationPage } from 'pages/InitRegistrationPage'; +import { LogoutSuccess } from 'pages/LogoutSuccess'; import { ReassessmentPage } from 'pages/ReassessmentPage'; import { RegistrationPage } from 'pages/RegistrationPage'; import { RegistrationPaymentStatusPage } from 'pages/RegistrationPaymentStatusPage'; @@ -78,6 +79,10 @@ export const AppRouter: FC = () => { path={AppRoutes.AccessibilityStatementPage} element={} /> + } + />
diff --git a/frontend/packages/yki/src/styles/components/layouts/_header.scss b/frontend/packages/yki/src/styles/components/layouts/_header.scss index 2217d4221..f04c64e04 100644 --- a/frontend/packages/yki/src/styles/components/layouts/_header.scss +++ b/frontend/packages/yki/src/styles/components/layouts/_header.scss @@ -9,7 +9,9 @@ } } - & button { + &__left button, + &__center button, + &__right button { @include header-text; } diff --git a/frontend/packages/yki/src/styles/components/layouts/_session-header.scss b/frontend/packages/yki/src/styles/components/layouts/_session-header.scss new file mode 100644 index 000000000..2209bebed --- /dev/null +++ b/frontend/packages/yki/src/styles/components/layouts/_session-header.scss @@ -0,0 +1,18 @@ +.session-header { + background: #f0f3f7; + justify-content: flex-end; + padding: 2rem 6rem 2rem 2rem; + + @include phone { + align-items: flex-end; + flex-direction: column; + gap: 0; + justify-content: center; + padding: 1rem 2rem 1rem 1rem; + } + + & &__user-icon { + height: 2rem; + width: 2rem; + } +} diff --git a/frontend/packages/yki/src/styles/pages/_logout-success-page.scss b/frontend/packages/yki/src/styles/pages/_logout-success-page.scss new file mode 100644 index 000000000..8d56e3270 --- /dev/null +++ b/frontend/packages/yki/src/styles/pages/_logout-success-page.scss @@ -0,0 +1,36 @@ +.logout-success-page { + display: block; + flex: 1; + + &__heading { + @include phone { + padding-left: 2rem; + padding-top: 3rem; + } + } + + & &__back-button { + @include phone { + margin-top: 2rem; + padding: 2rem 2rem 0; + } + } + + & &__heading { + @include phone { + padding: 2rem 2rem 0; + } + } + + & &__content { + padding: 4rem 3rem; + + @include phone { + @include phone-divider-border-top; + @include phone-divider-border-bottom; + gap: 1.5rem; + margin-bottom: 4rem; + padding: 1rem 2rem; + } + } +} diff --git a/frontend/packages/yki/src/styles/styles.scss b/frontend/packages/yki/src/styles/styles.scss index f352a40c2..a6420c510 100644 --- a/frontend/packages/yki/src/styles/styles.scss +++ b/frontend/packages/yki/src/styles/styles.scss @@ -7,6 +7,7 @@ // Components @import 'components/layouts/header'; @import 'components/layouts/footer'; +@import 'components/layouts/session-header'; @import 'components/registration/exam-session-filters'; @import 'components/registration/public-registration'; @@ -14,6 +15,7 @@ @import 'pages/accessibility-statement-page'; @import 'pages/evaluation-order-page'; @import 'pages/evaluation-order-status-page'; +@import 'pages/logout-success-page'; @import 'pages/public-exam-details-page'; @import 'pages/reassessment-page'; @import 'pages/registration-page'; From ddf66e5783b2899543c56df52478f4f5cbb6b14e Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 22 Sep 2023 20:59:35 +0300 Subject: [PATCH 22/72] YKI(Frontend): Add 404-page --- .../yki/public/i18n/fi-FI/public.json | 4 +++ .../packages/yki/src/pages/NotFoundPage.tsx | 36 +++++++++++++++++++ .../packages/yki/src/routers/AppRouter.tsx | 2 ++ .../yki/src/styles/pages/_not-found-page.scss | 21 +++++++++++ frontend/packages/yki/src/styles/styles.scss | 1 + 5 files changed, 64 insertions(+) create mode 100644 frontend/packages/yki/src/pages/NotFoundPage.tsx create mode 100644 frontend/packages/yki/src/styles/pages/_not-found-page.scss diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index aa6e25bc1..3f0b8964a 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -375,6 +375,10 @@ "heading": "Uloskirjautuminen onnnistui", "info": "Olet kirjautunut ulos. Suljethan vielä kaikki selainikkunat." }, + "notFoundPage": { + "description": "Tarkista hakemasi osoite tai palaa aloitussivulle.", + "title": "Sivua ei löytynyt" + }, "reassessmentPage": { "info": { "general": { diff --git a/frontend/packages/yki/src/pages/NotFoundPage.tsx b/frontend/packages/yki/src/pages/NotFoundPage.tsx new file mode 100644 index 000000000..2fa2c5af9 --- /dev/null +++ b/frontend/packages/yki/src/pages/NotFoundPage.tsx @@ -0,0 +1,36 @@ +import { Paper } from '@mui/material'; +import { FC } from 'react'; +import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; +import { useWindowProperties } from 'shared/hooks'; + +import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { AppRoutes } from 'enums/app'; + +export const NotFoundPage: FC = () => { + const { t } = usePublicTranslation({ + keyPrefix: 'yki.pages.notFoundPage', + }); + const translateCommon = useCommonTranslation(); + const { isPhone } = useWindowProperties(); + + return ( +
+
+

{t('title')}

+ +
+ + {t('description')} + + {translateCommon('backToHomePage')} + + +
+ ); +}; diff --git a/frontend/packages/yki/src/routers/AppRouter.tsx b/frontend/packages/yki/src/routers/AppRouter.tsx index 755c37d65..f97e3f537 100644 --- a/frontend/packages/yki/src/routers/AppRouter.tsx +++ b/frontend/packages/yki/src/routers/AppRouter.tsx @@ -15,6 +15,7 @@ import { EvaluationOrderStatusPage } from 'pages/EvaluationOrderStatusPage'; import { ExamDetailsPage } from 'pages/ExamDetailsPage'; import { InitRegistrationPage } from 'pages/InitRegistrationPage'; import { LogoutSuccess } from 'pages/LogoutSuccess'; +import { NotFoundPage } from 'pages/NotFoundPage'; import { ReassessmentPage } from 'pages/ReassessmentPage'; import { RegistrationPage } from 'pages/RegistrationPage'; import { RegistrationPaymentStatusPage } from 'pages/RegistrationPaymentStatusPage'; @@ -83,6 +84,7 @@ export const AppRouter: FC = () => { path={AppRoutes.LogoutSuccess} element={} /> + } />
diff --git a/frontend/packages/yki/src/styles/pages/_not-found-page.scss b/frontend/packages/yki/src/styles/pages/_not-found-page.scss new file mode 100644 index 000000000..3615a83b3 --- /dev/null +++ b/frontend/packages/yki/src/styles/pages/_not-found-page.scss @@ -0,0 +1,21 @@ +.not-found-page { + flex: 1; + + &__btn { + max-width: fit-content; + } + + & &__info { + display: flex; + flex-direction: column; + gap: 2rem; + padding: 2.5rem; + + @include phone { + @include phone-divider-border-top; + @include phone-divider-border-bottom; + gap: 1.5rem; + padding: 1rem 2rem; + } + } +} diff --git a/frontend/packages/yki/src/styles/styles.scss b/frontend/packages/yki/src/styles/styles.scss index a6420c510..5029ee607 100644 --- a/frontend/packages/yki/src/styles/styles.scss +++ b/frontend/packages/yki/src/styles/styles.scss @@ -16,6 +16,7 @@ @import 'pages/evaluation-order-page'; @import 'pages/evaluation-order-status-page'; @import 'pages/logout-success-page'; +@import 'pages/not-found-page'; @import 'pages/public-exam-details-page'; @import 'pages/reassessment-page'; @import 'pages/registration-page'; From c4a49889e9462fe94bcf83662cbac80b217f4193 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 22 Sep 2023 21:00:50 +0300 Subject: [PATCH 23/72] YKI(Frontend): Dev proxy should simulate backend response for exam session that is not found --- frontend/packages/yki/setupProxy.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index 7a898cbe3..8499b83d2 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -1140,7 +1140,11 @@ module.exports = function (app) { (e) => e.id === Number(req.params.id) ); res.set('Content-Type', 'application/json; charset=utf-8'); - res.send(session); + if (session) { + res.send(session); + } else { + res.status(404).send("Exam session not found"); + } } catch (err) { printError(req, err); res.status(404).send(err.message); From 56281110b1c0764911e6518eb6950cbedb871b4b Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 22 Sep 2023 21:44:22 +0300 Subject: [PATCH 24/72] SHARED(Frontend): Introduce new shared class .fit-content-max-width --- frontend/packages/shared/CHANGELOG.MD | 6 ++++++ frontend/packages/shared/package.json | 2 +- .../shared/src/styles/abstracts/common/_spacing.scss | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/packages/shared/CHANGELOG.MD b/frontend/packages/shared/CHANGELOG.MD index 129ff3867..36aa24e1a 100644 --- a/frontend/packages/shared/CHANGELOG.MD +++ b/frontend/packages/shared/CHANGELOG.MD @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Released] +## [1.9.25] - 2023-09-22 + +### Added + +- New class `.fit-content-max-width` + ## [1.9.24] - 2023-09-13 ### Changed diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index 807fd41ee..853dca4ac 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@opetushallitus/kieli-ja-kaantajatutkinnot.shared", - "version": "1.9.24", + "version": "1.9.25", "description": "Shared Frontend Package", "exports": { "./components": "./src/components/index.tsx", diff --git a/frontend/packages/shared/src/styles/abstracts/common/_spacing.scss b/frontend/packages/shared/src/styles/abstracts/common/_spacing.scss index d3756a91e..ee9bf594e 100644 --- a/frontend/packages/shared/src/styles/abstracts/common/_spacing.scss +++ b/frontend/packages/shared/src/styles/abstracts/common/_spacing.scss @@ -9,6 +9,10 @@ max-width: 50%; } +.fit-content-max-width { + max-width: fit-content; +} + /* Heights */ .full-height { &#{&} { From 9a12415b944c56f7560373f7435f8d91c78cb86c Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 22 Sep 2023 21:45:58 +0300 Subject: [PATCH 25/72] YKI(Frontend): Use .fit-content-max-width class from shared package --- frontend/packages/yki/package.json | 2 +- frontend/packages/yki/src/pages/NotFoundPage.tsx | 2 +- .../yki/src/styles/pages/_not-found-page.scss | 4 ---- frontend/yarn.lock | 11 +++++++++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/packages/yki/package.json b/frontend/packages/yki/package.json index 9c0e58e7a..b984b9114 100644 --- a/frontend/packages/yki/package.json +++ b/frontend/packages/yki/package.json @@ -26,7 +26,7 @@ "yki:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.25" }, "devDependencies": { "multer": "^1.4.5-lts.1" diff --git a/frontend/packages/yki/src/pages/NotFoundPage.tsx b/frontend/packages/yki/src/pages/NotFoundPage.tsx index 2fa2c5af9..1d864c64b 100644 --- a/frontend/packages/yki/src/pages/NotFoundPage.tsx +++ b/frontend/packages/yki/src/pages/NotFoundPage.tsx @@ -23,7 +23,7 @@ export const NotFoundPage: FC = () => { {t('description')} Date: Fri, 22 Sep 2023 22:00:15 +0300 Subject: [PATCH 26/72] YKI(Frontend): Align components related to unavailable registration more closely with design --- .../yki/public/i18n/fi-FI/common.json | 2 +- .../yki/public/i18n/fi-FI/public.json | 6 +-- .../yki/src/pages/InitRegistrationPage.tsx | 53 +++++++++++-------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/frontend/packages/yki/public/i18n/fi-FI/common.json b/frontend/packages/yki/public/i18n/fi-FI/common.json index a13c1fbba..37cdaf0d8 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/common.json +++ b/frontend/packages/yki/public/i18n/fi-FI/common.json @@ -5,7 +5,7 @@ "appNameAbbreviation": "YKI", "appTitle": "Yleiset kielitutkinnot | Opetushallitus", "back": "Takaisin", - "backToHomePage": "Takaisin etusivulle", + "backToHomePage": "Palaa aloitussivulle", "cancel": "Peruuta", "component": { "table": { diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 3f0b8964a..a1ea52ba5 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -325,11 +325,11 @@ "unavailable": { "full": { "description": "Ilmoittautuminen ei ole mahdollista juuri nyt. Tutkinnossa ei ole vapaita paikkoja.", - "title": "Tutkinto on täynnä" + "title": "YKI-testi on täynnä" }, "past": { - "description": "Ilmoittautuminen ei ole enää mahdollista. Ilmoittautumisaika on päättynyt.", - "title": "Ilmoittautumisaika päättynyt" + "description": "Tilaisuuteen ilmoittautuminen ei ole enää mahdollista. Ilmoittautumisaika on päättynyt.", + "title": "Ilmoittautumisaika on päättynyt" }, "upcoming": { "description": "Ilmoittautuminen ei ole vielä mahdollista. Ilmoittautumisaika ei ole alkanut.", diff --git a/frontend/packages/yki/src/pages/InitRegistrationPage.tsx b/frontend/packages/yki/src/pages/InitRegistrationPage.tsx index 8d62137e5..c28ffd431 100644 --- a/frontend/packages/yki/src/pages/InitRegistrationPage.tsx +++ b/frontend/packages/yki/src/pages/InitRegistrationPage.tsx @@ -1,16 +1,16 @@ import { Box, Grid, Paper } from '@mui/material'; -import dayjs, { Dayjs } from 'dayjs'; +import dayjs from 'dayjs'; import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { H1, H2, HeaderSeparator, Text } from 'shared/components'; -import { APIResponseStatus, Severity } from 'shared/enums'; +import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; +import { APIResponseStatus, Color, Severity, Variant } from 'shared/enums'; import { useToast, useWindowProperties } from 'shared/hooks'; import { EnrollToQueue } from 'components/registration/EnrollToQueue'; import { PublicIdentificationGrid } from 'components/registration/PublicIdentificationGrid'; import { PublicRegistrationExamSessionDetails } from 'components/registration/PublicRegistrationExamSessionDetails'; import { PublicIdentificationPageSkeleton } from 'components/skeletons/PublicIdentificationPageSkeleton'; -import { usePublicTranslation } from 'configs/i18n'; +import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { AppRoutes } from 'enums/app'; import { PublicRegistrationFormStep } from 'enums/publicRegistration'; @@ -37,29 +37,27 @@ const ContentSelector = () => { }; const DescribeUnavailability = ({ - now, - open, - start, + descriptionPrefix, }: { - now: Dayjs; - open: boolean; - start: Dayjs; + descriptionPrefix: string; }) => { const { t } = usePublicTranslation({ keyPrefix: 'yki.component.registration.unavailable', }); - - const reasonForUnavailability = !open - ? now.isBefore(start) - ? 'upcoming' - : 'past' - : 'full'; + const translateCommon = useCommonTranslation(); return ( - <> -

{t(reasonForUnavailability + '.title')}

- {t(reasonForUnavailability + '.description')} - +
+ {t(descriptionPrefix + '.description')} + + {translateCommon('backToHomePage')} + +
); }; @@ -74,12 +72,21 @@ const RegistrationNotAvailable = () => { const { open, availableQueue, start } = ExamSessionUtils.getEffectiveRegistrationPeriodDetails(examSession); const now = dayjs(); + const reasonForUnavailability = !open + ? now.isBefore(start) + ? 'upcoming' + : 'past' + : 'full'; return (
-

{availableQueue ? t('enrollToQueue.header') : t('header')}

+

+ {availableQueue + ? t('enrollToQueue.header') + : t(`unavailable.${reasonForUnavailability}.title`)} +

@@ -92,7 +99,9 @@ const RegistrationNotAvailable = () => { {availableQueue ? ( ) : ( - + )}
From 65b119a14735c6a3d06886bf8e9ad8af31ccc840 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Sat, 23 Sep 2023 21:16:46 +0300 Subject: [PATCH 27/72] YKI(Frontend): Return errors from dev proxy for init registration API calls for certain exam sessions --- frontend/packages/yki/setupProxy.js | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index 8499b83d2..4464eca9d 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -281,7 +281,7 @@ const adminUser = { ], lang: 'fi', }, - 'auth-method': 'CAS' + 'auth-method': 'CAS', }; /* @@ -1044,25 +1044,18 @@ module.exports = function (app) { app.post('/yki/api/registration/init', (req, res) => { const mockCall = () => { try { - /* - // Uncomment to test out different error messages. switch (req.body.exam_session_id) { case 11: res.status(409).send({ error: { registered: true } }); - case 13: - res.status(409).send({ error: { closed: true } }); case 12: res.status(409).send({ error: { full: true } }); + case 13: + res.status(409).send({ error: { closed: true } }); default: - req.body.exam_session_id % 2 === 0 - ? res.send(initRegistrationEmailAuth) - : res.send(initRegistration); + req.body.exam_session_id % 2 === 0 + ? res.send(initRegistrationEmailAuth) + : res.send(initRegistration); } - */ - - req.body.exam_session_id % 2 === 0 - ? res.send(initRegistrationEmailAuth) - : res.send(initRegistration); } catch (err) { res.status(404).send(err.message); } @@ -1143,7 +1136,7 @@ module.exports = function (app) { if (session) { res.send(session); } else { - res.status(404).send("Exam session not found"); + res.status(404).send('Exam session not found'); } } catch (err) { printError(req, err); From 42008e8ba971d0836baaaba711cb73526d8885de Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Sat, 23 Sep 2023 21:17:50 +0300 Subject: [PATCH 28/72] YKI(Frontend): Stepper stylings for init registration errors --- .../PublicRegistrationStepper.tsx | 21 ++-- .../registration/RegistrationNotAvailable.tsx | 108 ++++++++++++++++++ .../yki/src/pages/InitRegistrationPage.tsx | 89 +-------------- .../registration/_public-registration.scss | 7 ++ 4 files changed, 132 insertions(+), 93 deletions(-) create mode 100644 frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx index 38b6a974d..765dedfbc 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx @@ -1,7 +1,7 @@ import { Step, StepLabel, Stepper } from '@mui/material'; import { useSearchParams } from 'react-router-dom'; import { CircularStepper, H2, Text } from 'shared/components'; -import { Color } from 'shared/enums'; +import { APIResponseStatus, Color } from 'shared/enums'; import { useWindowProperties } from 'shared/hooks'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; @@ -12,6 +12,8 @@ import { registrationSelector } from 'redux/selectors/registration'; export const PublicRegistrationStepper = () => { const { activeStep } = useAppSelector(registrationSelector); + const { status: initRegistrationStatus } = + useAppSelector(registrationSelector).initRegistration; const { t } = usePublicTranslation({ keyPrefix: 'yki.component.registration.stepper', }); @@ -22,8 +24,10 @@ export const PublicRegistrationStepper = () => { const paymentStatus = params.get('status'); const isError = - activeStep === PublicRegistrationFormStep.Done && - paymentStatus !== PaymentStatus.Success; + (activeStep === PublicRegistrationFormStep.Done && + paymentStatus !== PaymentStatus.Success) || + (activeStep === PublicRegistrationFormStep.Register && + initRegistrationStatus === APIResponseStatus.Error); const doneStepNumber = PublicRegistrationFormStep.Done; @@ -41,11 +45,7 @@ export const PublicRegistrationStepper = () => { }; const getDescription = (stepNumber: number) => { - if (isError) { - return t('paymentAborted'); - } else { - return t(`step.${PublicRegistrationFormStep[stepNumber]}`); - } + return t(`step.${PublicRegistrationFormStep[stepNumber]}`); }; const getNextInformation = (stepNumber: number) => { @@ -100,8 +100,11 @@ export const PublicRegistrationStepper = () => { { + const { t } = usePublicTranslation({ + keyPrefix: 'yki.component.registration.unavailable', + }); + const translateCommon = useCommonTranslation(); + + return ( +
+ {t(descriptionPrefix + '.description')} + + {translateCommon('backToHomePage')} + +
+ ); +}; + +const getReasonForUnavailability = (examSession: ExamSession) => { + const { open, start } = + ExamSessionUtils.getEffectiveRegistrationPeriodDetails(examSession); + const now = dayjs(); + const reasonForUnavailability = !open + ? now.isBefore(start) + ? 'upcoming' + : 'past' + : 'full'; + + return reasonForUnavailability; +}; + +export const RegistrationUnavailableHeader = () => { + const { t } = usePublicTranslation({ + keyPrefix: 'yki.component.registration', + }); + const examSession = useAppSelector(examSessionSelector) + .examSession as ExamSession; + const { availableQueue } = + ExamSessionUtils.getEffectiveRegistrationPeriodDetails(examSession); + const reasonForUnavailability = getReasonForUnavailability(examSession); + + return ( +

+ {availableQueue + ? t('enrollToQueue.header') + : t(`unavailable.${reasonForUnavailability}.title`)} +

+ ); +}; + +export const RegistrationNotAvailable = () => { + const { isPhone } = useWindowProperties(); + const examSession = useAppSelector(examSessionSelector) + .examSession as ExamSession; + const { availableQueue } = + ExamSessionUtils.getEffectiveRegistrationPeriodDetails(examSession); + const reasonForUnavailability = getReasonForUnavailability(examSession); + + return ( + +
+
+ + +
+ +
+
+ + {availableQueue ? ( + + ) : ( + + )} +
+
+
+
+
+ ); +}; diff --git a/frontend/packages/yki/src/pages/InitRegistrationPage.tsx b/frontend/packages/yki/src/pages/InitRegistrationPage.tsx index c28ffd431..58300203e 100644 --- a/frontend/packages/yki/src/pages/InitRegistrationPage.tsx +++ b/frontend/packages/yki/src/pages/InitRegistrationPage.tsx @@ -1,20 +1,16 @@ -import { Box, Grid, Paper } from '@mui/material'; -import dayjs from 'dayjs'; +import { Box } from '@mui/material'; import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; -import { APIResponseStatus, Color, Severity, Variant } from 'shared/enums'; -import { useToast, useWindowProperties } from 'shared/hooks'; +import { APIResponseStatus, Severity } from 'shared/enums'; +import { useToast } from 'shared/hooks'; -import { EnrollToQueue } from 'components/registration/EnrollToQueue'; import { PublicIdentificationGrid } from 'components/registration/PublicIdentificationGrid'; -import { PublicRegistrationExamSessionDetails } from 'components/registration/PublicRegistrationExamSessionDetails'; +import { RegistrationNotAvailable } from 'components/registration/RegistrationNotAvailable'; import { PublicIdentificationPageSkeleton } from 'components/skeletons/PublicIdentificationPageSkeleton'; -import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { AppRoutes } from 'enums/app'; import { PublicRegistrationFormStep } from 'enums/publicRegistration'; -import { ExamSession } from 'interfaces/examSessions'; import { loadExamSession } from 'redux/reducers/examSession'; import { resetPublicIdentificationState } from 'redux/reducers/publicIdentification'; import { setActiveStep } from 'redux/reducers/registration'; @@ -36,81 +32,6 @@ const ContentSelector = () => { } }; -const DescribeUnavailability = ({ - descriptionPrefix, -}: { - descriptionPrefix: string; -}) => { - const { t } = usePublicTranslation({ - keyPrefix: 'yki.component.registration.unavailable', - }); - const translateCommon = useCommonTranslation(); - - return ( -
- {t(descriptionPrefix + '.description')} - - {translateCommon('backToHomePage')} - -
- ); -}; - -const RegistrationNotAvailable = () => { - const { t } = usePublicTranslation({ - keyPrefix: 'yki.component.registration', - }); - const examSession = useAppSelector(examSessionSelector) - .examSession as ExamSession; - const { isPhone } = useWindowProperties(); - - const { open, availableQueue, start } = - ExamSessionUtils.getEffectiveRegistrationPeriodDetails(examSession); - const now = dayjs(); - const reasonForUnavailability = !open - ? now.isBefore(start) - ? 'upcoming' - : 'past' - : 'full'; - - return ( - -
-
-

- {availableQueue - ? t('enrollToQueue.header') - : t(`unavailable.${reasonForUnavailability}.title`)} -

- -
- -
-
- - {availableQueue ? ( - - ) : ( - - )} -
-
-
-
-
- ); -}; - export const InitRegistrationPage = () => { // i18n const { t } = usePublicTranslation({ diff --git a/frontend/packages/yki/src/styles/components/registration/_public-registration.scss b/frontend/packages/yki/src/styles/components/registration/_public-registration.scss index 07935c1cf..af8701702 100644 --- a/frontend/packages/yki/src/styles/components/registration/_public-registration.scss +++ b/frontend/packages/yki/src/styles/components/registration/_public-registration.scss @@ -66,6 +66,13 @@ color: $color-grey-700; } } + + &__step-error { + span, + svg { + color: $color-red-500; + } + } } .half-width-on-desktop { From 2bccb7983b1567947f3a93e4452edc8f6119d344 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Sat, 23 Sep 2023 21:41:18 +0300 Subject: [PATCH 29/72] YKI(Frontend): Correct stepper titles for registration page when registration init fails --- .../yki/public/i18n/fi-FI/public.json | 4 +++ .../registration/PublicRegistrationGrid.tsx | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index a1ea52ba5..82cd33d79 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -323,6 +323,10 @@ } }, "unavailable": { + "alreadyRegistered": { + "description": "TODO Tähän tekstit figmasta", + "title": "Olet jo ilmoittautunut toiseen YKI-testiin samana testipäivänä" + }, "full": { "description": "Ilmoittautuminen ei ole mahdollista juuri nyt. Tutkinnossa ei ole vapaita paikkoja.", "title": "YKI-testi on täynnä" diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx index d1557be5b..5bbcaf3c2 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx @@ -17,7 +17,10 @@ import { PublicRegistrationStepper } from 'components/registration/PublicRegistr import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppSelector } from 'configs/redux'; import { APIEndpoints, PaymentStatus } from 'enums/api'; -import { PublicRegistrationFormStep } from 'enums/publicRegistration'; +import { + PublicRegistrationFormStep, + PublicRegistrationInitError, +} from 'enums/publicRegistration'; import { ExamSession } from 'interfaces/examSessions'; import { examSessionSelector } from 'redux/selectors/examSession'; import { registrationSelector } from 'redux/selectors/registration'; @@ -130,29 +133,40 @@ const StepContentSelector = () => { const Heading = () => { const { activeStep } = useAppSelector(registrationSelector); + const { error: initRegistrationError } = + useAppSelector(registrationSelector).initRegistration; const { status: submitFormStatus } = useAppSelector(registrationSelector).submitRegistration; const [params] = useSearchParams(); const paymentStatus = params.get('status') as PaymentStatus; const { t } = usePublicTranslation({ - keyPrefix: 'yki.component.registration.steps', + keyPrefix: 'yki.component.registration', }); if (activeStep === PublicRegistrationFormStep.Register) { - if (submitFormStatus === APIResponseStatus.Success) { - return t('register.success.heading'); + if (initRegistrationError) { + switch (initRegistrationError) { + case PublicRegistrationInitError.AlreadyRegistered: + return t('unavailable.alreadyRegistered.title'); + case PublicRegistrationInitError.ExamSessionFull: + return t('unavailable.full.title'); + case PublicRegistrationInitError.RegistrationPeriodClosed: + return t('unavailable.past.title'); + } + } else if (submitFormStatus === APIResponseStatus.Success) { + return t('steps.register.success.heading'); } else { - return t('register.inProgress.heading'); + return t('steps.register.inProgress.heading'); } } else { switch (paymentStatus) { case PaymentStatus.Success: - return t('payment.success.heading'); + return t('steps.payment.success.heading'); case PaymentStatus.Cancel: - return t('payment.cancel.heading'); + return t('steps.payment.cancel.heading'); default: - return t('payment.error.heading'); + return t('steps.payment.error.heading'); } } }; From c5a05b27220096dfa8de9738d7f33bc6406c8182 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Sun, 24 Sep 2023 11:41:16 +0300 Subject: [PATCH 30/72] YKI(Frontend): Proper error views for case where init registration fails. --- .../yki/public/i18n/fi-FI/public.json | 2 +- .../registration/PublicRegistrationGrid.tsx | 26 +++++++---- .../PublicRegistrationInitErrorView.tsx | 46 +++++++++++++++++++ .../yki/src/redux/reducers/registration.ts | 6 ++- 4 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 82cd33d79..d0f2a8103 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -324,7 +324,7 @@ }, "unavailable": { "alreadyRegistered": { - "description": "TODO Tähän tekstit figmasta", + "description": "Voit ilmoittautua vain yhteen YKI-testiin samana testipäivänä", "title": "Olet jo ilmoittautunut toiseen YKI-testiin samana testipäivänä" }, "full": { diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx index 5bbcaf3c2..5fea4d278 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx @@ -10,28 +10,31 @@ import { import { APIResponseStatus } from 'shared/enums'; import { useWindowProperties } from 'shared/hooks'; +import { PublicRegistrationInitErrorView } from 'components/registration/errors/PublicRegistrationInitErrorView'; import { PublicRegistrationControlButtons } from 'components/registration/PublicRegistrationControlButtons'; import { PublicRegistrationExamSessionDetails } from 'components/registration/PublicRegistrationExamSessionDetails'; import { PublicRegistrationStepContents } from 'components/registration/PublicRegistrationStepContents'; import { PublicRegistrationStepper } from 'components/registration/PublicRegistrationStepper'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; -import { useAppSelector } from 'configs/redux'; +import { useAppDispatch, useAppSelector } from 'configs/redux'; import { APIEndpoints, PaymentStatus } from 'enums/api'; import { PublicRegistrationFormStep, PublicRegistrationInitError, } from 'enums/publicRegistration'; import { ExamSession } from 'interfaces/examSessions'; +import { loadExamSession } from 'redux/reducers/examSession'; import { examSessionSelector } from 'redux/selectors/examSession'; import { registrationSelector } from 'redux/selectors/registration'; const RegistrationForm = () => { - const { status: initRegistrationStatus, error } = + const dispatch = useAppDispatch(); + const { status: initRegistrationStatus, examSessionId } = useAppSelector(registrationSelector).initRegistration; const { status: submitFormStatus } = useAppSelector(registrationSelector).submitRegistration; - const { examSession } = useAppSelector(examSessionSelector); - const translateCommon = useCommonTranslation(); + const { examSession, status: examSessionStatus } = + useAppSelector(examSessionSelector); const [searchParams] = useSearchParams(); useEffect(() => { @@ -43,6 +46,11 @@ const RegistrationForm = () => { window.location.href = `${APIEndpoints.Logout}?redirect=${redirectTo}`; } }); + useEffect(() => { + if (!examSession && examSessionStatus === APIResponseStatus.NotStarted) { + dispatch(loadExamSession(examSessionId as number)); + } + }, [dispatch, examSession, examSessionId, examSessionStatus]); if (submitFormStatus === APIResponseStatus.Success) { return ( @@ -61,11 +69,11 @@ const RegistrationForm = () => { case APIResponseStatus.Error: return (
-

- {error - ? translateCommon(`errors.registration.${error}`) - : translateCommon('error')} -

+ +
); case APIResponseStatus.NotStarted: diff --git a/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx b/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx new file mode 100644 index 000000000..499dfb9e5 --- /dev/null +++ b/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx @@ -0,0 +1,46 @@ +import { CustomButton, Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; + +import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { useAppSelector } from 'configs/redux'; +import { AppRoutes } from 'enums/app'; +import { PublicRegistrationInitError } from 'enums/publicRegistration'; +import { registrationSelector } from 'redux/selectors/registration'; + +const DescribeInitError = () => { + const { t } = usePublicTranslation({ + keyPrefix: 'yki.component.registration.unavailable', + }); + + const error = useAppSelector(registrationSelector).initRegistration + .error as PublicRegistrationInitError; + + switch (error) { + case PublicRegistrationInitError.ExamSessionFull: + return {t('full.description')}; + case PublicRegistrationInitError.RegistrationPeriodClosed: + return {t('past.description')}; + // TODO The designs for the "already registered" case are much more complex + // than the current implementation. + case PublicRegistrationInitError.AlreadyRegistered: + return {t('alreadyRegistered.description')}; + } +}; + +export const PublicRegistrationInitErrorView = () => { + const translateCommon = useCommonTranslation(); + + return ( +
+ + + {translateCommon('backToHomePage')} + +
+ ); +}; diff --git a/frontend/packages/yki/src/redux/reducers/registration.ts b/frontend/packages/yki/src/redux/reducers/registration.ts index 47964331b..b954d4e69 100644 --- a/frontend/packages/yki/src/redux/reducers/registration.ts +++ b/frontend/packages/yki/src/redux/reducers/registration.ts @@ -18,6 +18,7 @@ export interface RegistrationState { initRegistration: { status: APIResponseStatus; error?: PublicRegistrationInitError; + examSessionId?: number; }; submitRegistration: { status: APIResponseStatus; @@ -54,8 +55,9 @@ const registrationSlice = createSlice({ name: 'registration', initialState, reducers: { - initRegistration(state, _action: PayloadAction) { + initRegistration(state, action: PayloadAction) { state.initRegistration.status = APIResponseStatus.InProgress; + state.initRegistration.examSessionId = action.payload; }, rejectPublicRegistrationInit( state, @@ -81,7 +83,7 @@ const registrationSlice = createSlice({ state, action: PayloadAction ) { - state.initRegistration = { status: APIResponseStatus.Success }; + state.initRegistration.status = APIResponseStatus.Success; const { registration_id, is_strongly_identified, user } = action.payload; const nationality = user.nationalities && user.nationalities[0]; if (is_strongly_identified) { From 1c3bfdc05b54fabd11507508e3cc29ad73b4fa1b Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Sun, 24 Sep 2023 21:51:17 +0300 Subject: [PATCH 31/72] YKI(Frontend): Fix lint --- .../src/components/registration/RegistrationNotAvailable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx b/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx index 8764c86d3..18a9fa94e 100644 --- a/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx +++ b/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx @@ -51,7 +51,7 @@ const getReasonForUnavailability = (examSession: ExamSession) => { return reasonForUnavailability; }; -export const RegistrationUnavailableHeader = () => { +const RegistrationUnavailableHeader = () => { const { t } = usePublicTranslation({ keyPrefix: 'yki.component.registration', }); From eeb53816d0f120b3d0a30bc4af03ae03bc626730 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 26 Sep 2023 20:05:33 +0300 Subject: [PATCH 32/72] YKI(Frontend): Align registration payment status views more closely with design --- .../yki/public/i18n/fi-FI/public.json | 18 +++---- frontend/packages/yki/setupProxy.js | 9 ++-- .../registration/PublicRegistrationGrid.tsx | 24 ++-------- .../PublicRegistrationStepper.tsx | 19 ++++---- .../PublicRegistrationInitErrorView.tsx | 47 ++++++++++++++----- .../components/registration/steps/Payment.tsx | 35 ++++++++------ .../yki/src/enums/publicRegistration.ts | 4 +- .../yki/src/redux/reducers/registration.ts | 5 +- 8 files changed, 90 insertions(+), 71 deletions(-) diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index d0f2a8103..0dc4919a6 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -284,14 +284,12 @@ }, "payment": { "cancel": { - "description": "Ilmoittautuminen vahvistuu vasta, kun olet maksanut tutkintomaksun.", - "heading": "Maksutapahtuma keskeytyi", - "title": "Keskeytit maksutapahtuman" + "description": "Tutkintomaksun maksaminen ei onnistunut. Yritä maksua uudelleen.", + "heading": "Maksu epäonnistui" }, "error": { - "description": "Voit yrittää maksamista uudestaan. Ilmoittautuminen vahvistuu vasta, kun olet maksanut tutkintomaksun.", - "heading": "Virhe maksutapahtumassa", - "title": "Maksutapahtuman käsittelyssä tapahtui virhe" + "description": "Tutkintomaksun maksaminen ei onnistunut. Yritä maksua uudelleen.", + "heading": "Maksu epäonnistui" }, "success": { "whatsNext": { @@ -327,6 +325,10 @@ "description": "Voit ilmoittautua vain yhteen YKI-testiin samana testipäivänä", "title": "Olet jo ilmoittautunut toiseen YKI-testiin samana testipäivänä" }, + "generic": { + "description": "Lomakkeen lähetyksessä tapahtui virhe. Voit palata ilmoittautumislomakkeelle ja yrittää uudelleen.", + "title": "Ilmoittautumislomakkeen lähetys epäonnistui" + }, "full": { "description": "Ilmoittautuminen ei ole mahdollista juuri nyt. Tutkinnossa ei ole vapaita paikkoja.", "title": "YKI-testi on täynnä" @@ -352,11 +354,11 @@ }, "evaluationOrderStatusPage": { "cancel": { - "heading": "Maksutapahtuma keskeytyi!", + "heading": "Maksu epäonnistui", "info": "Voit yrittää uudelleen." }, "error": { - "heading": "Tapahtui virhe!", + "heading": "Maksu epäonnistui", "info": "Voit yrittää uudelleen." }, "success": { diff --git a/frontend/packages/yki/setupProxy.js b/frontend/packages/yki/setupProxy.js index 4464eca9d..f97610259 100644 --- a/frontend/packages/yki/setupProxy.js +++ b/frontend/packages/yki/setupProxy.js @@ -1051,10 +1051,13 @@ module.exports = function (app) { res.status(409).send({ error: { full: true } }); case 13: res.status(409).send({ error: { closed: true } }); + // This error case shouldn't ordinarily happen + case 14: + res.status(409).send({ error: { full: false, registered: false } }); default: - req.body.exam_session_id % 2 === 0 - ? res.send(initRegistrationEmailAuth) - : res.send(initRegistration); + req.body.exam_session_id % 2 === 0 + ? res.send(initRegistrationEmailAuth) + : res.send(initRegistration); } } catch (err) { res.status(404).send(err.message); diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx index 5fea4d278..08bc75d2f 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationGrid.tsx @@ -18,10 +18,7 @@ import { PublicRegistrationStepper } from 'components/registration/PublicRegistr import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { APIEndpoints, PaymentStatus } from 'enums/api'; -import { - PublicRegistrationFormStep, - PublicRegistrationInitError, -} from 'enums/publicRegistration'; +import { PublicRegistrationFormStep } from 'enums/publicRegistration'; import { ExamSession } from 'interfaces/examSessions'; import { loadExamSession } from 'redux/reducers/examSession'; import { examSessionSelector } from 'redux/selectors/examSession'; @@ -67,15 +64,7 @@ const RegistrationForm = () => { switch (initRegistrationStatus) { case APIResponseStatus.Cancelled: case APIResponseStatus.Error: - return ( -
- - -
- ); + return ; case APIResponseStatus.NotStarted: case APIResponseStatus.InProgress: return null; @@ -154,14 +143,7 @@ const Heading = () => { if (activeStep === PublicRegistrationFormStep.Register) { if (initRegistrationError) { - switch (initRegistrationError) { - case PublicRegistrationInitError.AlreadyRegistered: - return t('unavailable.alreadyRegistered.title'); - case PublicRegistrationInitError.ExamSessionFull: - return t('unavailable.full.title'); - case PublicRegistrationInitError.RegistrationPeriodClosed: - return t('unavailable.past.title'); - } + return t(`unavailable.${initRegistrationError}.title`); } else if (submitFormStatus === APIResponseStatus.Success) { return t('steps.register.success.heading'); } else { diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx index 765dedfbc..066cbe745 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationStepper.tsx @@ -66,12 +66,15 @@ export const PublicRegistrationStepper = () => { return `${t('phase')} ${partStatus}: ${getDescription(stepNumber)}`; }; - const stepValue = Math.min(activeStep, doneStepNumber); + const stepValue = + isError && activeStep === PublicRegistrationFormStep.Done + ? PublicRegistrationFormStep.Payment + : Math.min(activeStep, doneStepNumber); const mobileStepValue = stepValue * (100 / doneStepNumber); const mobilePhaseText = `${stepValue}/${doneStepNumber}`; const mobileAriaLabel = `${t('phase')} ${mobilePhaseText}: ${getDescription( - activeStep + stepValue )}`; if (isPhone) { @@ -85,8 +88,8 @@ export const PublicRegistrationStepper = () => { size={90} />
-

{getDescription(activeStep)}

- {getNextInformation(activeStep)} +

{getDescription(stepValue)}

+ {!isError && {getNextInformation(stepValue)}}
); @@ -94,17 +97,17 @@ export const PublicRegistrationStepper = () => { return ( {stepNumbers.map((i) => ( { @@ -16,31 +18,50 @@ const DescribeInitError = () => { .error as PublicRegistrationInitError; switch (error) { - case PublicRegistrationInitError.ExamSessionFull: - return {t('full.description')}; - case PublicRegistrationInitError.RegistrationPeriodClosed: - return {t('past.description')}; // TODO The designs for the "already registered" case are much more complex // than the current implementation. case PublicRegistrationInitError.AlreadyRegistered: return {t('alreadyRegistered.description')}; + case PublicRegistrationInitError.ExamSessionFull: + case PublicRegistrationInitError.Past: + case PublicRegistrationInitError.Upcoming: + default: + return {t(error + '.description')}; } }; export const PublicRegistrationInitErrorView = () => { const translateCommon = useCommonTranslation(); + const { examSession } = useAppSelector(examSessionSelector); + const { error } = useAppSelector(registrationSelector).initRegistration; + const showExamSessionDetails = + error === PublicRegistrationInitError.ExamSessionFull || + error === PublicRegistrationInitError.Past || + error === PublicRegistrationInitError.Upcoming; return ( -
- - + {showExamSessionDetails ? ( + + ) : null} +
- {translateCommon('backToHomePage')} - + + + {translateCommon('backToHomePage')} + +
); }; diff --git a/frontend/packages/yki/src/components/registration/steps/Payment.tsx b/frontend/packages/yki/src/components/registration/steps/Payment.tsx index 891164df8..c5637ff76 100644 --- a/frontend/packages/yki/src/components/registration/steps/Payment.tsx +++ b/frontend/packages/yki/src/components/registration/steps/Payment.tsx @@ -1,10 +1,12 @@ import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Link } from '@mui/material'; import { useSearchParams } from 'react-router-dom'; -import { H2, Text } from 'shared/components'; +import { CustomButton, H2, Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; -import { usePublicTranslation } from 'configs/i18n'; +import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { PaymentStatus } from 'enums/api'; +import { AppRoutes } from 'enums/app'; const PaymentSuccess = () => { const { t } = usePublicTranslation({ @@ -47,12 +49,7 @@ const PaymentCancel = () => { keyPrefix: 'yki.component.registration.steps.payment.cancel', }); - return ( - <> -

{t('title')}

- {t('description')} - - ); + return {t('description')}; }; const PaymentError = () => { @@ -60,17 +57,13 @@ const PaymentError = () => { keyPrefix: 'yki.component.registration.steps.payment.error', }); - return ( - <> -

{t('title')}

- {t('description')} - - ); + return {t('description')}; }; export const Payment = () => { const [params] = useSearchParams(); const paymentStatus = params.get('status') as PaymentStatus; + const translateCommon = useCommonTranslation(); const renderPayment = () => { switch (paymentStatus) { @@ -83,5 +76,17 @@ export const Payment = () => { } }; - return
{renderPayment()}
; + return ( +
+ {renderPayment()} + + {translateCommon('backToHomePage')} + +
+ ); }; diff --git a/frontend/packages/yki/src/enums/publicRegistration.ts b/frontend/packages/yki/src/enums/publicRegistration.ts index c6f15fc3f..9511a0560 100644 --- a/frontend/packages/yki/src/enums/publicRegistration.ts +++ b/frontend/packages/yki/src/enums/publicRegistration.ts @@ -8,7 +8,9 @@ export enum PublicRegistrationFormStep { export enum PublicRegistrationInitError { AlreadyRegistered = 'alreadyRegistered', ExamSessionFull = 'examSessionFull', - RegistrationPeriodClosed = 'registrationPeriodClosed', + Generic = 'generic', + Past = 'past', + Upcoming = 'upcoming', } export enum PublicRegistrationFormSubmitError { diff --git a/frontend/packages/yki/src/redux/reducers/registration.ts b/frontend/packages/yki/src/redux/reducers/registration.ts index b954d4e69..7377112df 100644 --- a/frontend/packages/yki/src/redux/reducers/registration.ts +++ b/frontend/packages/yki/src/redux/reducers/registration.ts @@ -66,14 +66,15 @@ const registrationSlice = createSlice({ state.initRegistration.status = APIResponseStatus.Error; const { closed, full, registered } = action.payload.error; if (closed) { - state.initRegistration.error = - PublicRegistrationInitError.RegistrationPeriodClosed; + state.initRegistration.error = PublicRegistrationInitError.Past; } else if (full) { state.initRegistration.error = PublicRegistrationInitError.ExamSessionFull; } else if (registered) { state.initRegistration.error = PublicRegistrationInitError.AlreadyRegistered; + } else { + state.initRegistration.error = PublicRegistrationInitError.Generic; } }, resetPublicRegistration() { From ba90498e9a869874d4874579a52833edc3bbe908 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 26 Sep 2023 20:38:23 +0300 Subject: [PATCH 33/72] YKI(Frontend): Adjust evalution order status views with designs --- .../yki/public/i18n/fi-FI/public.json | 4 ++-- .../src/pages/EvaluationOrderStatusPage.tsx | 21 ++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 0dc4919a6..ea648334c 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -355,11 +355,11 @@ "evaluationOrderStatusPage": { "cancel": { "heading": "Maksu epäonnistui", - "info": "Voit yrittää uudelleen." + "info": "Tarkistusarviointimaksun maksaminen ei onnistunut. Yritä maksua uudelleen." }, "error": { "heading": "Maksu epäonnistui", - "info": "Voit yrittää uudelleen." + "info": "Tarkistusarviointimaksun maksaminen ei onnistunut. Yritä maksua uudelleen." }, "success": { "heading": "Olet lähettänyt tarkistusarviointipyynnön!", diff --git a/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx b/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx index c7b15b742..af2fa6b07 100644 --- a/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx +++ b/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx @@ -1,14 +1,15 @@ import { Box, Paper } from '@mui/material'; import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { H1, HeaderSeparator, Text } from 'shared/components'; -import { APIResponseStatus } from 'shared/enums'; +import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; +import { APIResponseStatus, Color, Variant } from 'shared/enums'; import { useWindowProperties } from 'shared/hooks'; import { DateUtils } from 'shared/utils'; import { OrderStatus } from 'components/orderStatus/OrderStatus'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; +import { AppRoutes } from 'enums/app'; import { EvaluationOrderDetails } from 'interfaces/evaluationOrder'; import { loadEvaluationOrderDetails, @@ -41,12 +42,15 @@ const EvaluationDetails = ({ const EvaluationOrderStatusContents = ({ heading, contents, + showEvaluationOrderDetails, }: { heading: string; contents: JSX.Element; + showEvaluationOrderDetails: boolean; }) => { const { evaluationOrderDetails } = useAppSelector(evaluationOrderSelector); const { isPhone } = useWindowProperties(); + const translateCommon = useCommonTranslation(); return ( <> @@ -58,10 +62,18 @@ const EvaluationOrderStatusContents = ({ elevation={isPhone ? 0 : 3} className="public-evaluation-order-status-page__paper rows gapped" > - {evaluationOrderDetails && ( + {evaluationOrderDetails && showEvaluationOrderDetails && ( )} {contents} + + {translateCommon('backToHomePage')} + ); @@ -81,6 +93,7 @@ const Success = () => { {t('body2')} } + showEvaluationOrderDetails={true} /> ); }; @@ -94,6 +107,7 @@ const Cancel = () => { {t('info')}} + showEvaluationOrderDetails={false} /> ); }; @@ -107,6 +121,7 @@ const Error = () => { {t('info')}} + showEvaluationOrderDetails={false} /> ); }; From b1e26368ea0e22dea227ad52cb89ba671d68f77e Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 27 Sep 2023 12:09:41 +0300 Subject: [PATCH 34/72] YKI(Frontend): Fix mapping from error to translation --- frontend/packages/yki/src/enums/publicRegistration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/enums/publicRegistration.ts b/frontend/packages/yki/src/enums/publicRegistration.ts index 9511a0560..60f95724d 100644 --- a/frontend/packages/yki/src/enums/publicRegistration.ts +++ b/frontend/packages/yki/src/enums/publicRegistration.ts @@ -7,7 +7,7 @@ export enum PublicRegistrationFormStep { export enum PublicRegistrationInitError { AlreadyRegistered = 'alreadyRegistered', - ExamSessionFull = 'examSessionFull', + ExamSessionFull = 'full', Generic = 'generic', Past = 'past', Upcoming = 'upcoming', From bf5c75c31617299c4dacb49443aee3dcb16d6b37 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 27 Sep 2023 12:11:45 +0300 Subject: [PATCH 35/72] YKI(Frontend): Reusable BackToFrontPageButton component --- .../elements/BackToFrontPageButton.tsx | 20 +++++++++++++++++++ .../registration/RegistrationNotAvailable.tsx | 17 ++++------------ .../PublicRegistrationInitErrorView.tsx | 17 ++++------------ .../components/registration/steps/Payment.tsx | 17 ++++------------ .../src/pages/EvaluationOrderStatusPage.tsx | 16 ++++----------- .../packages/yki/src/pages/NotFoundPage.tsx | 17 ++++------------ 6 files changed, 40 insertions(+), 64 deletions(-) create mode 100644 frontend/packages/yki/src/components/elements/BackToFrontPageButton.tsx diff --git a/frontend/packages/yki/src/components/elements/BackToFrontPageButton.tsx b/frontend/packages/yki/src/components/elements/BackToFrontPageButton.tsx new file mode 100644 index 000000000..3256438bb --- /dev/null +++ b/frontend/packages/yki/src/components/elements/BackToFrontPageButton.tsx @@ -0,0 +1,20 @@ +import { CustomButton } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; + +import { useCommonTranslation } from 'configs/i18n'; +import { AppRoutes } from 'enums/app'; + +export const BackToFrontPageButton = () => { + const translateCommon = useCommonTranslation(); + + return ( + + {translateCommon('backToHomePage')} + + ); +}; diff --git a/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx b/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx index 18a9fa94e..f53e7eeb9 100644 --- a/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx +++ b/frontend/packages/yki/src/components/registration/RegistrationNotAvailable.tsx @@ -1,14 +1,13 @@ import { Grid, Paper } from '@mui/material'; import dayjs from 'dayjs'; -import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; -import { Color, Variant } from 'shared/enums'; +import { H1, HeaderSeparator, Text } from 'shared/components'; import { useWindowProperties } from 'shared/hooks'; +import { BackToFrontPageButton } from 'components/elements/BackToFrontPageButton'; import { EnrollToQueue } from 'components/registration/EnrollToQueue'; import { PublicRegistrationExamSessionDetails } from 'components/registration/PublicRegistrationExamSessionDetails'; -import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { usePublicTranslation } from 'configs/i18n'; import { useAppSelector } from 'configs/redux'; -import { AppRoutes } from 'enums/app'; import { ExamSession } from 'interfaces/examSessions'; import { examSessionSelector } from 'redux/selectors/examSession'; import { ExamSessionUtils } from 'utils/examSession'; @@ -21,19 +20,11 @@ const DescribeUnavailability = ({ const { t } = usePublicTranslation({ keyPrefix: 'yki.component.registration.unavailable', }); - const translateCommon = useCommonTranslation(); return (
{t(descriptionPrefix + '.description')} - - {translateCommon('backToHomePage')} - +
); }; diff --git a/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx b/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx index f6394934c..eb5658e04 100644 --- a/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx +++ b/frontend/packages/yki/src/components/registration/errors/PublicRegistrationInitErrorView.tsx @@ -1,10 +1,9 @@ -import { CustomButton, Text } from 'shared/components'; -import { Color, Variant } from 'shared/enums'; +import { Text } from 'shared/components'; +import { BackToFrontPageButton } from 'components/elements/BackToFrontPageButton'; import { PublicRegistrationExamSessionDetails } from 'components/registration/PublicRegistrationExamSessionDetails'; -import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { usePublicTranslation } from 'configs/i18n'; import { useAppSelector } from 'configs/redux'; -import { AppRoutes } from 'enums/app'; import { PublicRegistrationInitError } from 'enums/publicRegistration'; import { examSessionSelector } from 'redux/selectors/examSession'; import { registrationSelector } from 'redux/selectors/registration'; @@ -31,7 +30,6 @@ const DescribeInitError = () => { }; export const PublicRegistrationInitErrorView = () => { - const translateCommon = useCommonTranslation(); const { examSession } = useAppSelector(examSessionSelector); const { error } = useAppSelector(registrationSelector).initRegistration; const showExamSessionDetails = @@ -53,14 +51,7 @@ export const PublicRegistrationInitErrorView = () => { } > - - {translateCommon('backToHomePage')} - + ); diff --git a/frontend/packages/yki/src/components/registration/steps/Payment.tsx b/frontend/packages/yki/src/components/registration/steps/Payment.tsx index c5637ff76..5ffb2d4a8 100644 --- a/frontend/packages/yki/src/components/registration/steps/Payment.tsx +++ b/frontend/packages/yki/src/components/registration/steps/Payment.tsx @@ -1,12 +1,11 @@ import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Link } from '@mui/material'; import { useSearchParams } from 'react-router-dom'; -import { CustomButton, H2, Text } from 'shared/components'; -import { Color, Variant } from 'shared/enums'; +import { H2, Text } from 'shared/components'; -import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { BackToFrontPageButton } from 'components/elements/BackToFrontPageButton'; +import { usePublicTranslation } from 'configs/i18n'; import { PaymentStatus } from 'enums/api'; -import { AppRoutes } from 'enums/app'; const PaymentSuccess = () => { const { t } = usePublicTranslation({ @@ -63,7 +62,6 @@ const PaymentError = () => { export const Payment = () => { const [params] = useSearchParams(); const paymentStatus = params.get('status') as PaymentStatus; - const translateCommon = useCommonTranslation(); const renderPayment = () => { switch (paymentStatus) { @@ -79,14 +77,7 @@ export const Payment = () => { return (
{renderPayment()} - - {translateCommon('backToHomePage')} - +
); }; diff --git a/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx b/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx index af2fa6b07..b996a9b1f 100644 --- a/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx +++ b/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx @@ -1,15 +1,15 @@ import { Box, Paper } from '@mui/material'; import { useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; -import { APIResponseStatus, Color, Variant } from 'shared/enums'; +import { H1, HeaderSeparator, Text } from 'shared/components'; +import { APIResponseStatus } from 'shared/enums'; import { useWindowProperties } from 'shared/hooks'; import { DateUtils } from 'shared/utils'; +import { BackToFrontPageButton } from 'components/elements/BackToFrontPageButton'; import { OrderStatus } from 'components/orderStatus/OrderStatus'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; -import { AppRoutes } from 'enums/app'; import { EvaluationOrderDetails } from 'interfaces/evaluationOrder'; import { loadEvaluationOrderDetails, @@ -50,7 +50,6 @@ const EvaluationOrderStatusContents = ({ }) => { const { evaluationOrderDetails } = useAppSelector(evaluationOrderSelector); const { isPhone } = useWindowProperties(); - const translateCommon = useCommonTranslation(); return ( <> @@ -66,14 +65,7 @@ const EvaluationOrderStatusContents = ({ )} {contents} - - {translateCommon('backToHomePage')} - + ); diff --git a/frontend/packages/yki/src/pages/NotFoundPage.tsx b/frontend/packages/yki/src/pages/NotFoundPage.tsx index 1d864c64b..af4c1bc38 100644 --- a/frontend/packages/yki/src/pages/NotFoundPage.tsx +++ b/frontend/packages/yki/src/pages/NotFoundPage.tsx @@ -1,17 +1,15 @@ import { Paper } from '@mui/material'; import { FC } from 'react'; -import { CustomButton, H1, HeaderSeparator, Text } from 'shared/components'; -import { Color, Variant } from 'shared/enums'; +import { H1, HeaderSeparator, Text } from 'shared/components'; import { useWindowProperties } from 'shared/hooks'; -import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; -import { AppRoutes } from 'enums/app'; +import { BackToFrontPageButton } from 'components/elements/BackToFrontPageButton'; +import { usePublicTranslation } from 'configs/i18n'; export const NotFoundPage: FC = () => { const { t } = usePublicTranslation({ keyPrefix: 'yki.pages.notFoundPage', }); - const translateCommon = useCommonTranslation(); const { isPhone } = useWindowProperties(); return ( @@ -22,14 +20,7 @@ export const NotFoundPage: FC = () => { {t('description')} - - {translateCommon('backToHomePage')} - + ); From 5c0d484b43f047047568c036f87ae00a6b0dd852 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 28 Sep 2023 16:47:07 +0300 Subject: [PATCH 36/72] YKI(Frontend): Add accessibility statement (Finnish version only) --- .../yki/public/i18n/fi-FI/accessibility.json | 76 ++++++++++++++++--- .../src/pages/AccessibilityStatementPage.tsx | 15 ++-- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/frontend/packages/yki/public/i18n/fi-FI/accessibility.json b/frontend/packages/yki/public/i18n/fi-FI/accessibility.json index 0c823925e..02f1c76c4 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/accessibility.json +++ b/frontend/packages/yki/public/i18n/fi-FI/accessibility.json @@ -14,37 +14,93 @@ "title": "Valvontaviranomaisen yhteystiedot" }, "caveats": [ + { + "name": "Käytetty kieli", + "points": [ + "Kaikkia elementtejä tai aria-labeleita ei ole käännetty sivun kohdekielelle. Esim. pudotusvalikoissa löytyy joka sivulla englanniksi nimettyjä elementtejä. (WCAG 2.5.3)" + ] + }, + { + "name": "Kontrastit", + "points": [ + "Syötekenttien kontrastit eivät täytä saavutettavuusvaatimuksia. (WCAG 1.4.11)", + "Ajoittain myöskään näppäimistön kohdistusindikaattori ei täytä kontrastivaatimuksia (WCAG 1.4.11)" + ] + }, + { + "name": "Sivujen nimet", + "points": [ + "Kaikilla sivuilla ei ole uniikkeja nimiä (title), jotka luettaisiin ruudunlukijalla ja jotka näkyisivät sivuhistoriassa (WCAG 2.4.2)" + ] + }, + { + "name": "Modaali", + "points": [ + "Modaalin sisältöihin pääseminen ei toimi kaikilla ruudunlukijoilla yleisten konventioiden mukaisesti eikä sen kaikkea sisältöä välttämättä lueta (WCAG 1.3.1, 4.1.2)" + ] + }, + { + "name": "Ohjelmallisen ja visuaalisen käyttöliittymän vastaavuus", + "points": [ + "Tutkinnon valitsemiseen liittyvä nostolaatikko on ohjelmallisesti ennen siihen liittyvää otsikkoa eikä siitä käy ohjelmallisesti ilmi, että se on nosto eikä tavallista leipätekstiä (WCAG 1.3.1)" + ] + }, + { + "name": "Pudotusvalikot/comboboxit", + "points": [ + "Palvelussa on käytetty hakuelementissä sekä ilmoittautumislomakkeella ns. comboboxeja, joiden ymmärrettävyys kaikilla ruudunlukijoilla on tällä hetkellä haasteellista mm. teknisen toteutuksen sekä epäselvän nimeämisen takia. Myös näppäimistökäytössä on haasteita. (WCAG 1.3.1, 2.4.6, 3.3.2, 4.1.2)" + ] + }, + { + "name": "Vaihepolku (stepper)", + "points": [ + "Vaihepolun nimi luetaan, mutta sen sisältämiä tietoja ei pääse katsomaan (WCAG 1.3.1, 4.1.2)", + "Mobiilitoteutuksessa vaihepolkua ei ole nimetty riittävän ymmärrettävällä tasolla (WCAG 1.3.1)" + ] + }, + { + "name": "Virheilmoitukset", + "points": [ + "Käyttäjälle ei aina tarjota selkeitä ohjelmallisia tai visuaalisia virheilmoituksia, esim. pakollisten valintapainikeryhmien tai valintaruutujen yhteydessä (WCAG 1.3.1, 3.3.1)" + ] + } ], "composition": { - "description": "", + "description": "Tämä seloste perustuu ulkopuolisen asiantuntijaorganisaation (Knowit Solutions Oy) toteuttamaan arviointiin siitä, täyttääkö verkkosivusto Lain digitaalisten palvelujen tarjoamisesta 306/209 vaatimukset.", "title": "Tämän saavutettavuusselosteen laatiminen" }, "enforcement": { - "description": "", + "description1": "Jos huomaat sivustolla saavutettavuusongelmia, anna ensin palautetta meille, eli sivuston ylläpitäjälle.", + "description2": "Vastauksessa voi mennä 14 päivää.", + "description3": "Jos et ole tyytyväinen saamaasi vastaukseen tai et saa vastausta lainkaan kahden viikon aikana, voit antaa palautteen Etelä-Suomen aluehallintovirastoon.", "title": "Täytäntöönpanomenettely" }, "feedback": { - "description1": "", - "description2": "", + "description1": "Huomasitko saavutettavuuspuutteen digipalvelussamme? Kerro se meille ja teemme parhaamme puutteen korjaamiseksi. ", + "description2": "Sähköpostilla", "title": "Palaute ja yhteystiedot" }, "furtherImprove": { - "description1": "", - "description2": "", + "description1": "Tarjoamme tukea käyttäjille, joille digipalvelut eivät ole saavutettavissa.", + "description2": "Olemme sitoutuneet digipalveluiden saavutettavuuden parantamiseen.", "title": "Teemme jatkuvasti työtä saavutettavuuden parantamiseksi" }, "nonAccessible": { - "description1": "", - "description2": "", + "description1": "Jäljempänä mainittu sisältö ei ole saavutettavissa seuraavista syistä", + "description2": "Lain digitaalisten palvelujen tarjoamisesta 306/2019 noudattamatta jättäminen", "title": "Ei-saavutettava sisältö" }, "status": { - "description": "", + "description": "Tämä verkkosivusto täyttää Verkkosisällön saavutettavuusohjeiden (WCAG) 2.1-tason AA-vaatimukset osittain.", "title": "Saavutettavuuden tila" } }, "heading": { - "description": "", + "body": { + "part1": "Tämä saavutettavuusseloste koskee palvelua yki.opintopolku.fi ja se on laadittu 22.9.2023.", + "part2": "Palvelua koskee Laki digitaalisten palvelujen tarjoamisesta, jossa edellytetään, että julkisten verkkopalvelujen on oltava saavutettavia.", + "part3": "Palvelun saavutettavuuden on arvioinut ulkopuolinen asiantuntijaorganisaatio." + }, "title": "Saavutettavuusseloste" } } diff --git a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx index d50dba3ee..34e0d230a 100644 --- a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx +++ b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx @@ -82,7 +82,11 @@ export const AccessibilityStatementPage = () => {

{translateAccessibility('heading.title')}

- {translateAccessibility('heading.description')} + + {translateAccessibility('heading.body.part1')}{' '} + {translateAccessibility('heading.body.part2')}{' '} + {translateAccessibility('heading.body.part3')} +
@@ -94,12 +98,11 @@ export const AccessibilityStatementPage = () => {
-

{translateAccessibility('content.nonAccessible.title')}

+

{translateAccessibility('content.nonAccessible.title')}

{translateAccessibility('content.nonAccessible.description1')} {':'} - - +
{translateAccessibility('content.nonAccessible.description2')}
{caveats.map(({}, i) => ( @@ -137,7 +140,9 @@ export const AccessibilityStatementPage = () => {

{translateAccessibility('content.enforcement.title')}

- {translateAccessibility('content.enforcement.description')} + {translateAccessibility('content.enforcement.description1')}{' '} + {translateAccessibility('content.enforcement.description2')}{' '} + {translateAccessibility('content.enforcement.description3')}
From 909dda5e475c4c4ca7e43f5d925792410f3f809e Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 28 Sep 2023 16:51:45 +0300 Subject: [PATCH 37/72] YKI(Frontend): Fix footer link to privacy policy statement [deploy] --- frontend/packages/yki/public/i18n/en-GB/public.json | 3 ++- frontend/packages/yki/public/i18n/fi-FI/public.json | 3 ++- frontend/packages/yki/public/i18n/sv-SE/public.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index 8485d6572..fff42b3de 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -75,7 +75,8 @@ "title": "Feedback and development suggestions" }, "privacy": { - "text": "Privacy policy statement" + "text": "Privacy policy statement", + "url": "https://www.oph.fi/en/education-and-qualifications/registering-yki-test#anchor-information-on-the-processing-of-personal-data" }, "ykiHomepage": { "text": "Website of the National Certificates of Language Proficiency (YKI) (oph.fi)" diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index ea648334c..d3645e65b 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -75,7 +75,8 @@ "title": "Palaute ja kehitysideat" }, "privacy": { - "text": "Tietosuojaseloste" + "text": "Tietosuojaseloste", + "url": "https://www.oph.fi/fi/koulutus-ja-tutkinnot/kieli-ja-kaantajatutkinnot/yleiset-kielitutkinnot-yki/ilmoittautuminen-yki-testiin#anchor-tietoa-henkilotietojen-kasittelysta" }, "ykiHomepage": { "text": "Yleisten kielitutkintojen verkkosivu (oph.fi)" diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 3c6ad79fb..3d0c47ea4 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -75,7 +75,8 @@ "title": "Respons och utvecklingsidéer" }, "privacy": { - "text": "Information om behandlingen av personuppgifter" + "text": "Information om behandlingen av personuppgifter", + "url": "https://www.oph.fi/sv/utbildning-och-examina/anmalan-till-yki-test#anchor-information-om-behandlingen-av-personuppgifter" }, "ykiHomepage": { "text": "Webbplatsen för allmänna språkexamina (YKI) (oph.fi)" From cf4bd70cac9d34948f667b6a5506a1694951792a Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 28 Sep 2023 17:46:38 +0300 Subject: [PATCH 38/72] YKI(Frontend): Remove indexing restrictions from HTML meta --- frontend/packages/yki/public/index.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/packages/yki/public/index.html b/frontend/packages/yki/public/index.html index 5dbb22d78..c582857f1 100644 --- a/frontend/packages/yki/public/index.html +++ b/frontend/packages/yki/public/index.html @@ -8,8 +8,6 @@ content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - - Date: Thu, 28 Sep 2023 15:44:08 +0300 Subject: [PATCH 39/72] YKI(Frontend): Add temporary link to the old frontend --- .../packages/yki/public/i18n/en-GB/public.json | 5 +++++ .../packages/yki/public/i18n/fi-FI/public.json | 5 +++++ .../packages/yki/public/i18n/sv-SE/public.json | 5 +++++ .../packages/yki/src/pages/RegistrationPage.tsx | 7 +++++++ .../yki/src/styles/pages/_registration-page.scss | 15 +++++++++++++++ 5 files changed, 37 insertions(+) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index 8485d6572..263348727 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -448,6 +448,11 @@ "selectLevel": "Choose proficiency level", "selectMunicipality": "Choose municipality" }, + "oldUILink": { + "text": "This is the new YKI registration page. Do you want to use the old registration page?", + "link": "https://yki.opintopolku.fi/yki?lang=en", + "linkText": "Go to the old YKI registration page" + }, "register": "Register", "title": "National Certificates of Language Proficiency (YKI) - Registration" } diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index ea648334c..c7e624222 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -463,6 +463,11 @@ "selectLevel": "Valitse taitotaso", "selectMunicipality": "Valitse paikkakunta" }, + "oldUILink": { + "text": "Tämä on uusi YKI-ilmoittautumissivu. Haluatko käyttää vanhaa YKI-ilmoittautumissivua?", + "link": "https://yki.opintopolku.fi/yki?lang=fi", + "linkText": "Siirry vanhalle YKI-ilmoittautumissivulle" + }, "register": "Ilmoittaudu", "title": "Yleiset kielitutkinnot (YKI) - Ilmoittautuminen" } diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 3c6ad79fb..f25e51554 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -448,6 +448,11 @@ "selectLevel": "Välj färdighetsnivå", "selectMunicipality": "Välj ort" }, + "oldUILink": { + "text": "Den här är den nya YKI-anmälningssidan. Vill du använda den gamla anmälningssidan?", + "link": "https://yki.opintopolku.fi/yki?lang=sv", + "linkText": "Till den gamla anmälningsidan" + }, "register": "Anmäl dig", "title": "Allmänna språkexamina (YKI) - Anmälan" } diff --git a/frontend/packages/yki/src/pages/RegistrationPage.tsx b/frontend/packages/yki/src/pages/RegistrationPage.tsx index c1bb129d1..c96a56395 100644 --- a/frontend/packages/yki/src/pages/RegistrationPage.tsx +++ b/frontend/packages/yki/src/pages/RegistrationPage.tsx @@ -54,6 +54,13 @@ export const RegistrationPage: FC = () => { item className="public-registration-page__grid-container__item-header" > + + {t('oldUILink.text')}{' '} + {t('oldUILink.linkText')} +

{t('title')}

diff --git a/frontend/packages/yki/src/styles/pages/_registration-page.scss b/frontend/packages/yki/src/styles/pages/_registration-page.scss index b9d6d2196..d48250934 100644 --- a/frontend/packages/yki/src/styles/pages/_registration-page.scss +++ b/frontend/packages/yki/src/styles/pages/_registration-page.scss @@ -67,4 +67,19 @@ } } } + + & &__link-to-old-ui { + background-color: $color-blue-200; + color: $color-text-primary; + margin: 2rem 0; + position: relative; + + & a { + font-weight: normal; + } + + & svg { + color: $color-secondary; + } + } } From b1672142be3c110ab6d396d2f57f0f48d08be66e Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 28 Sep 2023 18:34:52 +0300 Subject: [PATCH 40/72] YKI(Frontend): Update translations (public.json) --- .../yki/public/i18n/en-GB/public.json | 35 +++++++++---- .../yki/public/i18n/fi-FI/public.json | 16 +++--- .../yki/public/i18n/sv-SE/public.json | 49 +++++++++++++------ 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index c75df36bf..e3cc9bd27 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -174,7 +174,7 @@ "nationality": "Nationality", "phoneNumber": "Telephone number", "postNumber": "Postal code", - "postOffice": "Town/city", + "postOffice": "Municipality", "privacyStatementConfirmation": "Accept the terms and conditions for processing personal data", "ssn": "Personal identity code", "termsAndConditionsAgreed": "Accept the terms and conditions for registration" @@ -193,7 +193,7 @@ "nationality": "Nationality", "phoneNumber": "Telephone number", "postNumber": "Postal code", - "postOffice": "Town/city", + "postOffice": "Municipality", "ssn": "Personal identity code" }, "placeholders": { @@ -324,9 +324,17 @@ } }, "unavailable": { + "alreadyRegistered": { + "description": "You can register for only one YKI test on the same test day.", + "title": "You have already registered for another YKI test on the same test day" + }, "full": { "description": "Registration is not possible at the moment. There are no available seats for this test.", - "title": "The test is full." + "title": "The test is full" + }, + "generic": { + "description": "Sending the registration form failed. You can return to the registration form and try again.", + "title": "Sending the registration form failed" }, "past": { "description": "Registration is not possible at the moment. The registration period has ended.", @@ -349,29 +357,37 @@ }, "evaluationOrderStatusPage": { "cancel": { - "heading": "Payment aborted", - "info": "You aborted the payment. The reassessment request will only be taken into account after you have payed the reassessment fee." + "heading": "Payment was cancelled", + "info": "You may try to pay again. We will only process paid requests for reassessment." }, "error": { "heading": "Payment error", "info": "The payment did not go through. You may try again." }, "success": { - "heading": "Request for reassessment sent!", + "heading": "You have requested a reassessment!", "body1": "You have sent a request for reassessment.", - "body2": "The email also contains a receipt for the reassessment fee. If you have not received the email, please check your spam folder." + "body2": "You have received a confirmation of your request for reassessment by email. The email also contains information on the reassessment fee. If you have not received the email, please check your spam folder." } }, "examDetailsPage": { "toasts": { - "notFound": "Kielitutkintoa ei löytynyt" + "notFound": "YKI test was not found" } }, "initRegistrationPage": { "toasts": { - "notFound": "Kielitutkintoa ei löytynyt" + "notFound": "YKI test was not found" } }, + "logoutSuccessPage": { + "heading": "Log out complete", + "info": "You have logged out successfully. Close all browser windows." + }, + "notFoundPage": { + "description": "Please, check the website address or return to the front page.", + "title": "Page not found" + }, "reassessmentPage": { "info": { "general": { @@ -438,6 +454,7 @@ "information": "Search for YKI-tests by choosing the language and proficiency level. Click the SHOW RESULTS button to see the results.", "selectExamDetails": { "prompt": "Choose test", + "optional": "(optional)", "required": "(required)" } }, diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 5e9e12c79..c5402c719 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -327,7 +327,7 @@ "title": "Olet jo ilmoittautunut toiseen YKI-testiin samana testipäivänä" }, "generic": { - "description": "Lomakkeen lähetyksessä tapahtui virhe. Voit palata ilmoittautumislomakkeelle ja yrittää uudelleen.", + "description": "Ilmoittautumislomakkeen lähetys epäonnistui. Voit palata ilmoittautumislomakkeelle ja yrittää uudelleen.", "title": "Ilmoittautumislomakkeen lähetys epäonnistui" }, "full": { @@ -355,12 +355,12 @@ }, "evaluationOrderStatusPage": { "cancel": { - "heading": "Maksu epäonnistui", - "info": "Tarkistusarviointimaksun maksaminen ei onnistunut. Yritä maksua uudelleen." + "heading": "Maksu keskeytyi", + "info": "Voit yrittää maksua uudestaan. Käsittelemme vain maksetut tarkistusarviointipyynnöt." }, "error": { "heading": "Maksu epäonnistui", - "info": "Tarkistusarviointimaksun maksaminen ei onnistunut. Yritä maksua uudelleen." + "info": "Tarkistusarviointimaksun maksaminen ei onnistunut. Yritä maksua uudestaan." }, "success": { "heading": "Olet lähettänyt tarkistusarviointipyynnön!", @@ -370,20 +370,20 @@ }, "examDetailsPage": { "toasts": { - "notFound": "Kielitutkintoa ei löytynyt" + "notFound": "YKI-testiä ei löytynyt" } }, "initRegistrationPage": { "toasts": { - "notFound": "Kielitutkintoa ei löytynyt" + "notFound": "YKI-testiä ei löytynyt" } }, "logoutSuccessPage": { "heading": "Uloskirjautuminen onnnistui", - "info": "Olet kirjautunut ulos. Suljethan vielä kaikki selainikkunat." + "info": "Olet kirjautunut ulos. Sulje kaikki selainikkunat." }, "notFoundPage": { - "description": "Tarkista hakemasi osoite tai palaa aloitussivulle.", + "description": "Tarkista verkkosivun osoite tai palaa aloitussivulle.", "title": "Sivua ei löytynyt" }, "reassessmentPage": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index e9cc06bae..9005eba79 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -43,12 +43,12 @@ }, "renderEvaluationDetails": { "heading": "Uppgifter om examen", - "info": null + "info": "Du håller på att begära om kontrollbedömning om följande test:" }, "selectExaminationParts": { - "heading": "Välj delprov ", - "selectAtLeastOne": null, - "selectParts": "Välj de delprov för vilka du vill att bedömningen kontrolleras*", + "heading": "Välj delprov", + "selectAtLeastOne": "Välj minst ett delprov", + "selectParts": "Välj de delprov för vilka du vill att bedömningen kontrolleras *", "sumTotal": "Priset på kontrollbedömningen totalt" } }, @@ -324,10 +324,18 @@ } }, "unavailable": { + "alreadyRegistered": { + "description": "Du kan anmäla dig endast till ett YKI-test på samma testdag.", + "title": "Du har redan almält dig till ett annat YKI-test på samma testdag." + }, "full": { "description": "Det är inte möjligt att anmäla sig just nu. Det finns inga lediga platser i testet.", "title": "Testet är fullbokat" }, + "generic": { + "description": "Det gick inte att skicka anmälningsblankett. Du kan gå tillbaka till anmälningsblanketten och försöka på nytt.", + "title": "Det gick inte att skicka anmälningsblankett" + }, "past": { "description": "Det är inte längre möjligt att anmäla sig. Anmälningstiden har slutat.", "title": "Anmälningstiden har slutat" @@ -341,37 +349,45 @@ }, "pages": { "evaluationOrderPage": { - "notFound": null, - "title": "Gör en begäran om kontrollbedömning", + "notFound": "Uppgifter om kontrollbedömning hittades inte!", + "title": "Begär kotrollbedömning", "toasts": { - "loadingError": null + "loadingError": "Det gick inte att ladda uppgifter om kontrollbedömning!" } }, "evaluationOrderStatusPage": { "cancel": { - "heading": null, - "info": null + "heading": "Betalningen kunde inte genomföras", + "info": "Du kan försöka betala på nytt. Vi behandlar endast betalda begäranden om kontrollbedömning." }, "error": { - "heading": null, - "info": null + "heading": "Betalningsfel", + "info": "Betalningen kunde inte genomföras. Du kan försöka betala på nytt. " }, "success": { - "heading": null, - "body1": null, - "body2": null + "heading": "Du har begärt om kontrollbedömning!", + "body1": "Du har begärt om kontrollbedömning.", + "body2": "Du har fått en bekräftelse om din begäran om kontrollbedömning per e-post. E-posten innehåller också information om din avgift för kontrollbedömning. Om du inte har fått meddelandet, kolla din skräppostmapp." } }, "examDetailsPage": { "toasts": { - "notFound": null + "notFound": "YKI-test hittades inte" } }, "initRegistrationPage": { "toasts": { - "notFound": null + "notFound": "YKI-test hittades inte" } }, + "logoutSuccessPage": { + "heading": "Utloggning lyckades", + "info": "Du har loggat ut. Stäng alla fönster i webbläsaren." + }, + "notFoundPage": { + "description": "Kontrollera webbadress eller gå tillbaka till framsidan", + "title": "Sidan hittades inte" + }, "reassessmentPage": { "info": { "general": { @@ -438,6 +454,7 @@ "information": "Sök här för ett lämpligt YKI-test. Välj språk och färdighetsnivå. Tryck på VISA RESULTAT -knappen.", "selectExamDetails": { "prompt": "Välj examen", + "optional": "(valfritt)", "required": "(oblikatorisk)" } }, From 5423957c75e00c7a9d943298d4868473e3700384 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 28 Sep 2023 18:58:15 +0300 Subject: [PATCH 41/72] YKI(Frontend): Update translations (common.json) [deploy] --- .../yki/public/i18n/en-GB/common.json | 30 +++++---- .../yki/public/i18n/fi-FI/common.json | 14 ++--- .../yki/public/i18n/sv-SE/common.json | 61 ++++++++++++++++++- 3 files changed, 84 insertions(+), 21 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/common.json b/frontend/packages/yki/public/i18n/en-GB/common.json index 86dde4e26..4129868a1 100644 --- a/frontend/packages/yki/public/i18n/en-GB/common.json +++ b/frontend/packages/yki/public/i18n/en-GB/common.json @@ -30,7 +30,7 @@ "error": "An unexpected error has occurred.", "errors": { "customTextField": { - "dateFormat": "Input date in the format dd.mm.yyyy.", + "dateFormat": "Fill in as dd.mm.yyyy.", "emailFormat": "The email address is incorrect", "maxLength": "The text is too long", "required": "The information is mandatory", @@ -40,12 +40,12 @@ "loadingFailed": "Failed to download the information.", "mismatchingEmails": "The contents of the email address fields are different.", "registration": { - "alreadyRegistered": "Voit ilmoittautua vain yhteen tutkintotilaisuuteen.", - "examSessionFull": "Tutkintotilaisuus on täynnä.", - "formExpired": "Lomakkeen täyttöaika on umpeutunut.", - "paymentCreationFailed": "Lomakkeen lähetys epäonnistui.", - "personCreationFailed": "Lomakkeen lähetys epäonnistui.", - "registrationPeriodClosed": "Ilmoittautumisaika on päättynyt." + "alreadyRegistered": "You can register for only one YKI test.", + "examSessionFull": "The test is full.", + "formExpired": "Your registration form has expired.", + "paymentCreationFailed": "Sending the registration form failed.", + "personCreationFailed": "Sending the registration form failed.", + "registrationPeriodClosed": "The registration period has ended." } }, "examDate": "Test day", @@ -62,7 +62,7 @@ "female": "Female", "male": "Male", "other": "Other", - "preferNotToDisclose": "Prefer not to disclose" + "preferNotToDisclose": "Prefer not to respond" }, "header": { "accessibility": { @@ -99,15 +99,18 @@ "swe": "Swedish" }, "level": "Level", + "loadingContent": "Loading content", + "loadingDone": "Loading complete", "municipality": "Municipality", "navigationProtection": { - "description": "You will lose any changes you have made.", + "description": "All changes will be lost.", "header": "Are you sure you want to leave this page?" }, "next": "Next", "no": "No", "ophLogoAlt": "Logo: Finnish National Agency for Education.", - "placesAvailable": "Available places", + "ophLogoToFrontPageAlt": "The Finnish National Agency for Education - To the YKI registration page", + "placesAvailable": "Available seats", "postAdmission": "Post-registration", "price": "Price", "privacyStatement": { @@ -123,7 +126,12 @@ "reassessment": "Reassessment", "registration": "Registration", "registrationPeriod": "Registration period", - "yes": "Yes" + "specialArrangementsLink": "https://www.oph.fi/en/education-and-qualifications/registering-yki-test#anchor-do-you-need-special-arrangements", + "yes": "Yes", + "ykiHomepage": { + "ariaLabel": "Website of the National Certificates of Language Proficiency (oph.fi), open in new tab", + "link": "https://www.oph.fi/en/national-certificates-language-proficiency-yki" + } } } } diff --git a/frontend/packages/yki/public/i18n/fi-FI/common.json b/frontend/packages/yki/public/i18n/fi-FI/common.json index 37cdaf0d8..8ad2482e5 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/common.json +++ b/frontend/packages/yki/public/i18n/fi-FI/common.json @@ -5,7 +5,7 @@ "appNameAbbreviation": "YKI", "appTitle": "Yleiset kielitutkinnot | Opetushallitus", "back": "Takaisin", - "backToHomePage": "Palaa aloitussivulle", + "backToHomePage": "Takaisin aloitussivulle", "cancel": "Peruuta", "component": { "table": { @@ -30,7 +30,7 @@ "error": "Tapahtui odottamaton virhe.", "errors": { "customTextField": { - "dateFormat": "Syötä päivämäärä muodossa pp.kk.vvvv", + "dateFormat": "Kirjoita päivämäärä muodossa pp.kk.vvvv", "emailFormat": "Sähköpostiosoite on virheellinen", "maxLength": "Teksti on liian pitkä", "required": "Tieto on pakollinen", @@ -40,11 +40,11 @@ "loadingFailed": "Tietojen lataaminen epäonnistui.", "mismatchingEmails": "Sähköpostiosoitekenttien sisällöt eivät vastaa toisiaan", "registration": { - "alreadyRegistered": "Voit ilmoittautua vain yhteen tutkintotilaisuuteen.", - "examSessionFull": "Tutkintotilaisuus on täynnä.", - "formExpired": "Lomakkeen täyttöaika on umpeutunut.", - "paymentCreationFailed": "Lomakkeen lähetys epäonnistui.", - "personCreationFailed": "Lomakkeen lähetys epäonnistui.", + "alreadyRegistered": "Voit ilmoittautua vain yhteen YKI-testiin.", + "examSessionFull": "Tutkinto on täynnä", + "formExpired": "Ilmoittautumislomakkeen täyttöaika on umpeutunut.", + "paymentCreationFailed": "Ilmoittautumislomakkeen lähetys epäonnistui.", + "personCreationFailed": "Ilmoittautumislomakkeen lähetys epäonnistui.", "registrationPeriodClosed": "Ilmoittautumisaika on päättynyt." } }, diff --git a/frontend/packages/yki/public/i18n/sv-SE/common.json b/frontend/packages/yki/public/i18n/sv-SE/common.json index fd6d3bf91..663dbf830 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/common.json +++ b/frontend/packages/yki/public/i18n/sv-SE/common.json @@ -4,12 +4,50 @@ "actions": "Funktioner", "appNameAbbreviation": "YKI", "appTitle": "Allmänna språkexamina | Utbildningsstyrelsen", + "back": "Tillbaka", "backToHomePage": "Tillbaka till framsidan", + "cancel": "Avbryt", + "component": { + "table": { + "header": { + "searchResults": "Resultat ({{count}})", + "searchResultsAriaLabel_one": "Resultat (1 resultat)", + "searchResultsAriaLabel_other": "Resultat ({{count}} resultat)", + "searchResultsAriaLabel_zero": "Resultat (inga resultat)" + }, + "pagination": { + "backButtonLabel": "Tidigare sökresultat", + "displayedRowsLabel": "Visas {{from}}-{{to}} / {{count}} resultat", + "nextButtonLabel": "Följande sökresultat", + "rowsPerPage": "Resultat per sida" + } + } + }, "contactEmail": "kielitutkinnot@oph.fi", "dates": { "dateTimeFormat": "l [kl.] HH:mm" }, "error": "Ett oväntat fel uppstod.", + "errors": { + "customTextField": { + "dateFormat": "Ange datum i formatet dd.mm.åååå", + "emailFormat": "E-postadressen är felaktig", + "maxLength": "Texten är för lång", + "required": "Uppgiften är oblikatorisk", + "personalIdentityCodeFormat": "Personbeteckningensform kunde inte identifieras", + "telFormat": "Telefonummret är felaktigt" + }, + "loadingFailed": "Det gick inte att ladda uppgifterna.", + "mismatchingEmails": "Innehållet i fälten för e-postadress motsvarar inte varandra", + "registration": { + "alreadyRegistered": "Du kan anmäla dig endast till ett YKI-test.", + "examSessionFull": "Testet är fullbokat.", + "formExpired": "Tidsfrist för änmälningsblanketten har gått ut.", + "paymentCreationFailed": "Det gick inte att skicka anmälningsblankett.", + "personCreationFailed": "Det gick inte att skicka anmälningsblankett.", + "registrationPeriodClosed": "Anmälningstiden har slutat." + } + }, "examDate": "Testdag", "examParts": { "readingComprehension": "Textförståelse", @@ -19,11 +57,12 @@ }, "examSession": "Test", "examination": "Test", + "full": "Fullbokat", "gender": { "female": "Kvinna", "male": "Man", "other": "Annan", - "preferNotToDisclose": "Vill inte avslöja" + "preferNotToDisclose": "Vill ej svara" }, "header": { "accessibility": { @@ -39,6 +78,7 @@ "sv": "På svenska" } }, + "institution": "Testställe", "language": "Språk", "languageLevel": { "ALL": "alla nivåer", @@ -59,11 +99,19 @@ "swe": "svenska" }, "level": "Nivå", + "loadingContent": "Sidan laddas om", + "loadingDone": "Sidan omladdad", + "municipality": "Ort", "navigationProtection": { - "description": "Du förlorar ändringar du gjort.", + "description": "Du kommer att förlora dina ändringar.", "header": "Är du säker på att du vill lämna den här sidan?" }, + "next": "Följande", + "no": "Nej", "ophLogoAlt": "Logo: Opetushallitus/Utbildningsstyrelsen.", + "ophLogoToFrontPageAlt": "Opetushallitus/Utbildningsstyrelsen - Till anmälningssidan för YKI-testet", + "placesAvailable": "Lediga platser", + "postAdmission": "Efterhandsanmälan", "price": "Pris", "privacyStatement": { "description": "Vi följer EUs allmänna dataskyddsförordningen i behandling av personuppgifter.", @@ -76,7 +124,14 @@ "title": "Villkoren för behandling av personuppgifter" }, "reassessment": "Kontrollbedömning", - "registration": "Anmälning" + "registration": "Anmälning", + "registrationPeriod": "Anmälningstid", + "specialArrangementsLink": "https://www.oph.fi/sv/utbildning-och-examina/anmalan-till-yki-test#anchor-behover-du-specialarrangemang", + "yes": "Ja", + "ykiHomepage": { + "ariaLabel": "Webbplatsen för allmänna språkexamina (oph.fi), öppnas i en ny flik", + "link": "https://www.oph.fi/sv/allmanna-sprakexamina-yki" + } } } } From f27495ebc6f14594e6415326fee4958bca8ad598 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 28 Sep 2023 19:18:41 +0300 Subject: [PATCH 42/72] YKI(Frontend): Remove two leftover translations --- frontend/packages/yki/public/i18n/en-GB/public.json | 6 ++---- frontend/packages/yki/public/i18n/sv-SE/public.json | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index e3cc9bd27..d4095b7ae 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -286,13 +286,11 @@ "payment": { "cancel": { "description": "Your registration will be confirmed after you have paid the test fee.", - "heading": "Payment was cancelled", - "title": "You cancelled the payment" + "heading": "Payment was cancelled" }, "error": { "description": "You may try to pay again. Your registration will be confirmed after you have paid the test fee.", - "heading": "Payment error", - "title": "An error occurred when paying." + "heading": "Payment error" }, "success": { "whatsNext": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 9005eba79..1ba36101a 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -286,13 +286,11 @@ "payment": { "cancel": { "description": "Anmälning är bekräftat först då du har betalat examensavgiften.", - "heading": "Betalningen kunde inte genomföras", - "title": "Du har avbrytit betalningen" + "heading": "Betalningen kunde inte genomföras" }, "error": { "description": "Du kan försöka betala på nytt. Anmälning är bekräftat först då du har betalat examensavgiften.", - "heading": "Betalningsfel", - "title": "Det har uppstått ett fel i betalningen" + "heading": "Betalningsfel" }, "success": { "whatsNext": { From c1a3791e38de56bebaf64f2502f2b62c03aa4836 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 29 Sep 2023 09:44:15 +0300 Subject: [PATCH 43/72] YKI(Frontend): Latest changes [deploy] --- frontend/packages/yki/public/i18n/en-GB/public.json | 7 +++---- frontend/packages/yki/public/i18n/fi-FI/public.json | 3 +-- frontend/packages/yki/public/i18n/sv-SE/public.json | 3 +-- .../packages/yki/src/pages/EvaluationOrderStatusPage.tsx | 7 +------ 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index d4095b7ae..02337d5ca 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -347,10 +347,10 @@ }, "pages": { "evaluationOrderPage": { - "notFound": "Tarkistusarvioinnin tietoja ei löytynyt!", + "notFound": "Information about the reassessment was not found!", "title": "Request a reassessment", "toasts": { - "loadingError": "Tarkistusarvioinnin tietoja ei voitu ladata!" + "loadingError": "Information about the reassessment could not be loaded!" } }, "evaluationOrderStatusPage": { @@ -364,8 +364,7 @@ }, "success": { "heading": "You have requested a reassessment!", - "body1": "You have sent a request for reassessment.", - "body2": "You have received a confirmation of your request for reassessment by email. The email also contains information on the reassessment fee. If you have not received the email, please check your spam folder." + "body": "You have received a confirmation of your request for reassessment by email. The email also contains information on the reassessment fee. If you have not received the email, please check your spam folder." } }, "examDetailsPage": { diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index c5402c719..64b55ba4e 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -364,8 +364,7 @@ }, "success": { "heading": "Olet lähettänyt tarkistusarviointipyynnön!", - "body1": "Olet lähettänyt tarkistusarviointipyynnön.", - "body2": "Sinulle on lähetetty vahvistus tarkistusarviointipyynnöstäsi sähköpostilla. Sähköpostissa on myös kuitti tarkistusarviointimaksustasi. Jos et ole saanut viestiä, tarkista roskapostikansiosi." + "body": "Sinulle on lähetetty vahvistus tarkistusarviointipyynnöstäsi sähköpostilla. Sähköpostissa on myös kuitti tarkistusarviointimaksustasi. Jos et ole saanut viestiä, tarkista roskapostikansiosi." } }, "examDetailsPage": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 1ba36101a..7fa6d6b73 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -364,8 +364,7 @@ }, "success": { "heading": "Du har begärt om kontrollbedömning!", - "body1": "Du har begärt om kontrollbedömning.", - "body2": "Du har fått en bekräftelse om din begäran om kontrollbedömning per e-post. E-posten innehåller också information om din avgift för kontrollbedömning. Om du inte har fått meddelandet, kolla din skräppostmapp." + "body": "Du har fått en bekräftelse om din begäran om kontrollbedömning per e-post. E-posten innehåller också information om din avgift för kontrollbedömning. Om du inte har fått meddelandet, kolla din skräppostmapp." } }, "examDetailsPage": { diff --git a/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx b/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx index b996a9b1f..860739ac5 100644 --- a/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx +++ b/frontend/packages/yki/src/pages/EvaluationOrderStatusPage.tsx @@ -79,12 +79,7 @@ const Success = () => { return ( - {t('body1')} - {t('body2')} - - } + contents={{t('body')}} showEvaluationOrderDetails={true} /> ); From d711306684009f26dd6cdcfd01d78b9bb592dec2 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 29 Sep 2023 12:51:44 +0300 Subject: [PATCH 44/72] YKI(Frontend): Use consistent capitalization for ExamSession municipality. Sort stored municipalities. --- .../registration/PublicRegistrationExamSessionDetails.tsx | 4 +++- .../examSession/PublicExamSessionListingRow.tsx | 8 ++++---- frontend/packages/yki/src/redux/reducers/examSessions.ts | 6 ++++-- frontend/packages/yki/src/redux/selectors/examSessions.ts | 5 +++-- frontend/packages/yki/src/utils/examSession.ts | 7 +++++++ 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/frontend/packages/yki/src/components/registration/PublicRegistrationExamSessionDetails.tsx b/frontend/packages/yki/src/components/registration/PublicRegistrationExamSessionDetails.tsx index 0442aacde..d246f68b6 100644 --- a/frontend/packages/yki/src/components/registration/PublicRegistrationExamSessionDetails.tsx +++ b/frontend/packages/yki/src/components/registration/PublicRegistrationExamSessionDetails.tsx @@ -47,7 +47,9 @@ export const PublicRegistrationExamSessionDetails = ({ {`${translateCommon('institution')}: `} - {`${location.name}, ${location.street_address}, ${location.post_office}`} + {`${location.name}, ${ + location.street_address + }, ${ExamSessionUtils.getMunicipality(location)}`} {`${t('registrationTime')}: `} diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx index d7f325eea..6372faf3b 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx @@ -126,9 +126,9 @@ const PublicExamSessionListingCellsForDesktop = ({ {DateUtils.formatOptionalDate(examSession.session_date, 'l')} - {locationInfo?.name} + {locationInfo.name}
- {locationInfo?.post_office} + {ExamSessionUtils.getMunicipality(locationInfo)}
@@ -173,9 +173,9 @@ const PublicExamSessionListingCellsForPhone = ({ {translateCommon('institution')}
- {locationInfo?.name} + {locationInfo.name}
- {locationInfo?.post_office} + {ExamSessionUtils.getMunicipality(locationInfo)}
{translateCommon('registrationPeriod')} diff --git a/frontend/packages/yki/src/redux/reducers/examSessions.ts b/frontend/packages/yki/src/redux/reducers/examSessions.ts index a1865c1bc..a93ca115e 100644 --- a/frontend/packages/yki/src/redux/reducers/examSessions.ts +++ b/frontend/packages/yki/src/redux/reducers/examSessions.ts @@ -38,12 +38,14 @@ const examSessionsSlice = createSlice({ ExamSessionUtils.compareExamSessions(es1, es2) ); const uniqueMunicipalities = new Set( - examSessions.map((es) => es.location[0].post_office) + examSessions.map((es) => + ExamSessionUtils.getMunicipality(es.location[0]) + ) ); state.status = APIResponseStatus.Success; state.exam_sessions = examSessions; - state.municipalities = Array.from(uniqueMunicipalities); + state.municipalities = Array.from(uniqueMunicipalities).sort(); }, setPublicExamSessionFilters( state, diff --git a/frontend/packages/yki/src/redux/selectors/examSessions.ts b/frontend/packages/yki/src/redux/selectors/examSessions.ts index 30c80aa0a..3e70ede4e 100644 --- a/frontend/packages/yki/src/redux/selectors/examSessions.ts +++ b/frontend/packages/yki/src/redux/selectors/examSessions.ts @@ -31,9 +31,10 @@ const filterExamSessions = ( } if (filters.municipality) { - // TODO Does reaching to index 0 always work? The post_offices *should* be the same between different locations... filteredData = filteredData.filter( - (es) => es.location[0].post_office === filters.municipality + (es) => + ExamSessionUtils.getMunicipality(es.location[0]) === + filters.municipality ); } diff --git a/frontend/packages/yki/src/utils/examSession.ts b/frontend/packages/yki/src/utils/examSession.ts index b22d858b4..4c002a4a8 100644 --- a/frontend/packages/yki/src/utils/examSession.ts +++ b/frontend/packages/yki/src/utils/examSession.ts @@ -1,5 +1,6 @@ import dayjs, { Dayjs } from 'dayjs'; import { AppLanguage } from 'shared/enums'; +import { StringUtils } from 'shared/utils'; import { translateOutsideComponent } from 'configs/i18n'; import { ExamLanguage, ExamLevel, RegistrationKind } from 'enums/app'; @@ -247,4 +248,10 @@ export class ExamSessionUtils { }; } } + + static getMunicipality(location: ExamSessionLocation) { + return StringUtils.capitalize( + StringUtils.trimAndLowerCase(location.post_office) + ); + } } From a2a92dd08e9285d2063dc75feb0e46b7d0c45404 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 29 Sep 2023 13:01:12 +0300 Subject: [PATCH 45/72] YKI(Frontend): Update test snapshots --- .../__snapshots__/PublicExamSessionListing.test.tsx.snap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap index eb54f3405..875f45f98 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap @@ -614,7 +614,7 @@ Array [ > Jyväskylän kansalaisopisto
- JYVÄSKYLÄ + Jyväskylä Helsingin aikuisopisto
- HELSINKI + Helsinki Helsingin aikuisopisto
- HELSINKI + Helsinki Date: Tue, 3 Oct 2023 14:15:24 +0300 Subject: [PATCH 46/72] YKI(Frontend): Sort strings according to 'fi' locale --- frontend/packages/yki/src/redux/reducers/examSessions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/redux/reducers/examSessions.ts b/frontend/packages/yki/src/redux/reducers/examSessions.ts index a93ca115e..b65bb780f 100644 --- a/frontend/packages/yki/src/redux/reducers/examSessions.ts +++ b/frontend/packages/yki/src/redux/reducers/examSessions.ts @@ -45,7 +45,9 @@ const examSessionsSlice = createSlice({ state.status = APIResponseStatus.Success; state.exam_sessions = examSessions; - state.municipalities = Array.from(uniqueMunicipalities).sort(); + state.municipalities = Array.from(uniqueMunicipalities).sort( + new Intl.Collator('fi').compare + ); }, setPublicExamSessionFilters( state, From 1c45da50ec52e01133e9265a3c9fd9bbece23866 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Mon, 2 Oct 2023 14:44:04 +0300 Subject: [PATCH 47/72] YKI:VKT(Frontend): Restructure accessibility statement page to be more accessible [deploy] --- .../src/pages/AccessibilityStatementPage.tsx | 42 ++++++++++--------- .../src/pages/AccessibilityStatementPage.tsx | 42 ++++++++++--------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx b/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx index 64a3c2059..35f0750fa 100644 --- a/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx +++ b/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx @@ -44,16 +44,18 @@ const ItemBulletList = ({ item: string; bulletPoints: Array; }) => ( - -
    -
  • {item}
  • -
      - {bulletPoints.map((bulletPoint, i) => ( -
    • {bulletPoint}
    • - ))} -
    + <> + + {item} + +
      + {bulletPoints.map((bulletPoint, i) => ( + + {bulletPoint} + + ))}
    - + ); export const AccessibilityStatementPage = () => { @@ -102,16 +104,18 @@ export const AccessibilityStatementPage = () => { {translateAccessibility('content.nonAccessible.description2')} - {caveats.map(({}, i) => ( - - ))} +
      + {caveats.map(({}, i) => ( + + ))} +

{translateAccessibility('content.composition.title')}

diff --git a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx index 34e0d230a..e89ad759d 100644 --- a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx +++ b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx @@ -44,16 +44,18 @@ const ItemBulletList = ({ item: string; bulletPoints: Array; }) => ( - -
    -
  • {item}
  • -
      - {bulletPoints.map((bulletPoint, i) => ( -
    • {bulletPoint}
    • - ))} -
    + <> + + {item} + +
      + {bulletPoints.map((bulletPoint, i) => ( + + {bulletPoint} + + ))}
    - + ); export const AccessibilityStatementPage = () => { @@ -105,16 +107,18 @@ export const AccessibilityStatementPage = () => {
    {translateAccessibility('content.nonAccessible.description2')} - {caveats.map(({}, i) => ( - - ))} +
      + {caveats.map(({}, i) => ( + + ))} +

{translateAccessibility('content.composition.title')}

From 1d1d617e85b69ebbde112ab0cf198e872cde33f7 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 3 Oct 2023 14:43:45 +0300 Subject: [PATCH 48/72] YKI:VKT(Frontend): Fix bullet point nesting --- .../packages/vkt/src/pages/AccessibilityStatementPage.tsx | 8 +++----- .../packages/yki/src/pages/AccessibilityStatementPage.tsx | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx b/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx index 35f0750fa..ecaee64f8 100644 --- a/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx +++ b/frontend/packages/vkt/src/pages/AccessibilityStatementPage.tsx @@ -44,10 +44,8 @@ const ItemBulletList = ({ item: string; bulletPoints: Array; }) => ( - <> - - {item} - + + {item}
    {bulletPoints.map((bulletPoint, i) => ( @@ -55,7 +53,7 @@ const ItemBulletList = ({ ))}
- +
); export const AccessibilityStatementPage = () => { diff --git a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx index e89ad759d..07b690ad7 100644 --- a/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx +++ b/frontend/packages/yki/src/pages/AccessibilityStatementPage.tsx @@ -44,10 +44,8 @@ const ItemBulletList = ({ item: string; bulletPoints: Array; }) => ( - <> - - {item} - + + {item}
    {bulletPoints.map((bulletPoint, i) => ( @@ -55,7 +53,7 @@ const ItemBulletList = ({ ))}
- +
); export const AccessibilityStatementPage = () => { From 6e09bcb754258cd68cccc092aa2a6e7b433a920d Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Mon, 2 Oct 2023 15:26:28 +0300 Subject: [PATCH 49/72] SHARED(Frontend): LangSelector mobile style fixes --- frontend/packages/shared/CHANGELOG.MD | 6 ++++++ frontend/packages/shared/package.json | 2 +- .../shared/src/components/LangSelector/LangSelector.scss | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/packages/shared/CHANGELOG.MD b/frontend/packages/shared/CHANGELOG.MD index 36aa24e1a..2840e8e8a 100644 --- a/frontend/packages/shared/CHANGELOG.MD +++ b/frontend/packages/shared/CHANGELOG.MD @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Released] +## [1.9.26] - 2023-10-02 + +### Changed + +- Ensure LangSelector options are not uppercased, whether on mobile or on desktop + ## [1.9.25] - 2023-09-22 ### Added diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index 853dca4ac..c0a43af17 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@opetushallitus/kieli-ja-kaantajatutkinnot.shared", - "version": "1.9.25", + "version": "1.9.26", "description": "Shared Frontend Package", "exports": { "./components": "./src/components/index.tsx", diff --git a/frontend/packages/shared/src/components/LangSelector/LangSelector.scss b/frontend/packages/shared/src/components/LangSelector/LangSelector.scss index fa62e4b91..f27c4e92e 100644 --- a/frontend/packages/shared/src/components/LangSelector/LangSelector.scss +++ b/frontend/packages/shared/src/components/LangSelector/LangSelector.scss @@ -5,6 +5,10 @@ align-items: center; display: flex; + p { + text-transform: none; + } + @include mixins.not-phone { margin-left: 1.5rem; } From a57d3f62c7000c2e83c48fdb4362a1f75706660c Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Mon, 2 Oct 2023 15:28:17 +0300 Subject: [PATCH 50/72] AKR:OTR:VKT:YKI(Frontend): Take new LangSelector styles into use --- frontend/packages/akr/package.json | 2 +- frontend/packages/otr/package.json | 2 +- frontend/packages/vkt/package.json | 2 +- frontend/packages/yki/package.json | 2 +- frontend/yarn.lock | 17 +++++------------ 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/frontend/packages/akr/package.json b/frontend/packages/akr/package.json index 898c18b26..c7f4a8fd7 100644 --- a/frontend/packages/akr/package.json +++ b/frontend/packages/akr/package.json @@ -22,6 +22,6 @@ "akr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" } } diff --git a/frontend/packages/otr/package.json b/frontend/packages/otr/package.json index e86c52ecb..78f4942c8 100644 --- a/frontend/packages/otr/package.json +++ b/frontend/packages/otr/package.json @@ -25,6 +25,6 @@ "otr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" } } diff --git a/frontend/packages/vkt/package.json b/frontend/packages/vkt/package.json index 03d962b81..143218088 100644 --- a/frontend/packages/vkt/package.json +++ b/frontend/packages/vkt/package.json @@ -26,6 +26,6 @@ }, "dependencies": { "reduxjs-toolkit-persist": "^7.2.1", - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" } } diff --git a/frontend/packages/yki/package.json b/frontend/packages/yki/package.json index b984b9114..bf7504dbb 100644 --- a/frontend/packages/yki/package.json +++ b/frontend/packages/yki/package.json @@ -26,7 +26,7 @@ "yki:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.25" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" }, "devDependencies": { "multer": "^1.4.5-lts.1" diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9184d5d81..11bbf375c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2890,7 +2890,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.akr@workspace:packages/akr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" languageName: unknown linkType: soft @@ -2898,7 +2898,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.otr@workspace:packages/otr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" languageName: unknown linkType: soft @@ -2990,7 +2990,7 @@ __metadata: languageName: unknown linkType: soft -"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.25": +"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26": version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared" languageName: unknown @@ -3001,7 +3001,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.vkt@workspace:packages/vkt" dependencies: reduxjs-toolkit-persist: ^7.2.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" languageName: unknown linkType: soft @@ -3010,7 +3010,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.yki@workspace:packages/yki" dependencies: multer: ^1.4.5-lts.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.25" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" languageName: unknown linkType: soft @@ -12606,13 +12606,6 @@ __metadata: languageName: node linkType: hard -"shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.24": - version: 1.9.24 - resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@npm:1.9.24::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40Opetushallitus%2Fkieli-ja-kaantajatutkinnot.shared%2F1.9.24%2Fafaac13abda64defafbedb20b005d6f78a25688a" - checksum: 49b6b3edbda4ed59070394a881fcf9db2c6cb6aa90cffb3396e497bd835f611806dfee4e9ed31d0b251543cafa3c5cbb3ddb43b169d7e33f84f8c77b9e63ec64 - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" From 4e2fef8019bd4044f2e6aa929156fb1f538bd8d4 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Mon, 2 Oct 2023 15:37:18 +0300 Subject: [PATCH 51/72] YKI(Frontend): Indicate required instead of optional fields --- .../yki/public/i18n/en-GB/public.json | 1 - .../yki/public/i18n/fi-FI/public.json | 1 - .../yki/public/i18n/sv-SE/public.json | 1 - .../PublicExamSessionListingFilters.tsx | 20 +++++++++---------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index 02337d5ca..e4c36a8b2 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -451,7 +451,6 @@ "information": "Search for YKI-tests by choosing the language and proficiency level. Click the SHOW RESULTS button to see the results.", "selectExamDetails": { "prompt": "Choose test", - "optional": "(optional)", "required": "(required)" } }, diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 64b55ba4e..9f0c1446b 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -451,7 +451,6 @@ "information": "Hae tästä sopivaa YKI-testiä. Valitse kieli ja taitotaso. Paina NÄYTÄ TULOKSET -painiketta.", "selectExamDetails": { "prompt": "Valitse tutkinto", - "optional": "(valinnainen)", "required": "(pakollinen)" } }, diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 7fa6d6b73..58c4eca72 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -451,7 +451,6 @@ "information": "Sök här för ett lämpligt YKI-test. Välj språk och färdighetsnivå. Tryck på VISA RESULTAT -knappen.", "selectExamDetails": { "prompt": "Välj examen", - "optional": "(valfritt)", "required": "(oblikatorisk)" } }, diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx index d4410c992..010b75c47 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx @@ -132,7 +132,10 @@ export const PublicExamSessionFilters = ({ htmlFor="public-exam-session-filters__language-filter" sx={showError && !language ? { color: 'error.main' } : {}} > - {translateCommon('language')} + {translateCommon('language')}{' '} + + {t('filters.selectExamDetails.required')} + - {translateCommon('level')} + {translateCommon('level')}{' '} + + {t('filters.selectExamDetails.required')} + - {t('labels.selectMunicipality')}{' '} - - {t('filters.selectExamDetails.optional')} - + {t('labels.selectMunicipality')} - {t('labels.filterExamSessions')}{' '} - - {t('filters.selectExamDetails.optional')} - + {t('labels.filterExamSessions')} Date: Mon, 2 Oct 2023 16:25:37 +0300 Subject: [PATCH 52/72] YKI(Frontend): Remove Clear results button --- .../yki/public/i18n/en-GB/public.json | 1 - .../yki/public/i18n/fi-FI/public.json | 1 - .../yki/public/i18n/sv-SE/public.json | 1 - .../PublicExamSessionListingFilters.tsx | 26 +------------------ .../yki/src/redux/reducers/examSessions.ts | 4 --- .../registration/_exam-session-filters.scss | 5 ---- .../public_registration_page.spec.ts | 2 -- .../page-objects/publicRegistrationPage.ts | 5 ---- 8 files changed, 1 insertion(+), 44 deletions(-) diff --git a/frontend/packages/yki/public/i18n/en-GB/public.json b/frontend/packages/yki/public/i18n/en-GB/public.json index e4c36a8b2..d5137e69e 100644 --- a/frontend/packages/yki/public/i18n/en-GB/public.json +++ b/frontend/packages/yki/public/i18n/en-GB/public.json @@ -437,7 +437,6 @@ }, "filters": { "buttons": { - "empty": "Clear selection", "showResults": "Show results ({{count}})" }, "errorDialog": { diff --git a/frontend/packages/yki/public/i18n/fi-FI/public.json b/frontend/packages/yki/public/i18n/fi-FI/public.json index 9f0c1446b..eda15c860 100644 --- a/frontend/packages/yki/public/i18n/fi-FI/public.json +++ b/frontend/packages/yki/public/i18n/fi-FI/public.json @@ -437,7 +437,6 @@ }, "filters": { "buttons": { - "empty": "Tyhjennä valinnat", "showResults": "Näytä tulokset ({{count}})" }, "errorDialog": { diff --git a/frontend/packages/yki/public/i18n/sv-SE/public.json b/frontend/packages/yki/public/i18n/sv-SE/public.json index 58c4eca72..3f890f42c 100644 --- a/frontend/packages/yki/public/i18n/sv-SE/public.json +++ b/frontend/packages/yki/public/i18n/sv-SE/public.json @@ -437,7 +437,6 @@ }, "filters": { "buttons": { - "empty": "Radera allt", "showResults": "Visa resultat ({{count}})" }, "errorDialog": { diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx index 010b75c47..19b89072e 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx @@ -22,10 +22,7 @@ import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { ExamLanguage, ExamLevel } from 'enums/app'; import { ExamSessionFilters } from 'interfaces/examSessions'; -import { - resetPublicExamSessionFilters, - setPublicExamSessionFilters, -} from 'redux/reducers/examSessions'; +import { setPublicExamSessionFilters } from 'redux/reducers/examSessions'; import { examSessionsSelector, selectFilteredPublicExamSessions, @@ -33,7 +30,6 @@ import { export const PublicExamSessionFilters = ({ onApplyFilters, - onEmptyFilters, }: { onApplyFilters: () => void; onEmptyFilters: () => void; @@ -46,12 +42,6 @@ export const PublicExamSessionFilters = ({ const { showDialog } = useDialog(); const filtersGridRef = useRef(null); - const scrollToSearch = () => { - filtersGridRef.current?.scrollIntoView({ - block: 'end', - inline: 'nearest', - }); - }; const { filters, municipalities } = useAppSelector(examSessionsSelector); const filteredExamSessions = useAppSelector(selectFilteredPublicExamSessions); @@ -70,12 +60,6 @@ export const PublicExamSessionFilters = ({ const [showError, setShowError] = useState(false); - const handleEmptyBtnClick = () => { - dispatch(resetPublicExamSessionFilters()); - onEmptyFilters(); - scrollToSearch(); - }; - const handleSubmitBtnClick = () => { if (!filters.language || !filters.level) { setShowError(true); @@ -250,14 +234,6 @@ export const PublicExamSessionFilters = ({
- - {t('filters.buttons.empty')} - { }); it('can filter by current availability', () => { - onPublicRegistrationPage.clearAll(); onPublicRegistrationPage.selectExamLanguage('kaikki kielet'); onPublicRegistrationPage.selectExamLevel('kaikki tasot'); onPublicRegistrationPage.toggleShowOnlyIfAvailablePlaces(); @@ -39,7 +38,6 @@ describe('PublicRegistrationPage', () => { }); it('can filter by exam language and level', () => { - onPublicRegistrationPage.clearAll(); onPublicRegistrationPage.selectExamLanguage('suomi'); onPublicRegistrationPage.selectExamLevel('kaikki tasot'); onPublicRegistrationPage.expectResultsCount(9); diff --git a/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts b/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts index e747305da..97e117f97 100644 --- a/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts +++ b/frontend/packages/yki/src/tests/cypress/support/page-objects/publicRegistrationPage.ts @@ -2,7 +2,6 @@ import { selectComboBoxOptionByName } from 'tests/cypress/support/utils/comboBox class PublicRegistrationPage { elements = { - clearAllButton: () => cy.contains('Tyhjennä valinnat'), filterByLanguage: () => cy.findByRole('combobox', { name: /Valitse kieli/ }), filterByLevel: () => @@ -19,10 +18,6 @@ class PublicRegistrationPage { title: () => cy.findByTestId('public-registration-page__title-heading'), }; - clearAll() { - this.elements.clearAllButton().click(); - } - expectResultsCount(count: number) { this.elements .showResultsButton() From 71374a6a4d83edd759c42028c617e6241cce418b Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Mon, 2 Oct 2023 16:36:08 +0300 Subject: [PATCH 53/72] YKI(Frontend): Render admission period on two lines --- .../examSession/PublicExamSessionListingRow.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx index 6372faf3b..3a4c12c62 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx @@ -85,9 +85,13 @@ const renderAdmissionPeriod = ({ start: Dayjs; end: Dayjs; }) => { - return `${DateTimeUtils.renderDateTime( - start - )} — ${DateTimeUtils.renderDateTime(end)}`; + return ( + <> + {DateTimeUtils.renderDateTime(start)} — +
+ {DateTimeUtils.renderDateTime(end)} + + ); }; const AdmissionPeriodText = ({ examSession }: { examSession: ExamSession }) => { From 7a4605c6728e95b4c04ee114ffaa472bdfc35fdc Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Mon, 2 Oct 2023 16:48:50 +0300 Subject: [PATCH 54/72] YKI(Frontend): Update snapshots [deploy] --- .../PublicExamSessionListing.test.tsx.snap | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap index 875f45f98..2d9b18f0c 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap @@ -233,7 +233,10 @@ Array [ postAdmission :
- yki.co00on.4amte0.4amteTi0eFor0amt — yki.co00on.2pmte0.2pmteTi0eFor0pmt + yki.co00on.4amte0.4amteTi0eFor0amt + — +
+ yki.co00on.2pmte0.2pmteTi0eFor0pmt - yki.co00on.2amte0.2amteTi0eFor0amt — yki.co00on.0pmte0.0pmteTi0eFor0pmt + yki.co00on.2amte0.2amteTi0eFor0amt + — +
+ yki.co00on.0pmte0.0pmteTi0eFor0pmt - yki.co00on.0amte0.0amteTi0eFor0amt — yki.co00on.6pmte0.6pmteTi0eFor0pmt + yki.co00on.0amte0.0amteTi0eFor0amt + — +
+ yki.co00on.6pmte0.6pmteTi0eFor0pmt - yki.co00on.3amte0.3amteTi0eFor0amt — yki.co00on.3pmte0.3pmteTi0eFor0pmt + yki.co00on.3amte0.3amteTi0eFor0amt + — +
+ yki.co00on.3pmte0.3pmteTi0eFor0pmt - yki.co00on.5amte0.5amteTi0eFor0amt — yki.co00on.1pmte0.1pmteTi0eFor0pmt + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt - yki.co00on.5amte0.5amteTi0eFor0amt — yki.co00on.1pmte0.1pmteTi0eFor0pmt + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt - yki.co00on.5amte0.5amteTi0eFor0amt — yki.co00on.3pmte0.3pmteTi0eFor0pmt + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.3pmte0.3pmteTi0eFor0pmt - yki.co00on.5amte0.5amteTi0eFor0amt — yki.co00on.1pmte0.1pmteTi0eFor0pmt + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt - yki.co00on.5amte0.5amteTi0eFor0amt — yki.co00on.1pmte0.1pmteTi0eFor0pmt + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt - yki.co00on.0amte0.0amteTi0eFor0amt — yki.co00on.3pmte0.3pmteTi0eFor0pmt + yki.co00on.0amte0.0amteTi0eFor0amt + — +
+ yki.co00on.3pmte0.3pmteTi0eFor0pmt Date: Thu, 5 Oct 2023 15:17:21 +0300 Subject: [PATCH 55/72] YKI(Frontend): Review fixes: remove unneeded prop, add aria-label --- .../PublicExamSessionListingFilters.tsx | 6 +- .../PublicExamSessionListingRow.tsx | 11 +- .../yki/src/pages/RegistrationPage.tsx | 9 +- .../PublicExamSessionListing.test.tsx.snap | 120 ++++++++++++------ 4 files changed, 90 insertions(+), 56 deletions(-) diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx index 19b89072e..edf15baa0 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx @@ -7,7 +7,7 @@ import { FormGroup, Typography, } from '@mui/material'; -import { useRef, useState } from 'react'; +import { useState } from 'react'; import { AutocompleteValue, ComboBox, @@ -32,7 +32,6 @@ export const PublicExamSessionFilters = ({ onApplyFilters, }: { onApplyFilters: () => void; - onEmptyFilters: () => void; }) => { // I18 const translateCommon = useCommonTranslation(); @@ -41,7 +40,6 @@ export const PublicExamSessionFilters = ({ }); const { showDialog } = useDialog(); - const filtersGridRef = useRef(null); const { filters, municipalities } = useAppSelector(examSessionsSelector); const filteredExamSessions = useAppSelector(selectFilteredPublicExamSessions); @@ -97,7 +95,7 @@ export const PublicExamSessionFilters = ({ const errorStyles = { color: 'error.main' }; return ( -
+
diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx index 3a4c12c62..e4e734263 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx @@ -85,12 +85,15 @@ const renderAdmissionPeriod = ({ start: Dayjs; end: Dayjs; }) => { + const startTimeStr = DateTimeUtils.renderDateTime(start); + const endTimeStr = DateTimeUtils.renderDateTime(end); + return ( - <> - {DateTimeUtils.renderDateTime(start)} — + + {startTimeStr} —
- {DateTimeUtils.renderDateTime(end)} - + {endTimeStr} +
); }; diff --git a/frontend/packages/yki/src/pages/RegistrationPage.tsx b/frontend/packages/yki/src/pages/RegistrationPage.tsx index c96a56395..02815116f 100644 --- a/frontend/packages/yki/src/pages/RegistrationPage.tsx +++ b/frontend/packages/yki/src/pages/RegistrationPage.tsx @@ -29,10 +29,6 @@ export const RegistrationPage: FC = () => { setResults(filteredExamSessions); setShowResults(true); }; - const onEmptyFilters = () => { - setResults(exam_sessions); - setShowResults(false); - }; useEffect(() => { if (status === APIResponseStatus.NotStarted) { @@ -101,10 +97,7 @@ export const RegistrationPage: FC = () => { > {t('filters.information')} - + {showResults && ( diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap index 2d9b18f0c..beef61b1a 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap @@ -233,10 +233,14 @@ Array [ postAdmission :
- yki.co00on.4amte0.4amteTi0eFor0amt - — -
- yki.co00on.2pmte0.2pmteTi0eFor0pmt + + yki.co00on.4amte0.4amteTi0eFor0amt + — +
+ yki.co00on.2pmte0.2pmteTi0eFor0pmt +
- yki.co00on.2amte0.2amteTi0eFor0amt - — -
- yki.co00on.0pmte0.0pmteTi0eFor0pmt + + yki.co00on.2amte0.2amteTi0eFor0amt + — +
+ yki.co00on.0pmte0.0pmteTi0eFor0pmt +
- yki.co00on.0amte0.0amteTi0eFor0amt - — -
- yki.co00on.6pmte0.6pmteTi0eFor0pmt + + yki.co00on.0amte0.0amteTi0eFor0amt + — +
+ yki.co00on.6pmte0.6pmteTi0eFor0pmt +
- yki.co00on.3amte0.3amteTi0eFor0amt - — -
- yki.co00on.3pmte0.3pmteTi0eFor0pmt + + yki.co00on.3amte0.3amteTi0eFor0amt + — +
+ yki.co00on.3pmte0.3pmteTi0eFor0pmt +
- yki.co00on.5amte0.5amteTi0eFor0amt - — -
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt +
- yki.co00on.5amte0.5amteTi0eFor0amt - — -
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt +
- yki.co00on.5amte0.5amteTi0eFor0amt - — -
- yki.co00on.3pmte0.3pmteTi0eFor0pmt + + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.3pmte0.3pmteTi0eFor0pmt +
- yki.co00on.5amte0.5amteTi0eFor0amt - — -
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt +
- yki.co00on.5amte0.5amteTi0eFor0amt - — -
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + + yki.co00on.5amte0.5amteTi0eFor0amt + — +
+ yki.co00on.1pmte0.1pmteTi0eFor0pmt +
- yki.co00on.0amte0.0amteTi0eFor0amt - — -
- yki.co00on.3pmte0.3pmteTi0eFor0pmt + + yki.co00on.0amte0.0amteTi0eFor0amt + — +
+ yki.co00on.3pmte0.3pmteTi0eFor0pmt +
Date: Tue, 3 Oct 2023 18:50:06 +0300 Subject: [PATCH 56/72] SHARED(Frontend): Use timezone plugin for Dayjs --- frontend/packages/shared/CHANGELOG.MD | 6 ++++++ frontend/packages/shared/package.json | 2 +- frontend/packages/shared/src/utils/date/date.ts | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/packages/shared/CHANGELOG.MD b/frontend/packages/shared/CHANGELOG.MD index 2840e8e8a..31981b6f7 100644 --- a/frontend/packages/shared/CHANGELOG.MD +++ b/frontend/packages/shared/CHANGELOG.MD @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Released] +## [1.9.27] - 2023-10-03 + +### Changed + +- Use the timezone plugin with Dayjs + ## [1.9.26] - 2023-10-02 ### Changed diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index c0a43af17..a7e4759e6 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@opetushallitus/kieli-ja-kaantajatutkinnot.shared", - "version": "1.9.26", + "version": "1.9.27", "description": "Shared Frontend Package", "exports": { "./components": "./src/components/index.tsx", diff --git a/frontend/packages/shared/src/utils/date/date.ts b/frontend/packages/shared/src/utils/date/date.ts index 47016101f..ec4a86c14 100644 --- a/frontend/packages/shared/src/utils/date/date.ts +++ b/frontend/packages/shared/src/utils/date/date.ts @@ -1,6 +1,8 @@ import dayjs, { Dayjs } from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat'; import localizedFormat from 'dayjs/plugin/localizedFormat'; +import timezone from 'dayjs/plugin/timezone'; +import utc from 'dayjs/plugin/utc'; import 'dayjs/locale/fi'; import 'dayjs/locale/sv-fi'; @@ -9,6 +11,8 @@ import { AppLanguage } from '../../enums'; dayjs.extend(localizedFormat); dayjs.extend(customParseFormat); +dayjs.extend(utc); +dayjs.extend(timezone); export class DateUtils { static setDayjsLocale(locale: AppLanguage) { From b6d0656d89830403e1376730ed0a48ef60a18888 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 3 Oct 2023 18:58:35 +0300 Subject: [PATCH 57/72] YKI(Frontend): Take user's timezone into account when checking if admission period is open. --- frontend/packages/yki/package.json | 2 +- .../PublicExamSessionListingRow.tsx | 6 ++- .../packages/yki/src/utils/examSession.ts | 50 ++++++------------- .../packages/yki/src/utils/serialization.ts | 26 ++++++++-- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/frontend/packages/yki/package.json b/frontend/packages/yki/package.json index bf7504dbb..144133174 100644 --- a/frontend/packages/yki/package.json +++ b/frontend/packages/yki/package.json @@ -26,7 +26,7 @@ "yki:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" }, "devDependencies": { "multer": "^1.4.5-lts.1" diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx index e4e734263..be10e3f24 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingRow.tsx @@ -85,8 +85,10 @@ const renderAdmissionPeriod = ({ start: Dayjs; end: Dayjs; }) => { - const startTimeStr = DateTimeUtils.renderDateTime(start); - const endTimeStr = DateTimeUtils.renderDateTime(end); + const startTimeStr = DateTimeUtils.renderDateTime( + start.tz('Europe/Helsinki') + ); + const endTimeStr = DateTimeUtils.renderDateTime(end.tz('Europe/Helsinki')); return ( diff --git a/frontend/packages/yki/src/utils/examSession.ts b/frontend/packages/yki/src/utils/examSession.ts index 4c002a4a8..5376a84c4 100644 --- a/frontend/packages/yki/src/utils/examSession.ts +++ b/frontend/packages/yki/src/utils/examSession.ts @@ -162,29 +162,20 @@ export class ExamSessionUtils { static hasRegistrationStarted(examSession: ExamSession, now: Dayjs) { const { registration_start_date } = examSession; - // TODO Consider timezones! Registration opening / closing times are supposed to be - // wrt. Finnish times, but user can be on a different timezone. - const registrationOpensAt = registration_start_date.hour(10); - - return registrationOpensAt.isBefore(now); + // Note: need to convert to utc because of poor support in Dayjs + // for comparing times across timezones + return registration_start_date.utc().isBefore(now.utc()); } static hasRegistrationEnded(examSession: ExamSession, now: Dayjs) { const { registration_end_date } = examSession; - // TODO Consider timezones! Registration opening / closing times are supposed to be - // wrt. Finnish times, but user can be on a different timezone. - const registrationClosesAt = registration_end_date.hour(16); - - return registrationClosesAt.isBefore(now); + return registration_end_date.utc().isBefore(now.utc()); } static hasPostAdmissionStarted(examSession: ExamSession, now: Dayjs) { if (examSession.post_admission_start_date) { - const postAdmissionOpensAt = - examSession.post_admission_start_date.hour(10); - - return postAdmissionOpensAt.isBefore(now); + return examSession.post_admission_start_date.utc().isBefore(now.utc()); } return false; @@ -192,10 +183,7 @@ export class ExamSessionUtils { static hasPostAdmissionEnded(examSession: ExamSession, now: Dayjs) { if (examSession.post_admission_end_date) { - const postAdmissionClosesAt = - examSession.post_admission_end_date.hour(16); - - return postAdmissionClosesAt.isBefore(now); + return examSession.post_admission_end_date.utc().isBefore(now.utc()); } return false; @@ -203,8 +191,6 @@ export class ExamSessionUtils { static getEffectiveRegistrationPeriodDetails(examSession: ExamSession) { const now = dayjs(); - const registrationOpensAt = examSession.registration_start_date?.hour(10); - const registrationClosesAt = examSession.registration_end_date?.hour(16); if ( !ExamSessionUtils.hasRegistrationEnded(examSession, now) || @@ -212,8 +198,8 @@ export class ExamSessionUtils { ) { return { kind: RegistrationKind.Admission, - start: registrationOpensAt, - end: registrationClosesAt, + start: examSession.registration_start_date, + end: examSession.registration_end_date, participants: examSession.participants, quota: examSession.max_participants, availablePlaces: Math.max( @@ -222,29 +208,25 @@ export class ExamSessionUtils { ), availableQueue: !examSession.queue_full, open: - registrationOpensAt.isBefore(now) && - registrationClosesAt.isAfter(now), + ExamSessionUtils.hasRegistrationStarted(examSession, now) && + !ExamSessionUtils.hasRegistrationEnded(examSession, now), }; } else { - const postAdmissionOpensAt = - examSession.post_admission_start_date?.hour(10); - const postAdmissionClosesAt = - examSession.post_admission_end_date?.hour(16); const quota = examSession.post_admission_quota || 0; + const start = examSession.post_admission_start_date as Dayjs; + const end = examSession.post_admission_end_date as Dayjs; return { kind: RegistrationKind.PostAdmission, - start: postAdmissionOpensAt as Dayjs, - end: postAdmissionClosesAt as Dayjs, + start, + end, participants: examSession.pa_participants, quota, availablePlaces: Math.max(quota - examSession.pa_participants, 0), availableQueue: false, open: - !!postAdmissionOpensAt && - !!postAdmissionClosesAt && - postAdmissionOpensAt.isBefore(now) && - postAdmissionClosesAt.isAfter(now), + ExamSessionUtils.hasPostAdmissionStarted(examSession, now) && + !ExamSessionUtils.hasPostAdmissionEnded(examSession, now), }; } } diff --git a/frontend/packages/yki/src/utils/serialization.ts b/frontend/packages/yki/src/utils/serialization.ts index be9216563..ae2c8ba63 100644 --- a/frontend/packages/yki/src/utils/serialization.ts +++ b/frontend/packages/yki/src/utils/serialization.ts @@ -30,22 +30,40 @@ import { import { EvaluationOrderState } from 'redux/reducers/evaluationOrder'; export class SerializationUtils { + static deserializeStartTime(date?: string) { + if (date) { + return dayjs(date, 'YYYY-MM-DD') + .tz('Europe/Helsinki') + .hour(10) + .tz(dayjs.tz.guess()); + } + } + + static deserializeEndTime(date?: string) { + if (date) { + return dayjs(date, 'YYYY-MM-DD') + .tz('Europe/Helsinki') + .hour(16) + .tz(dayjs.tz.guess()); + } + } + static deserializeExamSessionResponse( examSessionResponse: ExamSessionResponse ): ExamSession { return { ...examSessionResponse, session_date: dayjs(examSessionResponse.session_date), - post_admission_start_date: DateUtils.optionalStringToDate( + post_admission_start_date: SerializationUtils.deserializeStartTime( examSessionResponse.post_admission_start_date ), - post_admission_end_date: DateUtils.optionalStringToDate( + post_admission_end_date: SerializationUtils.deserializeEndTime( examSessionResponse.post_admission_end_date ), - registration_start_date: DateUtils.optionalStringToDate( + registration_start_date: SerializationUtils.deserializeStartTime( examSessionResponse.registration_start_date ) as Dayjs, - registration_end_date: DateUtils.optionalStringToDate( + registration_end_date: SerializationUtils.deserializeEndTime( examSessionResponse.registration_end_date ) as Dayjs, exam_fee: parseInt(examSessionResponse.exam_fee as string), From 9a63da4641cc12358e3fcbe86af795b573d4c0f2 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 4 Oct 2023 15:52:35 +0300 Subject: [PATCH 58/72] AKR:OTR:VKT(Frontend): Update shared package version to 1.9.27 --- frontend/packages/akr/package.json | 2 +- frontend/packages/otr/package.json | 2 +- frontend/packages/vkt/package.json | 2 +- frontend/yarn.lock | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/packages/akr/package.json b/frontend/packages/akr/package.json index c7f4a8fd7..161f63f42 100644 --- a/frontend/packages/akr/package.json +++ b/frontend/packages/akr/package.json @@ -22,6 +22,6 @@ "akr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" } } diff --git a/frontend/packages/otr/package.json b/frontend/packages/otr/package.json index 78f4942c8..eed5840f0 100644 --- a/frontend/packages/otr/package.json +++ b/frontend/packages/otr/package.json @@ -25,6 +25,6 @@ "otr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" } } diff --git a/frontend/packages/vkt/package.json b/frontend/packages/vkt/package.json index 143218088..7356a8c16 100644 --- a/frontend/packages/vkt/package.json +++ b/frontend/packages/vkt/package.json @@ -26,6 +26,6 @@ }, "dependencies": { "reduxjs-toolkit-persist": "^7.2.1", - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" } } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 11bbf375c..79d02c2d3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2890,7 +2890,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.akr@workspace:packages/akr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" languageName: unknown linkType: soft @@ -2898,7 +2898,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.otr@workspace:packages/otr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" languageName: unknown linkType: soft @@ -2990,7 +2990,7 @@ __metadata: languageName: unknown linkType: soft -"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26": +"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27": version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared" languageName: unknown @@ -3001,7 +3001,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.vkt@workspace:packages/vkt" dependencies: reduxjs-toolkit-persist: ^7.2.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" languageName: unknown linkType: soft @@ -3010,7 +3010,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.yki@workspace:packages/yki" dependencies: multer: ^1.4.5-lts.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.26" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" languageName: unknown linkType: soft From a9209a43ad78cbcbefe83886aa16b5571c703402 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 3 Oct 2023 19:06:43 +0300 Subject: [PATCH 59/72] YKI(Frontend): Report ExamSession as full if admission period has ended --- frontend/packages/yki/src/utils/examSession.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/packages/yki/src/utils/examSession.ts b/frontend/packages/yki/src/utils/examSession.ts index 5376a84c4..67afba5cb 100644 --- a/frontend/packages/yki/src/utils/examSession.ts +++ b/frontend/packages/yki/src/utils/examSession.ts @@ -8,7 +8,7 @@ import { ExamSession, ExamSessionLocation } from 'interfaces/examSessions'; export class ExamSessionUtils { private static getRegistrationAvailablePlaces(examSession: ExamSession) { - return examSession.max_participants - examSession.participants; + return Math.max(examSession.max_participants - examSession.participants, 0); } private static isPostAdmissionAvailable(examSession: ExamSession) { @@ -25,7 +25,10 @@ export class ExamSessionUtils { !ExamSessionUtils.hasPostAdmissionEnded(examSession, dayjs()) && examSession.post_admission_quota ) { - return examSession.post_admission_quota - examSession.pa_participants; + return Math.max( + examSession.post_admission_quota - examSession.pa_participants, + 0 + ); } return 0; @@ -202,10 +205,7 @@ export class ExamSessionUtils { end: examSession.registration_end_date, participants: examSession.participants, quota: examSession.max_participants, - availablePlaces: Math.max( - examSession.max_participants - examSession.participants, - 0 - ), + availablePlaces: ExamSessionUtils.getAvailablePlaces(examSession), availableQueue: !examSession.queue_full, open: ExamSessionUtils.hasRegistrationStarted(examSession, now) && @@ -222,7 +222,7 @@ export class ExamSessionUtils { end, participants: examSession.pa_participants, quota, - availablePlaces: Math.max(quota - examSession.pa_participants, 0), + availablePlaces: ExamSessionUtils.getAvailablePlaces(examSession), availableQueue: false, open: ExamSessionUtils.hasPostAdmissionStarted(examSession, now) && From c3f05a6d1737bc07aab70338d39fe75fd6aa0d3d Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Tue, 3 Oct 2023 19:07:48 +0300 Subject: [PATCH 60/72] YKI(Frontend): Improve sorting of exam sessions. Prioritize those sessions that can be registered to. --- .../packages/yki/src/utils/examSession.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/frontend/packages/yki/src/utils/examSession.ts b/frontend/packages/yki/src/utils/examSession.ts index 67afba5cb..8633dcc7b 100644 --- a/frontend/packages/yki/src/utils/examSession.ts +++ b/frontend/packages/yki/src/utils/examSession.ts @@ -44,8 +44,23 @@ export class ExamSessionUtils { return ExamSessionUtils.getAvailablePlaces(examSession) > 0; } + private static compareExamSessionsByAdmissionAvailability( + es1: ExamSession, + es2: ExamSession + ) { + if (es1.open && !es2.open) { + return -1; + } else if (!es1.open && es2.open) { + return 1; + } else { + return 0; + } + } + private static compareExamSessionsByLang(es1: ExamSession, es2: ExamSession) { - // TODO: write proper language comparator / re-order ExamLanguage enums + // Note: this is a silly comparison of language codes. + // Use when the actual order between exam languages is not a major concern, + // but exam sessions should just be grouped by language. if (es1.language_code < es2.language_code) { return -1; } else if (es1.language_code > es2.language_code) { @@ -119,11 +134,12 @@ export class ExamSessionUtils { static compareExamSessions(es1: ExamSession, es2: ExamSession) { // Prioritised ordering of comparators const comparatorFns = [ - ExamSessionUtils.compareExamSessionsByLang, + ExamSessionUtils.compareExamSessionsByAdmissionAvailability, ExamSessionUtils.compareExamSessionsByRoom, ExamSessionUtils.compareExamSessionsByQueueFullness, - ExamSessionUtils.compareExamSessionsByDate, ExamSessionUtils.compareExamSessionsByRegistrationEnding, + ExamSessionUtils.compareExamSessionsByDate, + ExamSessionUtils.compareExamSessionsByLang, ]; for (let i = 0; i < comparatorFns.length; i++) { From 870b63dd6b70503afde279a3191479143d9961af Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 4 Oct 2023 12:32:17 +0300 Subject: [PATCH 61/72] YKI(Frontend): Fix mocking of system time for tests. The previous attempt at setting a static system time for tests didn't actually work. Instead, the tests passed in spite of the actual time being used with the comparison logic. --- .../packages/yki/src/tests/jest/utils/examSession.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts b/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts index 3a0f8cb13..4d8cb3900 100644 --- a/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts +++ b/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts @@ -261,7 +261,9 @@ describe('ExamSessionUtils', () => { describe('getEffectiveRegistrationPeriodDetails', () => { const testDay = dayjs('2023-08-11'); - jest.useFakeTimers({ now: testDay.toDate() }); + beforeAll(() => { + jest.useFakeTimers().setSystemTime(testDay.toDate()); + }); it('should return correct data when regular admission is ongoing', () => { expectEffectiveRegistrationDetails(baseExamSession, { @@ -302,6 +304,8 @@ describe('ExamSessionUtils', () => { ); }); - jest.useRealTimers(); + afterAll(() => { + jest.useRealTimers(); + }); }); }); From dd0090bd86124a2273f26cc43d6c02b098f4c2c3 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 4 Oct 2023 13:07:15 +0300 Subject: [PATCH 62/72] YKI(Frontend): Extract comparison of timezoned dates to its own utility function --- frontend/packages/yki/src/utils/dateTime.ts | 9 +++++++ .../packages/yki/src/utils/examSession.ts | 25 ++++++++++--------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/frontend/packages/yki/src/utils/dateTime.ts b/frontend/packages/yki/src/utils/dateTime.ts index 689420590..de3b25996 100644 --- a/frontend/packages/yki/src/utils/dateTime.ts +++ b/frontend/packages/yki/src/utils/dateTime.ts @@ -12,4 +12,13 @@ export class DateTimeUtils { t('yki.common.dates.dateTimeFormat') ); } + + static isBeforeOrEqual(a: Dayjs, b: Dayjs) { + // Note: need to first convert to utc because of poor support in Dayjs + // for comparing times across timezones. + const d1 = a.utc(); + const d2 = b.utc(); + + return d1.isBefore(d2) || d1.isSame(2); + } } diff --git a/frontend/packages/yki/src/utils/examSession.ts b/frontend/packages/yki/src/utils/examSession.ts index 8633dcc7b..d2a81da0d 100644 --- a/frontend/packages/yki/src/utils/examSession.ts +++ b/frontend/packages/yki/src/utils/examSession.ts @@ -5,6 +5,7 @@ import { StringUtils } from 'shared/utils'; import { translateOutsideComponent } from 'configs/i18n'; import { ExamLanguage, ExamLevel, RegistrationKind } from 'enums/app'; import { ExamSession, ExamSessionLocation } from 'interfaces/examSessions'; +import { DateTimeUtils } from 'utils/dateTime'; export class ExamSessionUtils { private static getRegistrationAvailablePlaces(examSession: ExamSession) { @@ -181,31 +182,31 @@ export class ExamSessionUtils { static hasRegistrationStarted(examSession: ExamSession, now: Dayjs) { const { registration_start_date } = examSession; - // Note: need to convert to utc because of poor support in Dayjs - // for comparing times across timezones - return registration_start_date.utc().isBefore(now.utc()); + return DateTimeUtils.isBeforeOrEqual(registration_start_date, now); } static hasRegistrationEnded(examSession: ExamSession, now: Dayjs) { const { registration_end_date } = examSession; - return registration_end_date.utc().isBefore(now.utc()); + return DateTimeUtils.isBeforeOrEqual(registration_end_date, now); } static hasPostAdmissionStarted(examSession: ExamSession, now: Dayjs) { - if (examSession.post_admission_start_date) { - return examSession.post_admission_start_date.utc().isBefore(now.utc()); - } + const { post_admission_start_date } = examSession; - return false; + return ( + post_admission_start_date && + DateTimeUtils.isBeforeOrEqual(post_admission_start_date, now) + ); } static hasPostAdmissionEnded(examSession: ExamSession, now: Dayjs) { - if (examSession.post_admission_end_date) { - return examSession.post_admission_end_date.utc().isBefore(now.utc()); - } + const { post_admission_end_date } = examSession; - return false; + return ( + post_admission_end_date && + DateTimeUtils.isBeforeOrEqual(post_admission_end_date, now) + ); } static getEffectiveRegistrationPeriodDetails(examSession: ExamSession) { From 8c6bdc86a7fc4a548fd510c2d1279c235f3b0ab2 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 4 Oct 2023 13:08:36 +0300 Subject: [PATCH 63/72] YKI(Frontend): Fix tests --- .../src/tests/jest/utils/examSession.test.ts | 76 +++++++++++++++---- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts b/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts index 4d8cb3900..cbea441ec 100644 --- a/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts +++ b/frontend/packages/yki/src/tests/jest/utils/examSession.test.ts @@ -163,18 +163,6 @@ describe('ExamSessionUtils', () => { }); it('should prioritise comparators', () => { - // language code > room - expect( - ExamSessionUtils.compareExamSessions( - { - ...baseExamSession, - language_code: ExamLanguage.DEU, - participants: baseExamSession.max_participants, - }, - baseExamSession - ) - ).toEqual(-1); - // room > queue fullness expect( ExamSessionUtils.compareExamSessions( @@ -201,7 +189,7 @@ describe('ExamSessionUtils', () => { ) ).toEqual(1); - // earlier session date > ended registration + // ongoing registration > earlier session date // participants set as max_participants for both to avoid es1 to be considered full and es2 not expect( ExamSessionUtils.compareExamSessions( @@ -216,6 +204,20 @@ describe('ExamSessionUtils', () => { participants: baseExamSession.max_participants, } ) + ).toEqual(1); + + // exam date > language + expect( + ExamSessionUtils.compareExamSessions( + { + ...baseExamSession, + session_date: dayjs('2098-12-31'), + }, + { + ...baseExamSession, + language_code: ExamLanguage.DEU, + } + ) ).toEqual(-1); }); @@ -302,6 +304,54 @@ describe('ExamSessionUtils', () => { availableQueue: false, } ); + + expectEffectiveRegistrationDetails( + { + ...baseExamSession, + registration_end_date: testDay.subtract(2, 'day'), + post_admission_active: true, + post_admission_start_date: testDay.subtract(1, 'day'), + post_admission_end_date: testDay.add(2, 'day'), + pa_participants: 3, + post_admission_quota: 10, + }, + { + kind: RegistrationKind.PostAdmission, + open: true, + availableQueue: false, + availablePlaces: 7, + } + ); + }); + + it('should indicate session is full if registration period has ended', () => { + expectEffectiveRegistrationDetails( + { + ...baseExamSession, + registration_end_date: testDay.subtract(1, 'day'), + }, + { + kind: RegistrationKind.Admission, + open: false, + availablePlaces: 0, + } + ); + + expectEffectiveRegistrationDetails( + { + ...baseExamSession, + registration_end_date: testDay.subtract(3, 'day'), + post_admission_active: true, + post_admission_start_date: testDay.subtract(2, 'day'), + post_admission_end_date: testDay.subtract(1, 'day'), + post_admission_quota: 9, + }, + { + kind: RegistrationKind.PostAdmission, + open: false, + availablePlaces: 0, + } + ); }); afterAll(() => { From 02e79b513cb9341165df7d9ac6bbba4d44ea832a Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 4 Oct 2023 15:40:40 +0300 Subject: [PATCH 64/72] YKI(Frontend): Return working date format string from Jest's i18n mock. --- .../packages/yki/src/tests/jest/__mocks__/globalMocks.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/tests/jest/__mocks__/globalMocks.ts b/frontend/packages/yki/src/tests/jest/__mocks__/globalMocks.ts index 3469945fe..fb96fea3d 100644 --- a/frontend/packages/yki/src/tests/jest/__mocks__/globalMocks.ts +++ b/frontend/packages/yki/src/tests/jest/__mocks__/globalMocks.ts @@ -6,7 +6,13 @@ jest.mock('configs/i18n', () => ({ changeLang: (language: string) => language, usePublicTranslation: () => ({ t: (str: string) => str }), useCommonTranslation: () => (str: string) => str, - translateOutsideComponent: () => (str: string) => str, + translateOutsideComponent: () => (str: string) => { + if (str === 'yki.common.dates.dateTimeFormat') { + return 'l [klo] HH.mm'; + } else { + return str; + } + }, })); const mockAppSelector = jest.fn(() => ({})); From 3b540c58d7096aa670734c3de568ef639cef519b Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Wed, 4 Oct 2023 15:43:49 +0300 Subject: [PATCH 65/72] YKI(Frontend): Update tests and fixtures. Show exam sessions as full if registration has ended. --- .../yki/src/redux/selectors/examSessions.ts | 10 +-- .../public_registration_page.spec.ts | 2 +- .../PublicExamSessionListing.test.tsx | 6 ++ .../PublicExamSessionListing.test.tsx.snap | 64 +++++++++---------- 4 files changed, 44 insertions(+), 38 deletions(-) diff --git a/frontend/packages/yki/src/redux/selectors/examSessions.ts b/frontend/packages/yki/src/redux/selectors/examSessions.ts index 3e70ede4e..32a58a148 100644 --- a/frontend/packages/yki/src/redux/selectors/examSessions.ts +++ b/frontend/packages/yki/src/redux/selectors/examSessions.ts @@ -42,12 +42,12 @@ const filterExamSessions = ( filteredData = filteredData.filter((es) => { const { open, availablePlaces } = ExamSessionUtils.getEffectiveRegistrationPeriodDetails(es); - if (filters.excludeFullSessions && !availablePlaces) { - return false; - } else if (filters.excludeNonOpenSessions && !open) { - return false; + if (filters.excludeFullSessions && filters.excludeNonOpenSessions) { + return availablePlaces > 0 && open; + } else if (filters.excludeFullSessions) { + return availablePlaces > 0; } else { - return true; + return open; } }); } diff --git a/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts b/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts index f2470ae34..7bea77597 100644 --- a/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts +++ b/frontend/packages/yki/src/tests/cypress/integration/public_registration_page.spec.ts @@ -32,7 +32,7 @@ describe('PublicRegistrationPage', () => { onPublicRegistrationPage.selectExamLanguage('kaikki kielet'); onPublicRegistrationPage.selectExamLevel('kaikki tasot'); onPublicRegistrationPage.toggleShowOnlyIfAvailablePlaces(); - onPublicRegistrationPage.expectResultsCount(4); + onPublicRegistrationPage.expectResultsCount(3); onPublicRegistrationPage.toggleShowOnlyIfOngoingAdmission(); onPublicRegistrationPage.expectResultsCount(2); }); diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx b/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx index 755bc148b..49b3b1392 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx @@ -1,3 +1,4 @@ +import dayjs from 'dayjs'; import { BrowserRouter } from 'react-router-dom'; import renderer from 'react-test-renderer'; @@ -7,6 +8,11 @@ import { examSessions } from 'tests/msw/fixtures/examSession'; import { SerializationUtils } from 'utils/serialization'; describe('PublicExamSessionsTable', () => { + const testDay = dayjs('2023-08-11'); + beforeAll(() => { + jest.useFakeTimers().setSystemTime(testDay.toDate()); + }); + it('should render correctly', () => { const { exam_sessions } = SerializationUtils.deserializeExamSessionsResponse( diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap index beef61b1a..edd095712 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/__snapshots__/PublicExamSessionListing.test.tsx.snap @@ -234,12 +234,12 @@ Array [ :
- yki.co00on.4amte0.4amteTi0eFor0amt + 6/1/2023 klo 10.00 —
- yki.co00on.2pmte0.2pmteTi0eFor0pmt + 8/1/2023 klo 16.00
- 6 + full - yki.co00on.2amte0.2amteTi0eFor0amt + 8/1/2023 klo 10.00 —
- yki.co00on.0pmte0.0pmteTi0eFor0pmt + 9/1/2024 klo 16.00
- yki.co00on.0amte0.0amteTi0eFor0amt + 12/1/2030 klo 10.00 —
- yki.co00on.6pmte0.6pmteTi0eFor0pmt + 12/10/2039 klo 16.00
- yki.co00on.3amte0.3amteTi0eFor0amt + 12/3/2025 klo 10.00 —
- yki.co00on.3pmte0.3pmteTi0eFor0pmt + 12/14/2039 klo 16.00
- yki.co00on.5amte0.5amteTi0eFor0amt + 2/1/2019 klo 10.00 —
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + 2/28/2039 klo 16.00
- yki.co00on.5amte0.5amteTi0eFor0amt + 2/1/2019 klo 10.00 —
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + 2/28/2039 klo 16.00
- yki.co00on.5amte0.5amteTi0eFor0amt + 2/1/2019 klo 10.00 —
- yki.co00on.3pmte0.3pmteTi0eFor0pmt + 2/28/2029 klo 16.00
- yki.co00on.5amte0.5amteTi0eFor0amt + 2/1/2019 klo 10.00 —
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + 2/28/2039 klo 16.00
- yki.co00on.5amte0.5amteTi0eFor0amt + 2/1/2019 klo 10.00 —
- yki.co00on.1pmte0.1pmteTi0eFor0pmt + 2/28/2039 klo 16.00
- yki.co00on.0amte0.0amteTi0eFor0amt + 11/1/2020 klo 10.00 —
- yki.co00on.3pmte0.3pmteTi0eFor0pmt + 12/2/2020 klo 16.00
- 10 + full Date: Thu, 5 Oct 2023 12:36:53 +0300 Subject: [PATCH 66/72] SHARED:AKR:OTR:VKT:YKI(Frontend): Paginated tables should scroll header into view when page or rowsPerPage changes --- frontend/packages/akr/package.json | 2 +- frontend/packages/otr/package.json | 2 +- frontend/packages/shared/CHANGELOG.MD | 6 ++ frontend/packages/shared/package.json | 2 +- .../Table/ManagedPaginatedTable.tsx | 57 +++++++++--------- .../src/components/Table/PaginatedTable.tsx | 58 +++++++++---------- frontend/packages/vkt/package.json | 2 +- frontend/packages/yki/package.json | 2 +- frontend/yarn.lock | 10 ++-- 9 files changed, 75 insertions(+), 66 deletions(-) diff --git a/frontend/packages/akr/package.json b/frontend/packages/akr/package.json index 161f63f42..f8bd791d8 100644 --- a/frontend/packages/akr/package.json +++ b/frontend/packages/akr/package.json @@ -22,6 +22,6 @@ "akr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" } } diff --git a/frontend/packages/otr/package.json b/frontend/packages/otr/package.json index eed5840f0..17013064a 100644 --- a/frontend/packages/otr/package.json +++ b/frontend/packages/otr/package.json @@ -25,6 +25,6 @@ "otr:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" } } diff --git a/frontend/packages/shared/CHANGELOG.MD b/frontend/packages/shared/CHANGELOG.MD index 31981b6f7..8d99bd504 100644 --- a/frontend/packages/shared/CHANGELOG.MD +++ b/frontend/packages/shared/CHANGELOG.MD @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Released] +## [1.9.28] - 2023-10-05 + +### Changed + +- Make PaginatedTable and ManagedPaginatedTable scroll up if page (or rows per page) is changed + ## [1.9.27] - 2023-10-03 ### Changed diff --git a/frontend/packages/shared/package.json b/frontend/packages/shared/package.json index a7e4759e6..067e8c392 100644 --- a/frontend/packages/shared/package.json +++ b/frontend/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@opetushallitus/kieli-ja-kaantajatutkinnot.shared", - "version": "1.9.27", + "version": "1.9.28", "description": "Shared Frontend Package", "exports": { "./components": "./src/components/index.tsx", diff --git a/frontend/packages/shared/src/components/Table/ManagedPaginatedTable.tsx b/frontend/packages/shared/src/components/Table/ManagedPaginatedTable.tsx index 25b81eaeb..f35498b59 100644 --- a/frontend/packages/shared/src/components/Table/ManagedPaginatedTable.tsx +++ b/frontend/packages/shared/src/components/Table/ManagedPaginatedTable.tsx @@ -1,5 +1,5 @@ import { Table, TableBody, TablePagination } from '@mui/material'; -import { ChangeEvent, Fragment } from 'react'; +import { ChangeEvent, Fragment, useRef } from 'react'; import { defaultDisplayedRowsLabel, @@ -39,40 +39,41 @@ export function ManagedPaginatedTable({ backIconButtonProps, nextIconButtonProps, }: ManagedPaginatedTableProps): JSX.Element { + const headerRef = useRef(null); + const handlePageChange = (page: number) => { + onPageChange(page); + headerRef.current?.scrollIntoView(); + }; const handleRowsPerPageChange = (event: ChangeEvent) => { - onPageChange(0); + handlePageChange(0); onRowsPerPageChange(+event.target.value); }; const count = data.length; - const Pagination = ({ - showHeaderContent, - }: { - showHeaderContent: boolean; - }) => ( -
- {showHeaderContent && headerContent} - onPageChange(newPage)} - page={page} - onRowsPerPageChange={handleRowsPerPageChange} - rowsPerPage={rowsPerPage} - rowsPerPageOptions={rowsPerPageOptions} - labelRowsPerPage={rowsPerPageLabel} - labelDisplayedRows={labelDisplayedRows ?? defaultDisplayedRowsLabel} - backIconButtonProps={backIconButtonProps} - nextIconButtonProps={nextIconButtonProps} - /> -
+ const Pagination = () => ( + handlePageChange(newPage)} + page={page} + onRowsPerPageChange={handleRowsPerPageChange} + rowsPerPage={rowsPerPage} + rowsPerPageOptions={rowsPerPageOptions} + labelRowsPerPage={rowsPerPageLabel} + labelDisplayedRows={labelDisplayedRows ?? defaultDisplayedRowsLabel} + backIconButtonProps={backIconButtonProps} + nextIconButtonProps={nextIconButtonProps} + /> ); return ( <> - +
+ {headerContent} + +
({ })}
- {showBottomPagination && } + {showBottomPagination && ( +
+ +
+ )} ); } diff --git a/frontend/packages/shared/src/components/Table/PaginatedTable.tsx b/frontend/packages/shared/src/components/Table/PaginatedTable.tsx index 3f28c53bc..5c5572d66 100644 --- a/frontend/packages/shared/src/components/Table/PaginatedTable.tsx +++ b/frontend/packages/shared/src/components/Table/PaginatedTable.tsx @@ -5,7 +5,7 @@ import { TableBody, TablePagination, } from '@mui/material'; -import { ChangeEvent, Fragment, useState } from 'react'; +import { ChangeEvent, Fragment, useRef, useState } from 'react'; import { CustomTableProps } from './CustomTable'; import { WithId } from '../../interfaces/with'; @@ -60,10 +60,15 @@ export function PaginatedTable({ nextIconButtonProps, controlledPaging, }: PaginatedTableProps): JSX.Element { + const headerRef = useRef(null); const [internalPage, setInternalPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(initialRowsPerPage); + const handlePageChange = (page: number) => { + setPage(page); + headerRef.current?.scrollIntoView(); + }; const handleRowsPerPageChange = (event: ChangeEvent) => { - setPage(0); + handlePageChange(0); setRowsPerPage(+event.target.value); }; @@ -71,35 +76,30 @@ export function PaginatedTable({ const setPage = controlledPaging?.setPage ?? setInternalPage; const count = data.length; - const Pagination = ({ - page, - showHeaderContent, - }: { - page: number; - showHeaderContent: boolean; - }) => ( -
- {showHeaderContent && headerContent} - setPage(newPage)} - page={page} - onRowsPerPageChange={handleRowsPerPageChange} - rowsPerPage={rowsPerPage} - rowsPerPageOptions={rowsPerPageOptions} - labelRowsPerPage={rowsPerPageLabel} - labelDisplayedRows={labelDisplayedRows ?? defaultDisplayedRowsLabel} - backIconButtonProps={backIconButtonProps} - nextIconButtonProps={nextIconButtonProps} - /> -
+ const Pagination = ({ page }: { page: number }) => ( + handlePageChange(newPage)} + page={page} + onRowsPerPageChange={handleRowsPerPageChange} + rowsPerPage={rowsPerPage} + rowsPerPageOptions={rowsPerPageOptions} + labelRowsPerPage={rowsPerPageLabel} + labelDisplayedRows={labelDisplayedRows ?? defaultDisplayedRowsLabel} + backIconButtonProps={backIconButtonProps} + nextIconButtonProps={nextIconButtonProps} + /> ); return ( <> - +
+ {headerContent} + +
+ ({ })}
- {showBottomPagination && ( - - )} + {showBottomPagination && } ); } diff --git a/frontend/packages/vkt/package.json b/frontend/packages/vkt/package.json index 7356a8c16..2f4bb7a3b 100644 --- a/frontend/packages/vkt/package.json +++ b/frontend/packages/vkt/package.json @@ -26,6 +26,6 @@ }, "dependencies": { "reduxjs-toolkit-persist": "^7.2.1", - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" } } diff --git a/frontend/packages/yki/package.json b/frontend/packages/yki/package.json index 144133174..d24981a2b 100644 --- a/frontend/packages/yki/package.json +++ b/frontend/packages/yki/package.json @@ -26,7 +26,7 @@ "yki:tslint": "yarn g:tsc --pretty --noEmit" }, "dependencies": { - "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + "shared": "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" }, "devDependencies": { "multer": "^1.4.5-lts.1" diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 79d02c2d3..c03d045e5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2890,7 +2890,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.akr@workspace:packages/akr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" languageName: unknown linkType: soft @@ -2898,7 +2898,7 @@ __metadata: version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.otr@workspace:packages/otr" dependencies: - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" languageName: unknown linkType: soft @@ -2990,7 +2990,7 @@ __metadata: languageName: unknown linkType: soft -"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27": +"@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared, shared@npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28": version: 0.0.0-use.local resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.shared@workspace:packages/shared" languageName: unknown @@ -3001,7 +3001,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.vkt@workspace:packages/vkt" dependencies: reduxjs-toolkit-persist: ^7.2.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" languageName: unknown linkType: soft @@ -3010,7 +3010,7 @@ __metadata: resolution: "@opetushallitus/kieli-ja-kaantajatutkinnot.yki@workspace:packages/yki" dependencies: multer: ^1.4.5-lts.1 - shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.27" + shared: "npm:@opetushallitus/kieli-ja-kaantajatutkinnot.shared@1.9.28" languageName: unknown linkType: soft From 16bbc33883e54f1e31e652daf1ab5a5b959f14e3 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Thu, 5 Oct 2023 12:27:34 +0300 Subject: [PATCH 67/72] YKI(Frontend): Language dropdown to show Finnish, Swedish, English and then rest in alphabetical order [deploy] --- .../examSession/PublicExamSessionListingFilters.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx index edf15baa0..98310212d 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListingFilters.tsx @@ -121,7 +121,12 @@ export const PublicExamSessionFilters = ({ Date: Fri, 6 Oct 2023 11:10:19 +0300 Subject: [PATCH 68/72] YKI(Frontend): Reset table page when filters are applied again --- .../examSession/PublicExamSessionListing.tsx | 41 +++++++++++++++---- .../yki/src/pages/RegistrationPage.tsx | 15 ++++++- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx index e0dcc91c0..854357258 100644 --- a/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx +++ b/frontend/packages/yki/src/components/registration/examSession/PublicExamSessionListing.tsx @@ -1,7 +1,7 @@ import { LabelDisplayedRowsArgs } from '@mui/material/TablePagination'; import { Box } from '@mui/system'; import { TFunction } from 'i18next'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef } from 'react'; import { CustomCircularProgress, H3, @@ -35,26 +35,32 @@ const getDisplayedRowsLabel = ( export const PublicExamSessionsTable = ({ examSessions, + onPageChange, + onRowsPerPageChange, + page, + rowsPerPage, + rowsPerPageOptions, }: { examSessions: Array; + onPageChange: (page: number) => void; + onRowsPerPageChange: (rowsPerPage: number) => void; + page: number; + rowsPerPage: number; + rowsPerPageOptions: Array; }) => { const translateCommon = useCommonTranslation(); - // Pagination - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(20); - return ( } getRowDetails={getRowDetails} - rowsPerPageOptions={[10, 20, 50]} + rowsPerPageOptions={rowsPerPageOptions} page={page} - onPageChange={setPage} + onPageChange={onPageChange} rowsPerPage={rowsPerPage} - onRowsPerPageChange={setRowsPerPage} + onRowsPerPageChange={onRowsPerPageChange} rowsPerPageLabel={translateCommon( 'component.table.pagination.rowsPerPage' )} @@ -70,8 +76,18 @@ export const PublicExamSessionsTable = ({ export const PublicExamSessionListing = ({ examSessions, + onPageChange, + onRowsPerPageChange, + page, + rowsPerPage, + rowsPerPageOptions, }: { examSessions: Array; + onPageChange: (page: number) => void; + onRowsPerPageChange: (rowsPerPage: number) => void; + page: number; + rowsPerPage: number; + rowsPerPageOptions: Array; }) => { const translateCommon = useCommonTranslation(); const { isPhone } = useWindowProperties(); @@ -120,7 +136,14 @@ export const PublicExamSessionListing = ({ })}
- + ); } diff --git a/frontend/packages/yki/src/pages/RegistrationPage.tsx b/frontend/packages/yki/src/pages/RegistrationPage.tsx index 02815116f..77cc37098 100644 --- a/frontend/packages/yki/src/pages/RegistrationPage.tsx +++ b/frontend/packages/yki/src/pages/RegistrationPage.tsx @@ -28,8 +28,14 @@ export const RegistrationPage: FC = () => { const onApplyFilters = () => { setResults(filteredExamSessions); setShowResults(true); + setPage(0); }; + // Pagination + const [page, setPage] = useState(0); + const rowsPerPageOptions = [10, 20, 50]; + const [rowsPerPage, setRowsPerPage] = useState(20); + useEffect(() => { if (status === APIResponseStatus.NotStarted) { dispatch(loadExamSessions()); @@ -106,7 +112,14 @@ export const RegistrationPage: FC = () => { className="public-registration-page__grid-container__result-box" data-testid="public-registration-page__grid-container__result-box" > - + )} From 4aa4c297215bc8cf2d5d4d4d7eeb519bfaea867f Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 6 Oct 2023 11:27:47 +0300 Subject: [PATCH 69/72] YKI(Frontend): Fix test --- .../examSession/PublicExamSessionListing.test.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx b/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx index 49b3b1392..133ee24ba 100644 --- a/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx +++ b/frontend/packages/yki/src/tests/jest/components/registration/examSession/PublicExamSessionListing.test.tsx @@ -21,7 +21,14 @@ describe('PublicExamSessionsTable', () => { const tree = renderer .create( - + ) .toJSON(); From 3e5ea29da387c3dfefad1de97ecd3c5cb9a76d4e Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 6 Oct 2023 12:38:54 +0300 Subject: [PATCH 70/72] YKI(Frontend): Fix typo Co-authored-by: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> --- frontend/packages/yki/src/utils/dateTime.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/packages/yki/src/utils/dateTime.ts b/frontend/packages/yki/src/utils/dateTime.ts index de3b25996..51812077b 100644 --- a/frontend/packages/yki/src/utils/dateTime.ts +++ b/frontend/packages/yki/src/utils/dateTime.ts @@ -19,6 +19,6 @@ export class DateTimeUtils { const d1 = a.utc(); const d2 = b.utc(); - return d1.isBefore(d2) || d1.isSame(2); + return d1.isBefore(d2) || d1.isSame(d2); } } From 56defb5fa3c5cf6efcb55a740dc9534fb3de3adc Mon Sep 17 00:00:00 2001 From: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> Date: Fri, 6 Oct 2023 15:12:50 +0300 Subject: [PATCH 71/72] VKT(Frontend & Backend) OPHVKTKEH-164: uloskirjautumistoiminto (#565) * VKT(Backend) OPHVKTKEH-164: logout endpoint for backend * VKT(Backend) OPHVKTKEH-164: variable name fixed * VKT(Backend) OPHVKTKEH-164: authentication info API endpoint * VKT(Backend) OPHVKTKEH-164: oops, prettier * VKT(Backend & Frontend) OPHVKTKEH-164: login bar and internal state * VKT(Frontend) OPHVKTKEH-164 added target to ext link * VKT(Frontend) OPHVKTKEH-164: login bar UI * VKT(Frontend) OPHVKTKEH-164: logout success page fixes * VKT(Frontend & Backend) OPHVKTKEH-164: Logout fixes * VKT(Backend) OPHVKTKEH-164: return to logout success page after logout [deploy] * VKT(Frontend & Backend) OPHVKTKEH-164: review fixes * VKT(Frontend) OPHVKTKEH-164: translations for logout --- .../java/fi/oph/vkt/api/PublicController.java | 18 ++++++- .../fi/oph/vkt/service/PublicAuthService.java | 10 ++++ .../vkt/service/PublicEnrollmentService.java | 8 +-- .../oph/vkt/service/PublicPersonService.java | 6 +++ .../main/java/fi/oph/vkt/util/PersonUtil.java | 16 ++++++ .../java/fi/oph/vkt/util/SessionUtil.java | 4 ++ .../vkt/src/main/resources/application.yaml | 2 + .../oph-configuration/vkt.properties.template | 1 + .../vkt/public/i18n/fi-FI/common.json | 7 ++- .../vkt/public/i18n/fi-FI/public.json | 4 ++ .../vkt/public/i18n/sv-SE/common.json | 4 ++ .../vkt/public/i18n/sv-SE/public.json | 4 ++ .../vkt/src/components/layouts/Header.tsx | 9 +++- .../components/layouts/SessionStateHeader.tsx | 38 +++++++++++++ frontend/packages/vkt/src/enums/api.ts | 2 + frontend/packages/vkt/src/enums/app.ts | 1 + .../vkt/src/hooks/useAuthentication.ts | 17 +++++- .../packages/vkt/src/pages/LogoutSuccess.tsx | 53 +++++++++++++++++++ .../vkt/src/redux/reducers/publicUser.ts | 42 +++++++++++++++ .../packages/vkt/src/redux/sagas/index.ts | 2 + .../vkt/src/redux/sagas/publicUser.ts | 33 ++++++++++++ .../vkt/src/redux/selectors/publicUser.ts | 3 ++ .../packages/vkt/src/redux/store/index.ts | 2 + .../packages/vkt/src/routers/AppRouter.tsx | 9 ++++ .../styles/components/layouts/_header.scss | 4 +- .../components/layouts/_session-header.scss | 18 +++++++ .../styles/pages/_logout-success-page.scss | 36 +++++++++++++ frontend/packages/vkt/src/styles/styles.scss | 2 + 28 files changed, 342 insertions(+), 13 deletions(-) create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/util/PersonUtil.java create mode 100644 frontend/packages/vkt/src/components/layouts/SessionStateHeader.tsx create mode 100644 frontend/packages/vkt/src/pages/LogoutSuccess.tsx create mode 100644 frontend/packages/vkt/src/redux/reducers/publicUser.ts create mode 100644 frontend/packages/vkt/src/redux/sagas/publicUser.ts create mode 100644 frontend/packages/vkt/src/redux/selectors/publicUser.ts create mode 100644 frontend/packages/vkt/src/styles/components/layouts/_session-header.scss create mode 100644 frontend/packages/vkt/src/styles/pages/_logout-success-page.scss diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java b/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java index c8ff0ee1f..be5262377 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java @@ -4,6 +4,7 @@ import fi.oph.vkt.api.dto.PublicEnrollmentDTO; import fi.oph.vkt.api.dto.PublicEnrollmentInitialisationDTO; import fi.oph.vkt.api.dto.PublicExamEventDTO; +import fi.oph.vkt.api.dto.PublicPersonDTO; import fi.oph.vkt.api.dto.PublicReservationDTO; import fi.oph.vkt.model.Enrollment; import fi.oph.vkt.model.Person; @@ -209,8 +210,21 @@ public void validateTicket( } @GetMapping(path = "/auth/info") - public Person authInfo(final HttpSession session) { - return publicPersonService.getPerson(SessionUtil.getPersonId(session)); + public Optional authInfo(final HttpSession session) { + if (session == null || !SessionUtil.hasPersonId(session)) { + return Optional.empty(); + } + + return Optional.of(publicPersonService.getPersonDTO(SessionUtil.getPersonId(session))); + } + + @GetMapping(path = "/auth/logout") + public void logout(final HttpSession session, final HttpServletResponse httpResponse) throws IOException { + if (session != null) { + session.invalidate(); + } + + httpResponse.sendRedirect(publicAuthService.createCasLogoutUrl()); } @GetMapping(path = "/payment/create/{enrollmentId:\\d+}/redirect") diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicAuthService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicAuthService.java index b7fe05b71..ef34beb4a 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicAuthService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicAuthService.java @@ -5,6 +5,7 @@ import fi.oph.vkt.model.type.EnrollmentType; import fi.oph.vkt.repository.PersonRepository; import fi.oph.vkt.service.auth.CasTicketValidationService; +import fi.oph.vkt.util.UIRouteUtil; import fi.oph.vkt.util.exception.APIException; import fi.oph.vkt.util.exception.APIExceptionType; import java.net.URLEncoder; @@ -38,6 +39,15 @@ public String createCasLoginUrl(final long examEventId, final EnrollmentType typ return casLoginUrl + "?service=" + casServiceUrl + "&locale=" + appLocale.name().toLowerCase(); } + public String createCasLogoutUrl() { + final String casLogoutUrl = environment.getRequiredProperty("app.cas-oppija.logout-url"); + final String casServiceUrl = URLEncoder.encode( + environment.getRequiredProperty("app.cas-oppija.service-logout-url"), + StandardCharsets.UTF_8 + ); + return casLogoutUrl + "?service=" + casServiceUrl; + } + @Transactional public Person createPersonFromTicket(final String ticket, final long examEventId, final EnrollmentType type) { final Map personDetails = casTicketValidationService.validate(ticket, examEventId, type); diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java index a78a62e1d..1ea08aa1b 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java @@ -15,6 +15,7 @@ import fi.oph.vkt.repository.ExamEventRepository; import fi.oph.vkt.repository.ReservationRepository; import fi.oph.vkt.util.ExamEventUtil; +import fi.oph.vkt.util.PersonUtil; import fi.oph.vkt.util.exception.APIException; import fi.oph.vkt.util.exception.APIExceptionType; import fi.oph.vkt.util.exception.NotFoundException; @@ -137,12 +138,7 @@ private PublicEnrollmentInitialisationDTO createEnrollmentInitialisationDTO( .hasCongestion(false) .build(); - final PublicPersonDTO personDTO = PublicPersonDTO - .builder() - .id(person.getId()) - .lastName(person.getLastName()) - .firstName(person.getFirstName()) - .build(); + final PublicPersonDTO personDTO = PersonUtil.createPublicPersonDTO(person); return PublicEnrollmentInitialisationDTO .builder() diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicPersonService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicPersonService.java index 39047e3dc..eb473e831 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicPersonService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicPersonService.java @@ -1,7 +1,9 @@ package fi.oph.vkt.service; +import fi.oph.vkt.api.dto.PublicPersonDTO; import fi.oph.vkt.model.Person; import fi.oph.vkt.repository.PersonRepository; +import fi.oph.vkt.util.PersonUtil; import fi.oph.vkt.util.exception.NotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -15,4 +17,8 @@ public class PublicPersonService { public Person getPerson(final Long personId) { return personRepository.findById(personId).orElseThrow(() -> new NotFoundException("Person not found")); } + + public PublicPersonDTO getPersonDTO(final Long personId) { + return PersonUtil.createPublicPersonDTO(getPerson(personId)); + } } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/util/PersonUtil.java b/backend/vkt/src/main/java/fi/oph/vkt/util/PersonUtil.java new file mode 100644 index 000000000..77b2eae1f --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/util/PersonUtil.java @@ -0,0 +1,16 @@ +package fi.oph.vkt.util; + +import fi.oph.vkt.api.dto.PublicPersonDTO; +import fi.oph.vkt.model.Person; + +public class PersonUtil { + + public static PublicPersonDTO createPublicPersonDTO(final Person person) { + return PublicPersonDTO + .builder() + .id(person.getId()) + .lastName(person.getLastName()) + .firstName(person.getFirstName()) + .build(); + } +} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/util/SessionUtil.java b/backend/vkt/src/main/java/fi/oph/vkt/util/SessionUtil.java index fce0ebc1a..1b2790b8f 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/util/SessionUtil.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/util/SessionUtil.java @@ -8,6 +8,10 @@ public class SessionUtil { private static final String PERSON_ID_SESSION_KEY = "person_id"; + public static boolean hasPersonId(final HttpSession session) { + return session.getAttribute(PERSON_ID_SESSION_KEY) != null; + } + public static Long getPersonId(final HttpSession session) { final Long personId = (Long) session.getAttribute(PERSON_ID_SESSION_KEY); diff --git a/backend/vkt/src/main/resources/application.yaml b/backend/vkt/src/main/resources/application.yaml index 3ad77ac84..03ae85ec9 100644 --- a/backend/vkt/src/main/resources/application.yaml +++ b/backend/vkt/src/main/resources/application.yaml @@ -71,7 +71,9 @@ app: api: ${public-base-url:http://localhost:${server.port}}/vkt/api/v1 cas-oppija: login-url: ${cas-oppija.login-url:https://testiopintopolku.fi/cas-oppija/login} + logout-url: ${cas-oppija.logout-url:https://testiopintopolku.fi/cas-oppija/logout} service-url: ${cas-oppija.service-url:http://localhost:${server.port}/vkt/api/v1/auth/validate/%s/%s} + service-logout-url: ${public-base-url:http://localhost:4002}/vkt/uloskirjautuminen-onnistui validate-ticket-url: ${cas-oppija.validate-ticket-url:https://testiopintopolku.fi/cas-oppija/serviceValidate} reservation: duration: ${reservation.duration:PT30M} diff --git a/backend/vkt/src/main/resources/oph-configuration/vkt.properties.template b/backend/vkt/src/main/resources/oph-configuration/vkt.properties.template index ce3d9bc2c..759dd248b 100644 --- a/backend/vkt/src/main/resources/oph-configuration/vkt.properties.template +++ b/backend/vkt/src/main/resources/oph-configuration/vkt.properties.template @@ -21,6 +21,7 @@ reservation.duration=PT30M public-base-url={{vkt_app_url}} cas-oppija.login-url={{opintopolku_baseurl}}/cas-oppija/login +cas-oppija.logout-url={{opintopolku_baseurl}}/cas-oppija/logout cas-oppija.service-url={{vkt_app_url}}/vkt/api/v1/auth/validate/%s/%s cas-oppija.validate-ticket-url={{opintopolku_baseurl}}/cas-oppija/serviceValidate diff --git a/frontend/packages/vkt/public/i18n/fi-FI/common.json b/frontend/packages/vkt/public/i18n/fi-FI/common.json index 19ca7c825..e74636fd8 100644 --- a/frontend/packages/vkt/public/i18n/fi-FI/common.json +++ b/frontend/packages/vkt/public/i18n/fi-FI/common.json @@ -101,6 +101,9 @@ "continueToMain": "Jatka sisältöön", "langSelectorAriaLabel": "Kieli / Språk" }, + "sessionState": { + "logOut": "Kirjaudu ulos" + }, "lang": { "fi": "Suomeksi", "sv": "På svenska" @@ -145,12 +148,14 @@ "paymentFail": "Ilmoittautuminen - maksua ei suoritettu", "paymentSuccess": "Ilmoittautuminen - maksu valmis", "done": "Ilmoittautuminen - valmis", + "logoutSuccess": "Uloskirjautuminen onnnistui", "accessibilityStatement": "Saavutettavuusseloste", "privacyPolicy": "Tietosuojaseloste", "clerkEnrollmentOverview": "Virkailija ilmoittautuminen", "clerkExamEventCreate": "Virkailija lisää tutkintopäivä", "clerkExamOverview": "Virkailija tutkintosivu", - "clerkHomepage": "Virkailija" + "clerkHomepage": "Virkailija", + "logoutSuccess": "Uloskirjautuminen onnnistui" }, "requiredFieldsInfo": "Tähdellä (*) merkityt kentät ovat pakollisia.", "rowsPerPageLabel": "Tulokset per sivu", diff --git a/frontend/packages/vkt/public/i18n/fi-FI/public.json b/frontend/packages/vkt/public/i18n/fi-FI/public.json index f18acece2..23469f1df 100644 --- a/frontend/packages/vkt/public/i18n/fi-FI/public.json +++ b/frontend/packages/vkt/public/i18n/fi-FI/public.json @@ -219,6 +219,10 @@ "enrollToQueue": "Ilmoittaudu jonoon" }, "title": "Tulevat tutkintotilaisuudet" + }, + "logoutSuccessPage": { + "heading": "Uloskirjautuminen onnnistui", + "info": "Olet kirjautunut ulos. Suljethan vielä kaikki selainikkunat." } } } diff --git a/frontend/packages/vkt/public/i18n/sv-SE/common.json b/frontend/packages/vkt/public/i18n/sv-SE/common.json index a36f5c8f3..e900b2be0 100644 --- a/frontend/packages/vkt/public/i18n/sv-SE/common.json +++ b/frontend/packages/vkt/public/i18n/sv-SE/common.json @@ -101,6 +101,9 @@ "continueToMain": "Fortsätt till innehållet", "langSelectorAriaLabel": "Kieli / Språk" }, + "sessionState": { + "logOut": "Logga ut" + }, "lang": { "fi": "Suomeksi", "sv": "På svenska" @@ -137,6 +140,7 @@ "paymentFail": "Anmälan - avgiften har inte betalats", "paymentSuccess": "Anmälan - betalningen är slutförd", "done": "Anmälan - färdig", + "logoutSuccess": "Utloggning lyckades", "privacyPolicy": "Dataskyddsbeskrivning", "clerkEnrollmentOverview": "Administratör anmälning", "clerkExamEventCreate": "Administratör lägg till examensdag", diff --git a/frontend/packages/vkt/public/i18n/sv-SE/public.json b/frontend/packages/vkt/public/i18n/sv-SE/public.json index bfadef075..cb3f2dd66 100644 --- a/frontend/packages/vkt/public/i18n/sv-SE/public.json +++ b/frontend/packages/vkt/public/i18n/sv-SE/public.json @@ -226,6 +226,10 @@ "enrollToQueue": "Ställ dig i kön" }, "title": "Kommande examenstillfällen" + }, + "logoutSuccessPage": { + "heading": "Utloggning lyckades", + "info": "Du har loggat ut. Stäng alla fönster i webbläsaren." } } } diff --git a/frontend/packages/vkt/src/components/layouts/Header.tsx b/frontend/packages/vkt/src/components/layouts/Header.tsx index 3fdb55d7b..6896eae31 100644 --- a/frontend/packages/vkt/src/components/layouts/Header.tsx +++ b/frontend/packages/vkt/src/components/layouts/Header.tsx @@ -11,6 +11,7 @@ import { useWindowProperties } from 'shared/hooks'; import { ClerkHeaderButtons } from 'components/layouts/clerkHeader/ClerkHeaderButtons'; import { ClerkNavTabs } from 'components/layouts/clerkHeader/ClerkNavTabs'; +import { SessionStateHeader } from 'components/layouts/SessionStateHeader'; import { changeLang, getCurrentLang, @@ -29,7 +30,7 @@ export const Header = (): JSX.Element => { [translateCommon('header.lang.sv'), swedish], ]); - const { isAuthenticated, isClerkUI } = useAuthentication(); + const { isAuthenticated, isClerkUI, publicUser } = useAuthentication(); const logoRedirectURL = isAuthenticated ? AppRoutes.ClerkHomePage : AppRoutes.PublicHomePage; @@ -54,6 +55,12 @@ export const Header = (): JSX.Element => { /> )} + {publicUser?.isAuthenticated && ( + + )}
diff --git a/frontend/packages/vkt/src/components/layouts/SessionStateHeader.tsx b/frontend/packages/vkt/src/components/layouts/SessionStateHeader.tsx new file mode 100644 index 000000000..2c7fc68b2 --- /dev/null +++ b/frontend/packages/vkt/src/components/layouts/SessionStateHeader.tsx @@ -0,0 +1,38 @@ +import { + LogoutOutlined as LogoutIcon, + Person as PersonIcon, +} from '@mui/icons-material'; +import Button from '@mui/material/Button'; +import { FC } from 'react'; +import { Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; + +import { useCommonTranslation } from 'configs/i18n'; +import { APIEndpoints } from 'enums/api'; + +interface SessionStateHeaderProps { + firstName: string; + lastName: string; +} + +export const SessionStateHeader: FC = ({ + firstName, + lastName, +}) => { + const translateCommon = useCommonTranslation(); + + return ( + + ); +}; diff --git a/frontend/packages/vkt/src/enums/api.ts b/frontend/packages/vkt/src/enums/api.ts index a4d2a92f8..9fcbc96c5 100644 --- a/frontend/packages/vkt/src/enums/api.ts +++ b/frontend/packages/vkt/src/enums/api.ts @@ -1,11 +1,13 @@ export enum APIEndpoints { PublicAuthLogin = '/vkt/api/v1/auth/login/:examEventId/:type?locale=:locale', + PublicAuthLogout = '/vkt/api/v1/auth/logout', PublicExamEvent = '/vkt/api/v1/examEvent', PublicEnrollment = '/vkt/api/v1/enrollment', PublicReservation = '/vkt/api/v1/reservation', PaymentCreate = '/vkt/api/v1/payment/create/:enrollmentId/redirect?locale=:locale', ClerkExamEvent = '/vkt/api/v1/clerk/examEvent', ClerkUser = '/vkt/api/v1/clerk/user', + PublicUser = '/vkt/api/v1/auth/info', ClerkEnrollment = '/vkt/api/v1/clerk/enrollment', ClerkPayment = '/vkt/api/v1/clerk/payment', } diff --git a/frontend/packages/vkt/src/enums/app.ts b/frontend/packages/vkt/src/enums/app.ts index 4d2b21ad8..9e2bb52af 100644 --- a/frontend/packages/vkt/src/enums/app.ts +++ b/frontend/packages/vkt/src/enums/app.ts @@ -20,6 +20,7 @@ export enum AppRoutes { ClerkLocalLogoutPage = '/vkt/cas/localLogout', AccessibilityStatementPage = '/vkt/saavutettavuusseloste', PrivacyPolicyPage = '/vkt/tietosuojaseloste', + LogoutSuccess = '/vkt/uloskirjautuminen-onnistui', NotFoundPage = '*', } diff --git a/frontend/packages/vkt/src/hooks/useAuthentication.ts b/frontend/packages/vkt/src/hooks/useAuthentication.ts index 1bf278ea8..a67f5d0f1 100644 --- a/frontend/packages/vkt/src/hooks/useAuthentication.ts +++ b/frontend/packages/vkt/src/hooks/useAuthentication.ts @@ -4,14 +4,18 @@ import { APIResponseStatus } from 'shared/enums'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { AppRoutes } from 'enums/app'; import { loadClerkUser } from 'redux/reducers/clerkUser'; +import { loadPublicUser } from 'redux/reducers/publicUser'; import { clerkUserSelector } from 'redux/selectors/clerkUser'; +import { publicUserSelector } from 'redux/selectors/publicUser'; export const useAuthentication = () => { const dispatch = useAppDispatch(); const clerkUser = useAppSelector(clerkUserSelector); + const publicUser = useAppSelector(publicUserSelector); const activeURL = window.location.href; const isClerkURL = activeURL.includes(AppRoutes.ClerkHomePage); + const isPublicURL = !isClerkURL; useEffect(() => { if (clerkUser.status === APIResponseStatus.NotStarted) { @@ -19,7 +23,16 @@ export const useAuthentication = () => { dispatch(loadClerkUser()); } } - }, [clerkUser.status, isClerkURL, dispatch]); + if (publicUser.status === APIResponseStatus.NotStarted) { + if (isPublicURL) { + dispatch(loadPublicUser()); + } + } + }, [clerkUser.status, publicUser.status, isClerkURL, isPublicURL, dispatch]); - return { isAuthenticated: clerkUser.isAuthenticated, isClerkUI: isClerkURL }; + return { + isAuthenticated: clerkUser.isAuthenticated, + isClerkUI: isClerkURL, + publicUser: publicUser, + }; }; diff --git a/frontend/packages/vkt/src/pages/LogoutSuccess.tsx b/frontend/packages/vkt/src/pages/LogoutSuccess.tsx new file mode 100644 index 000000000..9d0b99d4b --- /dev/null +++ b/frontend/packages/vkt/src/pages/LogoutSuccess.tsx @@ -0,0 +1,53 @@ +import { ArrowBackIosOutlined as ArrowBackIosOutlinedIcon } from '@mui/icons-material'; +import { Grid, Paper } from '@mui/material'; +import { CustomButtonLink, H1, HeaderSeparator, Text } from 'shared/components'; +import { Color, Variant } from 'shared/enums'; + +import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; +import { AppRoutes } from 'enums/app'; + +export const LogoutSuccess: React.FC = () => { + const translateCommon = useCommonTranslation(); + const { t } = usePublicTranslation({ + keyPrefix: 'vkt.component.logoutSuccessPage', + }); + + return ( + + + } + className="color-secondary-dark" + > + {translateCommon('backToHomePage')} + + + +

{t('heading')}

+ +
+ + + {t('info')} + + {translateCommon('backToHomePage')} + + + +
+ ); +}; diff --git a/frontend/packages/vkt/src/redux/reducers/publicUser.ts b/frontend/packages/vkt/src/redux/reducers/publicUser.ts new file mode 100644 index 000000000..8aff9d421 --- /dev/null +++ b/frontend/packages/vkt/src/redux/reducers/publicUser.ts @@ -0,0 +1,42 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { APIResponseStatus } from 'shared/enums'; + +import { PublicPerson } from 'interfaces/publicPerson'; + +interface PublicUserState extends Omit { + status: APIResponseStatus; + isAuthenticated: boolean; +} + +const initialState: PublicUserState = { + status: APIResponseStatus.NotStarted, + isAuthenticated: false, + firstName: '', + lastName: '', +}; + +const publicUserSlice = createSlice({ + name: 'publicUser', + initialState, + reducers: { + loadPublicUser(state) { + state.status = APIResponseStatus.InProgress; + }, + rejectPublicUser(state) { + state.status = APIResponseStatus.Error; + state.isAuthenticated = initialState.isAuthenticated; + state.firstName = initialState.firstName; + state.lastName = initialState.lastName; + }, + storePublicUser(state, action: PayloadAction) { + state.status = APIResponseStatus.Success; + state.isAuthenticated = true; + state.firstName = action.payload.firstName; + state.lastName = action.payload.lastName; + }, + }, +}); + +export const publicUserReducer = publicUserSlice.reducer; +export const { loadPublicUser, rejectPublicUser, storePublicUser } = + publicUserSlice.actions; diff --git a/frontend/packages/vkt/src/redux/sagas/index.ts b/frontend/packages/vkt/src/redux/sagas/index.ts index 916b900da..b9f653c4f 100644 --- a/frontend/packages/vkt/src/redux/sagas/index.ts +++ b/frontend/packages/vkt/src/redux/sagas/index.ts @@ -7,12 +7,14 @@ import { watchClerkNewExamDate } from 'redux/sagas/clerkNewExamDate'; import { watchClerkUser } from 'redux/sagas/clerkUser'; import { watchPublicEnrollments } from 'redux/sagas/publicEnrollment'; import { watchPublicExamEvents } from 'redux/sagas/publicExamEvent'; +import { watchPublicUser } from 'redux/sagas/publicUser'; export default function* rootSaga() { yield all([ watchListExamEvents(), watchClerkNewExamDate(), watchClerkUser(), + watchPublicUser(), watchPublicEnrollments(), watchPublicExamEvents(), watchClerkExamEventOverview(), diff --git a/frontend/packages/vkt/src/redux/sagas/publicUser.ts b/frontend/packages/vkt/src/redux/sagas/publicUser.ts new file mode 100644 index 000000000..4eda42bb1 --- /dev/null +++ b/frontend/packages/vkt/src/redux/sagas/publicUser.ts @@ -0,0 +1,33 @@ +import { call, put, takeLatest } from '@redux-saga/core/effects'; +import { AxiosResponse } from 'axios'; +import { HTTPStatusCode } from 'shared/enums'; + +import axiosInstance from 'configs/axios'; +import { APIEndpoints } from 'enums/api'; +import { PublicPerson } from 'interfaces/publicPerson'; +import { + loadPublicUser, + rejectPublicUser, + storePublicUser, +} from 'redux/reducers/publicUser'; + +function* loadPublicUserSaga() { + try { + const response: AxiosResponse = yield call( + axiosInstance.get, + APIEndpoints.PublicUser + ); + + if (response.status === HTTPStatusCode.Ok) { + yield put(storePublicUser(response.data)); + } else { + yield put(rejectPublicUser()); + } + } catch (error) { + yield put(rejectPublicUser()); + } +} + +export function* watchPublicUser() { + yield takeLatest(loadPublicUser.type, loadPublicUserSaga); +} diff --git a/frontend/packages/vkt/src/redux/selectors/publicUser.ts b/frontend/packages/vkt/src/redux/selectors/publicUser.ts new file mode 100644 index 000000000..59a32bc83 --- /dev/null +++ b/frontend/packages/vkt/src/redux/selectors/publicUser.ts @@ -0,0 +1,3 @@ +import { RootState } from 'configs/redux'; + +export const publicUserSelector = (state: RootState) => state.publicUser; diff --git a/frontend/packages/vkt/src/redux/store/index.ts b/frontend/packages/vkt/src/redux/store/index.ts index f6d38c25f..6ea13e37a 100644 --- a/frontend/packages/vkt/src/redux/store/index.ts +++ b/frontend/packages/vkt/src/redux/store/index.ts @@ -12,6 +12,7 @@ import { clerkNewExamDateReducer } from 'redux/reducers/clerkNewExamDate'; import { clerkUserReducer } from 'redux/reducers/clerkUser'; import { publicEnrollmentReducer } from 'redux/reducers/publicEnrollment'; import { publicExamEventReducer } from 'redux/reducers/publicExamEvent'; +import { publicUserReducer } from 'redux/reducers/publicUser'; import rootSaga from 'redux/sagas/index'; const persistConfig = { @@ -25,6 +26,7 @@ const reducer = combineReducers({ APIError: APIErrorReducer, clerkListExamEvent: clerkListExamEventReducer, clerkUser: clerkUserReducer, + publicUser: publicUserReducer, publicEnrollment: publicEnrollmentReducer, clerkNewExamDate: clerkNewExamDateReducer, publicExamEvent: publicExamEventReducer, diff --git a/frontend/packages/vkt/src/routers/AppRouter.tsx b/frontend/packages/vkt/src/routers/AppRouter.tsx index 74ae6ba86..26dffb7d3 100644 --- a/frontend/packages/vkt/src/routers/AppRouter.tsx +++ b/frontend/packages/vkt/src/routers/AppRouter.tsx @@ -15,6 +15,7 @@ import { ClerkEnrollmentOverviewPage } from 'pages/ClerkEnrollmentOverviewPage'; import { ClerkExamEventCreatePage } from 'pages/ClerkExamEventCreatePage'; import { ClerkExamEventOverviewPage } from 'pages/ClerkExamEventOverviewPage'; import { ClerkHomePage } from 'pages/ClerkHomePage'; +import { LogoutSuccess } from 'pages/LogoutSuccess'; import { NotFoundPage } from 'pages/NotFoundPage'; import { PrivacyPolicyPage } from 'pages/PrivacyPolicyPage'; import { PublicEnrollmentPage } from 'pages/PublicEnrollmentPage'; @@ -168,6 +169,14 @@ export const AppRouter: FC = () => { } /> + + + + } + /> Date: Wed, 11 Oct 2023 11:46:23 +0300 Subject: [PATCH 72/72] =?UTF-8?q?AKR:OTR(Frontend)=20OPHAKRKEH-510=20Selos?= =?UTF-8?q?teiden=20k=C3=A4=C3=A4nn=C3=B6kset=20(#585)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * AKR(Frontend) OPHAKRKEH-510: accessibility translations * OTR(Frontend) OPHAKRKEH-510: english privacy statement * AKR:OTR(Frontend) OPHAKRKEH-510: privacy and accessibility translations * AKR(Frontend) OPHAKRKEH-510: accessibility links fixed * AKR:OTR(Frontend) OPHAKRKEH-510: statement footer links fixed [deploy] * AKR:OTR(Frontend) OPHAKRKEH-510: minor review fixes --- .../akr/public/i18n/en-GB/accessibility.json | 84 ++++++++- .../akr/public/i18n/en-GB/translation.json | 2 +- .../akr/public/i18n/sv-SE/accessibility.json | 84 ++++++++- .../akr/public/i18n/sv-SE/translation.json | 2 +- .../otr/public/i18n/en-GB/accessibility.json | 79 +++++++- .../otr/public/i18n/en-GB/privacy.json | 170 +++++++++++++++++- .../otr/public/i18n/en-GB/translation.json | 4 +- .../otr/public/i18n/sv-SE/accessibility.json | 79 +++++++- .../otr/public/i18n/sv-SE/privacy.json | 170 +++++++++++++++++- .../otr/public/i18n/sv-SE/translation.json | 4 +- 10 files changed, 666 insertions(+), 12 deletions(-) diff --git a/frontend/packages/akr/public/i18n/en-GB/accessibility.json b/frontend/packages/akr/public/i18n/en-GB/accessibility.json index 0967ef424..b8f51b59e 100644 --- a/frontend/packages/akr/public/i18n/en-GB/accessibility.json +++ b/frontend/packages/akr/public/i18n/en-GB/accessibility.json @@ -1 +1,83 @@ -{} +{ + "akr": { + "accessibility": { + "content": { + "administrativeAgency": { + "description": "If you notice any accessibility issues on the website, please first send feedback to us, i.e. the site administrator. It may take us up to 14 days to reply. If you are not satisfied with the reply or you do not receive a reply at all within two weeks,", + "extraDescription": "You can find information about how to submit a notification and how the matter will be handled from the website of the Regional State Administrative Agency for Southern Finland.", + "links": { + "link": "https://www.webaccessibility.fi/rights-of-users/", + "title": "you can submit a report to the Regional State Administrative Agency for Southern Finland (opens in a new window)" + }, + "title": "Regulatory authority" + }, + "caveats": { + "description": "Non-accessible content and its limitations", + "extraDescription": "Accessibility requirements, which are not fulfilled", + "items": [ + { + "description": "The progression of heading ranks is not in order on every page.", + "claim": "1.3.1 Info and Relationships", + "title": "Perceivable: Progression of heading ranks" + }, + { + "description": "The header of the first table column is empty", + "claim": "1.3.1 Info and Relationships", + "title": "Perceivable: Missing table header" + }, + { + "description": "The language selector component has no traditional label. There is, however, an aria-label.", + "claim": "2.4.6 Headings and Labels", + "title": "Operable: Label is missing from the language selector component" + }, + { + "description": "The ‘Display on page’ component of the table has no traditional label. There is, however, an aria-label.", + "claim": "2.4.6 Headings and Labels", + "title": "Operable: Label is missing from a table’s ‘Display on page’ component" + } + ] + }, + "contactAdministrativeAgency": { + "links": { + "email": { + "link": "saavutettavuus@avi.fi", + "title": "saavutettavuus@avi.fi" + }, + "website": { + "ariaLabel": "Accessibility Monitoring Unit, opens in a new window", + "link": "https://www.webaccessibility.fi/", + "title": "Accessibility Monitoring Unit website" + } + }, + "name": "Regional State Administrative Agency for Southern Finland", + "phone": "telephone switchboard 0295 016 000", + "title": "Contact details of the regulatory authority", + "unit": "Accessibility control unit" + }, + "feedback": { + "description": "By email", + "subtitle": "Did you notice an accessibility issue in our digital service? Let us know and we will do the best we can to fix it.", + "title": "Feedback and contact details" + }, + "furtherImprove": { + "description": "We are committed to improving the accessibility of digital services.", + "extraDescription": "Did you notice an accessibility issue? Let us know and we will do the best we can to fix it.", + "subtitle": "We offer support to users for whom digital services are not accessible", + "title": "We are continuously working to improve accessibility" + }, + "nonAccessible": { + "description": "The website is not yet fully compliant", + "title": "Non-accessible content" + }, + "status": { + "description": "Partly compliant with accessibility requirements", + "title": "The state of accessibility of the digital service" + } + }, + "heading": { + "description": "This accessibility statement applies to the service akr.opintopolku.fi and has been created / updated on 4 October 2023. The service is subject to the Act on the Provision of Digital Services, which requires public sector to comply with accessibility requirements in their digital services. The accessibility of the service has been evaluated by an external expert organisation.", + "title": "Accessibility statement for the Register of Authorised Translators" + } + } + } +} diff --git a/frontend/packages/akr/public/i18n/en-GB/translation.json b/frontend/packages/akr/public/i18n/en-GB/translation.json index 9c36e0b75..a0178d830 100644 --- a/frontend/packages/akr/public/i18n/en-GB/translation.json +++ b/frontend/packages/akr/public/i18n/en-GB/translation.json @@ -215,7 +215,7 @@ }, "links": { "accessibility": { - "text": "Accessibility statement (in Finnish)" + "text": "Accessibility statement" }, "akrHomepage": { "ariaLabel": "Examination system of authorised translators (oph.fi), open in a new tab", diff --git a/frontend/packages/akr/public/i18n/sv-SE/accessibility.json b/frontend/packages/akr/public/i18n/sv-SE/accessibility.json index 0967ef424..e79af50ed 100644 --- a/frontend/packages/akr/public/i18n/sv-SE/accessibility.json +++ b/frontend/packages/akr/public/i18n/sv-SE/accessibility.json @@ -1 +1,83 @@ -{} +{ + "akr": { + "accessibility": { + "content": { + "administrativeAgency": { + "description": "Om du upptäcker tillgänglighetsproblem på webbplatsen, påtala det först för oss, dvs. webbplatsens administratörer. Det kan ta 14 dagar innan du får ett svar. Om du inte är nöjd med det svar du har fått eller inte får något svar alls inom två veckor, ", + "extraDescription": "På regionförvaltningsverkets webbplats finns det noggrant beskrivet hur du kan lämna in en anmälan och hur ärendet behandlas.", + "links": { + "link": "https://www.tillganglighetskrav.fi/dina-rattigheter/", + "title": "kan du göra en anmälan till Regionförvaltningsverket i Södra Finland (öppnas i ett nytt fönster)." + }, + "title": "Tillsynsmyndighet" + }, + "caveats": { + "description": "Icke tillgängligt innehåll och dess brister", + "extraDescription": "Tillgänglighetskrav som inte uppfylls", + "items": [ + { + "description": "Rubriknivåerna avancerar inte i ordningsföljd på alla sidor.", + "claim": "1.3.1 Information och relationer", + "title": "Möjlig att uppfatta: Hur rubriknivåerna avancerar" + }, + { + "description": "Rubriken i den första kolumnen i tabellen är tom.", + "claim": "1.3.1 Information och relationer", + "title": "Möjlig att uppfatta: Tabellrubrik saknas" + }, + { + "description": "Språkvalskomponenten har ingen traditionell ledtext/etikett. Det finns emellertid en Aria-label.", + "claim": "2.4.6 Rubriker och ledtexter/etiketter", + "title": "Hanterbar: Ledtext/etikett saknas i språkvalskomponenten" + }, + { + "description": "Komponenten ”Visa på sida” i tabellen har ingen traditionell ledtext/etikett. Det finns emellertid en Aria-label.", + "claim": "2.4.6 Rubriker och ledtexter/etiketter", + "title": "Hanterbar: Ledtext/etikett saknas i komponenten ”Visa på sida”" + } + ] + }, + "contactAdministrativeAgency": { + "links": { + "email": { + "link": "webbtillganglighet@rfv.fi", + "title": "webbtillganglighet@rfv.fi" + }, + "website": { + "ariaLabel": "Enheten för tillgänglighetstillsyn webbplats, öppnas i ett nytt fönster", + "link": "https://www.tillganglighetskrav.fi/", + "title": "Enheten för tillgänglighetstillsyn webbplats" + } + }, + "name": "Regionförvaltningsverket i Södra Finland", + "phone": "telefonnummer växel 0295 016 000", + "title": "Tillsynsmyndighetens kontaktuppgifter", + "unit": "Enheten för tillgänglighetstillsyn" + }, + "feedback": { + "description": "Per e-post", + "subtitle": "Märkte du ett fel i våra digitala tjänster? Berätta om det för oss och vi gör vårt bästa för att åtgärda bristen.", + "title": "Respons och kontaktuppgifter" + }, + "furtherImprove": { + "description": "Vi har förbundit oss till att förbättra tillgängligheten i digitala tjänster.", + "extraDescription": "Märkte du en brist i tillgängligheten? Berätta om det för oss och vi gör vårt bästa för att åtgärda bristen.", + "subtitle": "Vi erbjuder stöd åt de användare för vilka digitala tjänster inte är tillgängliga", + "title": "Vi arbetar ständigt för att förbättra tillgängligheten" + }, + "nonAccessible": { + "description": "Webbplatsen är ännu inte till alla delar förenlig med kraven", + "title": "Icke tillgängligt innehåll" + }, + "status": { + "description": "Uppfyller tillgänglighetskraven delvis", + "title": "Status för tillgänglighet i den digitala tjänsten" + } + }, + "heading": { + "description": "Detta tillgänglighetsutlåtande gäller tjänsten akr.opintopolku.fi och har utarbetats / uppdaterats 04.10.2023. Tjänsten omfattas av lagen om tillhandahållande av digitala tjänster, där det förutsätts att offentliga nättjänster ska vara tillgängliga. Tjänstens tillgänglighet har bedömts av en utomstående expertorganisation.", + "title": "Tillgänglighetsutlåtande för registret över auktoriserade translatorer" + } + } + } +} diff --git a/frontend/packages/akr/public/i18n/sv-SE/translation.json b/frontend/packages/akr/public/i18n/sv-SE/translation.json index 16c1348c7..3083c9536 100644 --- a/frontend/packages/akr/public/i18n/sv-SE/translation.json +++ b/frontend/packages/akr/public/i18n/sv-SE/translation.json @@ -215,7 +215,7 @@ }, "links": { "accessibility": { - "text": "Tillgänglighetsdirektiv (på finska)" + "text": "Tillgänglighetsdirektiv" }, "akrHomepage": { "ariaLabel": "Examenssystemet för auktoriserade translatorer (oph.fi), öppnas i en ny flik", diff --git a/frontend/packages/otr/public/i18n/en-GB/accessibility.json b/frontend/packages/otr/public/i18n/en-GB/accessibility.json index 0967ef424..ad452cf24 100644 --- a/frontend/packages/otr/public/i18n/en-GB/accessibility.json +++ b/frontend/packages/otr/public/i18n/en-GB/accessibility.json @@ -1 +1,78 @@ -{} +{ + "otr": { + "accessibility": { + "content": { + "administrativeAgency": { + "description": "If you notice any accessibility issues on the website, please first send feedback to us, i.e. the site administrator. It may take us up to 14 days to reply. If you are not satisfied with the reply or you do not receive a reply at all within two weeks, ", + "extraDescription": "You can find information about how to submit a notification and how the matter will be handled from the website of the Regional State Administrative Agency for Southern Finland.", + "links": { + "link": "https://www.webaccessibility.fi/rights-of-users/", + "title": "you can submit a report to the Regional State Administrative Agency for Southern Finland (opens in a new window)." + }, + "title": "Regulatory authority" + }, + "caveats": { + "description": "Non-accessible content and its limitations", + "extraDescription": "Accessibility requirements, which are not fulfilled", + "items": [ + { + "description": "The target of the EDUFI logo link cannot be determined by a screen reader.", + "claim": "2.4.4 - Link Purpose (In Context)", + "title": "Operable: Image link target unclear" + }, + { + "description": "Drop-down menus do not describe an input error.", + "claim": "3.3.1 Error Identification", + "title": "Understandable: Input error is not described" + }, + { + "description": "The system does not offer suggestions for correcting an error.", + "claim": "3.3.3 Error Suggestion", + "title": "Understandable: Error suggestion is missing" + } + ] + }, + "contactAdministrativeAgency": { + "links": { + "email": { + "link": "saavutettavuus@avi.fi", + "title": "saavutettavuus@avi.fi" + }, + "website": { + "ariaLabel": "Accessibility Monitoring Unit, opens in a new window", + "link": "https://www.webaccessibility.fi/", + "title": "Accessibility Monitoring Unit website" + } + }, + "name": "Regional State Administrative Agency for Southern Finland", + "phone": "telephone switchboard 0295 016 000", + "title": "Contact details of the regulatory authority", + "unit": "Accessibility control unit" + }, + "feedback": { + "description": "By email", + "subtitle": "Did you notice an accessibility issue in our digital service? Let us know and we will do the best we can to fix it.", + "title": "Feedback and contact details" + }, + "furtherImprove": { + "description": "We are committed to improving the accessibility of digital services.", + "extraDescription": "Did you notice an accessibility issue? Let us know and we will do the best we can to fix it.", + "subtitle": "We offer support to users for whom digital services are not accessible", + "title": "We are continuously working to improve accessibility" + }, + "nonAccessible": { + "description": "The website is not yet fully compliant", + "title": "Non-accessible content" + }, + "status": { + "description": "Partly compliant with accessibility requirements", + "title": "The state of accessibility of the digital service" + } + }, + "heading": { + "description": "This accessibility statement applies to the service otr.opintopolku.fi and has been created / updated on 4 October 2023. The service is subject to the Act on the Provision of Digital Services, which requires public sector to comply with accessibility requirements in their digital services. The accessibility of the service has been evaluated by an external expert organisation.", + "title": "Accessibility statement for the Register of Legal Interpreters" + } + } + } +} diff --git a/frontend/packages/otr/public/i18n/en-GB/privacy.json b/frontend/packages/otr/public/i18n/en-GB/privacy.json index 0967ef424..928697a89 100644 --- a/frontend/packages/otr/public/i18n/en-GB/privacy.json +++ b/frontend/packages/otr/public/i18n/en-GB/privacy.json @@ -1 +1,169 @@ -{} +{ + "otr": { + "privacy": { + "automatedDecisions": { + "description": "The data contained in the Register is not used for profiling of automated decision-making.", + "heading": "Information on the existence of automated decision-making, including profiling, and, at least in these cases, relevant information on the logic of the processing, as well as the significance and possible consequences of this processing for the data subject" + }, + "common": { + "email": "E-mail", + "group": "Group", + "phoneSwitch": "Telephone: +358 (0)29 533 1000 (switchboard)" + }, + "complaints": { + "description": "The data subject has the right to lodge a complaint with a supervisory authority, in particular in the Member State where they have their habitual residence or place of work, or where the alleged breach of the GDPR has taken place in.", + "heading": "The right to lodge a complaint with a supervisory authority" + }, + "dataContents": { + "group1": { + "civilRegistry": { + "description": "The Register verifies the data subject’s address and personal data from the Population Information System." + }, + "contactChanges": { + "description": "The data subject must notify the Finnish National Agency for Education by e-mail of any changes to their contact details" + }, + "dataDeletion": { + "description1": "The information concerning the data subject will be deleted without undue delay and no later than 2 months after the registration has expired or been cancelled.", + "description2": "The processing of the data subject’s personal data is necessary and proportionate for the performance of a task carried out by the authority in the public interest." + }, + "dataProcessing": { + "description": "No special categories of personal data are processed. No confidential personal data are processed." + }, + "givenDetails": { + "description": "Personal data: name, personal identity code, address, e-mail address, telephone number, and place of residence.", + "heading": "Information provided by the data subject" + }, + "prerequisites": { + "heading": "As to the studies which meet the requirements for registration in the Register of Legal Interpreters, an indication of which studies the interpreter has completed", + "ul1": "a specialist vocational qualification in legal interpreting or", + "ul2": "a higher education degree appropriate for the work of an interpreter and legal interpreting studies worth at least 35 credits or of a similar scope" + }, + "publishPermission": { + "description": "Based on the permission to publish, the name, operating area, contact details, and language pair(s) in which the interpreter is registered are displayed in the search engine. The interpreter entered in the Register has the right to object to the publication of information concerning them." + }, + "name": "Legal interpreters entered in the Register", + "otherDetails": { + "heading": "Other entered information", + "ul1": "language pair (two-way qualification)", + "ul2": "declaration of a legal interpreter", + "ul3": "validity of the record", + "ul4": "permissions to publish for the following information" + }, + "validity": { + "description1": "The entry in the Register is valid for five years.", + "description2": "The Finnish National Agency for Education will send an automatic reminder 3 months before the expiry of the registration. Renewal can be applied for not earlier than 6 months before the expiry date. If the registration expires, the interpreter’s information will be removed from the public search engine. It is possible to apply for renewal even after this." + } + }, + "group2": { + "civilRegistry": { + "description": "The Register verifies the official user’s address and personal data from the Population Information System." + }, + "givenDetails": { + "description": "Personal data: name, personal identity code, address, e-mail address, telephone number, and place of residence." + }, + "name": "Officials responsible for maintaining the Register" + }, + "heading": "Data content of the Register / categories of personal data processed" + }, + "dataSources": { + "description": "The data is collected in group 1 directly from the legal interpreters themselves and from the Population Information System.", + "heading": "Information on the origin of the personal data and, where applicable, whether the data have been obtained from publicly available sources" + }, + "dataTransfers": { + "description": "According to rule, no data are disclosed or transferred from the Register outside the EU or the European Economic Area.", + "heading": "Information on the transfer of data to third countries and information on the safeguard measures used (including information on the existence or absence of a Commission decision on the adequacy of data protection) and the means of obtaining a copy of information about their content" + }, + "description": "General Data Protection Regulation (EU) 2016/679 (GDPR).", + "handlingPurpose": { + "description1": "The Register of Legal Interpreters contains information on legal interpreters who have been approved by the Board for the Register of Legal Interpreters on application to be entered in the Register of Legal Interpreters.", + "description2": "The Register’s search engine enables a user to search for legal interpreters for an interpreting assignment. Interpreters can be contacted directly using the public contact details they have provided.", + "description3": "The Register stores and processes personal data necessary for managing the registration and the publication of the contact details mentioned above.", + "description4": "When consent is sought for the publication of the contact details of a legal interpreter, the data subject’s consent will be requested in advance while at the same time providing an explanation on this form of how the data will be processed.", + "heading": "Purpose and legal basis for the processing of personal data", + "law": { + "conclusion": { + "description": "The publicity and processing of information recorded in the Register of Legal Interpreters is governed by the Act on the Openness of Government Activities (<0>) and by the Data Protection Act (<1>).", + "law1": { + "content": "621/1999", + "link": "https://www.finlex.fi/fi/laki/ajantasa/1999/19990621" + }, + "law2": { + "content": "1050/2018", + "link": "https://www.finlex.fi/fi/laki/ajantasa/2018/20181050" + } + }, + "heading": "Legal grounds", + "part1": { + "description": "Act on the Register of Legal Interpreters", + "link": "https://www.finlex.fi/fi/laki/ajantasa/2015/20151590" + }, + "part2": { + "description": "Government Decree on the Register of Legal Interpreters", + "link": "https://www.finlex.fi/fi/laki/alkup/2016/20160177" + } + } + }, + "heading": "Privacy policy / Disclosure", + "holdingPeriod": { + "description1": "The information concerning the legal interpreters entered in the Register will be deleted from the Register without undue delay and no later than 2 months after the registration has expired or been cancelled.", + "description2": "The retention periods of personal data in the service are in accordance with retention periods specified in the archives formation plan and the records management plan.", + "description3": "Documents whose retention period has expired will be deleted from the system annually.", + "heading": "Retention period of personal data" + }, + "receivers": { + "description1": "According to the Act on the Openness of Government Activities (21.5.1999/621), Finnish National Agency for Education must disclose information from a public document to a requester, even if the document contains personal data. The disclosure or delivery of confidential information requires special grounds: status as a party or statutory right to access the information or the consent of the person whose protection is provided for by the confidentiality provision.", + "description2": "Personal data may be disclosed for scientific or historical research or the compilation of statistics under the conditions set out in Section 4 of the Data Protection Act (1050/2018).", + "description3": "The service providers (personal data processors) of the information system have access to the personal data contained in the Register to the extent specified by the Finnish National Agency for Education.", + "description4": "The data will not be disclosed for direct marketing purposes.", + "heading": "Recipients or categories of recipients of personal data" + }, + "registeredRights": { + "description1": "The data subject has the right to obtain confirmation from the controller that their personal data are or are not being processed. The controller must provide a copy of the processed personal data upon request.", + "description2": "The data subject has the right to obtain from the controller, without undue delay, the rectification of inaccurate or erroneous personal data relating to the data subject. The data subject must identify and justify what information they require to be rectified, what they consider to be the correct information, and how the correction is to be made.", + "description3": "If the processing is based on point (a) of Article 6(1) or on point (a) of Article 9(2) of the Data Protection Regulation, i.e. the data subject’s consent, the data subject has the right to erasure.", + "description4": "The data subject has the right to restrict processing in certain circumstances.", + "description5": "The data subject has the right that the controller informs the rectification or erasure or restriction of processing of the data subject’s personal data to the third party to whom the data have been disclosed in the event the data have been disclosed further.", + "description6": "The data subject has the right to data portability from one system to another if the processing is carried out automatically.", + "description7": "Requests related to the use of rights should be addressed to the contact person of the Register: Finnish National Agency for Education, P.O. Box 380, 00531 Helsinki, Finland. The data subject must include in the request for access the information necessary for the search (name and personal identity code).", + "description8": "If less than one year has elapsed since the data subject exercised their right of access, the controller may charge a fee based on the administrative costs of providing the information (Article 12(5)).", + "heading": "Rights of the data subject", + "rights": { + "right1": "Right of access to personal data", + "right2": "Right to rectification", + "right3": "Right to erasure", + "right4": "Right to restrict processing", + "right5": "Right to object", + "right6": "Right to data portability" + } + }, + "registerName": { + "contents": { + "description": "The Register consists of the following", + "item1": "legal interpreters entered in the Register", + "item2": "public search engine" + }, + "description": "Register of Legal Interpreters.", + "heading": "The name of the register" + }, + "registrar": { + "contact": { + "address": "P.O. Box 380, 00531 Helsinki, Finland (street address Hakaniemenranta 6, 00530 Helsinki).", + "name": "Finnish National Agency for Education", + "otherDetails": "Other contact details", + "phoneSwitch": "Telephone switchboard: 029 533 1000" + }, + "heading": "Controller" + }, + "registrarContactPerson": { + "heading": "Representative of the controller (contact person)", + "liable": { + "description": "Contact details of the data protection officer", + "name": "Jyrki Tuohela" + }, + "person": { + "name": "Saara Kalajoki" + } + } + } + } +} diff --git a/frontend/packages/otr/public/i18n/en-GB/translation.json b/frontend/packages/otr/public/i18n/en-GB/translation.json index 9af752181..ab79b626f 100644 --- a/frontend/packages/otr/public/i18n/en-GB/translation.json +++ b/frontend/packages/otr/public/i18n/en-GB/translation.json @@ -138,7 +138,7 @@ }, "links": { "accessibility": { - "text": "Accessibility statement (in Finnish)" + "text": "Accessibility statement" }, "contact": { "title": "Feedback and development suggestions" @@ -149,7 +149,7 @@ "text": "Information on the Register of Legal Interpreters (oph.fi)" }, "privacy": { - "text": "Privacy policy (in Finnish)" + "text": "Privacy policy" } } }, diff --git a/frontend/packages/otr/public/i18n/sv-SE/accessibility.json b/frontend/packages/otr/public/i18n/sv-SE/accessibility.json index 0967ef424..637b89f6b 100644 --- a/frontend/packages/otr/public/i18n/sv-SE/accessibility.json +++ b/frontend/packages/otr/public/i18n/sv-SE/accessibility.json @@ -1 +1,78 @@ -{} +{ + "otr": { + "accessibility": { + "content": { + "administrativeAgency": { + "description": "Om du upptäcker tillgänglighetsproblem på webbplatsen, påtala det först för oss, dvs. webbplatsens administratörer. Det kan ta 14 dagar innan du får ett svar. Om du inte är nöjd med det svar du har fått eller inte får något svar alls inom två veckor, ", + "extraDescription": "På regionförvaltningsverkets webbplats finns det noggrant beskrivet hur du kan lämna in en anmälan och hur ärendet behandlas.", + "links": { + "link": "https://www.tillganglighetskrav.fi/dina-rattigheter/", + "title": "kan du göra en anmälan till Regionförvaltningsverket i Södra Finland (öppnas i ett nytt fönster)." + }, + "title": "Tillsynsmyndighet" + }, + "caveats": { + "description": "Icke tillgängligt innehåll och dess brister", + "extraDescription": "Tillgänglighetskrav som inte uppfylls", + "items": [ + { + "description": "Objektet för OPH-logotypen på sidan framgår inte med skärmläsare.", + "claim": "2.4.4 - Syftet med en länk (i sammanhanget)", + "title": "Hanterbar: Bildlänkens objekt oklart" + }, + { + "description": "Rullgardinsmenyerna meddelar inte om inmatningsfel.", + "claim": "3.3.1 Identifiering av fel", + "title": "Begriplig: Inmatningsfel meddelas inte" + }, + { + "description": "Systemet erbjuder inte förslag för att rätta felet.", + "claim": "3.3.3 Förslag vid felhantering", + "title": "Begriplig: Förslag till rättelse av fel saknas" + } + ] + }, + "contactAdministrativeAgency": { + "links": { + "email": { + "link": "webbtillganglighet@rfv.fi", + "title": "webbtillganglighet@rfv.fi" + }, + "website": { + "ariaLabel": "Enheten för tillgänglighetstillsyn webbplats, öppnas i ett nytt fönster", + "link": "https://www.tillganglighetskrav.fi/", + "title": "Enheten för tillgänglighetstillsyn webbplats" + } + }, + "name": "Regionförvaltningsverket i Södra Finland", + "phone": "telefonnummer växel 0295 016 000", + "title": "Tillsynsmyndighetens kontaktuppgifter", + "unit": "Enheten för tillgänglighetstillsyn" + }, + "feedback": { + "description": "Per e-post", + "subtitle": "Märkte du ett fel i våra digitala tjänster? Berätta om det för oss och vi gör vårt bästa för att åtgärda bristen.", + "title": "Respons och kontaktuppgifter" + }, + "furtherImprove": { + "description": "Vi har förbundit oss till att förbättra tillgängligheten i digitala tjänster.", + "extraDescription": "Märkte du en brist i tillgängligheten? Berätta om det för oss och vi gör vårt bästa för att åtgärda bristen.", + "subtitle": "Vi erbjuder stöd åt de användare för vilka digitala tjänster inte är tillgängliga", + "title": "Vi arbetar ständigt för att förbättra tillgängligheten" + }, + "nonAccessible": { + "description": "Webbplatsen är ännu inte till alla delar förenlig med kraven", + "title": "Icke tillgängligt innehåll" + }, + "status": { + "description": "Uppfyller tillgänglighetskraven delvis", + "title": "Status för tillgänglighet i den digitala tjänsten" + } + }, + "heading": { + "description": "Detta tillgänglighetsutlåtande gäller tjänsten otr.opintopolku.fi och har utarbetats / uppdaterats 04.10.2023. Tjänsten omfattas av lagen om tillhandahållande av digitala tjänster, där det förutsätts att offentliga nättjänster ska vara tillgängliga. Tjänstens tillgänglighet har bedömts av en utomstående expertorganisation.", + "title": "Tillgänglighetsutlåtande för registret över rättstolkar" + } + } + } +} diff --git a/frontend/packages/otr/public/i18n/sv-SE/privacy.json b/frontend/packages/otr/public/i18n/sv-SE/privacy.json index 0967ef424..fe695af1f 100644 --- a/frontend/packages/otr/public/i18n/sv-SE/privacy.json +++ b/frontend/packages/otr/public/i18n/sv-SE/privacy.json @@ -1 +1,169 @@ -{} +{ + "otr": { + "privacy": { + "automatedDecisions": { + "description": "De uppgifter som registret innehåller används inte för profilering eller automatiskt beslutsfattande.", + "heading": "Förekomsten av automatiserat beslutsfattande, inbegripet profilering, varvid det åtminstone i dessa fall ska lämnas meningsfull information om logiken bakom samt betydelsen och de förutsedda följderna av sådan behandling för den registrerade" + }, + "common": { + "email": "E-post", + "group": "Grupp", + "phoneSwitch": "Telefon: 029 533 1000 (växel)" + }, + "complaints": { + "description": "Den registrerade har rätt att lämna in ett klagomål till en tillsynsmyndighet, särskilt i den medlemsstat där han eller hon har sin hemvist eller sin arbetsplats eller där den påstådda överträdelsen av dataskyddsförordningen har inträffat.", + "heading": "Rätt att lämna in klagomål till en tillsynsmyndighet" + }, + "dataContents": { + "group1": { + "civilRegistry": { + "description": "Registret verifierar i befolkningsdatasystemet adress- och personuppgifterna för den som ska registreras." + }, + "contactChanges": { + "description": "Den registrerade ska underrätta Utbildningsstyrelsen om förändringar som skett i kontaktuppgifterna per e-post" + }, + "dataDeletion": { + "description1": "Uppgifterna om den registrerade raderas utan ogrundat dröjsmål och senast 2 månader efter det att registreringen har upphört eller återkallats.", + "description2": "Behandlingen av personuppgifter som rör den registrerade behövs och är proportionell i en myndighets verksamhet för utförande av en uppgift av allmänt intresse." + }, + "dataProcessing": { + "description": "Inga särskilda kategorier av personuppgifter behandlas. Inga sekretessbelagda personuppgifter behandlas." + }, + "givenDetails": { + "description": "Personuppgifter: namn, personbeteckning, adress, e-postadress, telefonnummer och boningsort.", + "heading": "Uppgifter som den som ska registreras lämnat" + }, + "prerequisites": { + "heading": "När det gäller anteckning av studier som uppfyller kraven för anteckning i registret över rättstolkar anges vilka tolken har avlagt", + "ul1": "specialyrkesexamen för rättstolk/i rättstolkning eller", + "ul2": "högskoleexamen som lämpar sig för tolkuppdrag och studier i rättstolkning som omfattar eller motsvarar minst 35 studiepoäng" + }, + "publishPermission": { + "description": "I sökmotorn anges utifrån publiceringstillståndet rättstolkens namn, verksamhetsområde, kontaktuppgifter samt språkpar (ett eller flera), i vilket (vilka) tolken antecknats i registret. En tolk som antecknats i registret har rätt att förbjuda publicering av uppgifter som rör honom eller henne." + }, + "name": "Rättstolkar som antecknats i registret", + "otherDetails": { + "heading": "Övriga uppgifter som antecknas", + "ul1": "språkpar (i båda riktningarna)", + "ul2": "avgivande av rättstolkens försäkran", + "ul3": "anteckningens giltighet", + "ul4": "tillstånd för publicering gällande följande uppgifter" + }, + "validity": { + "description1": "Registeranteckningen gäller i fem år.", + "description2": "Utbildningsstyrelsen skickar en automatisk påminnelse ur registret 3 månader innan anteckningen går ut. Förnyelse av anteckning kan sökas tidigast 6 månader före utgångsdagen. Om anteckningen går ut, raderas uppgifterna om tolken från den offentliga sökmotorn. Förnyelse kan ansökas även efter detta." + } + }, + "group2": { + "civilRegistry": { + "description": "Registret verifierar i befolkningsdatasystemet adress- och personuppgifterna för användande tjänstemän." + }, + "givenDetails": { + "description": "Personuppgifter: namn, personbeteckning, adress, e-postadress, telefonnummer och boningsort." + }, + "name": "Tjänstemän som sköter administrering av registret" + }, + "heading": "Registrets datainnehåll / kategorier av personuppgifter som behandlas" + }, + "dataSources": { + "description": "Uppgifterna inhämtas hos rättstolkarna själva grupp 1 samt från befolkningsdatasystemet.", + "heading": "Varifrån personuppgifterna kommer och i förekommande fall huruvida de har sitt ursprung i allmänt tillgängliga källor" + }, + "dataTransfers": { + "description": "Uppgifter lämnas inte ut regelmässigt och överförs inte från registret till länder utanför EU eller EES.", + "heading": "Information om överföring av uppgifter till tredjeländer och om vilka skyddsåtgärder som används (inkl. information om huruvida ett beslut av kommissionen om adekvat skyddsnivå föreligger eller saknas) och hur en kopia av dem kan erhållas eller information om innehållet i dem." + }, + "description": "EU:s dataskyddsförordning 2016/679 (GDPR).", + "handlingPurpose": { + "description1": "I registret över rättstolkar finns uppgifter om rättstolkar som Nämnden för registret över rättstolkar efter ansökan har godkänt för att införas i registret över rättstolkar.", + "description2": "I registrets offentliga sökmotor kan rättstolkar sökas för tolkningsuppdrag. Tolkarna kan kontaktas direkt via de offentliga kontaktuppgifter som de angett.", + "description3": "I registret lagras och behandlas personuppgifter som behövs för att administrera anteckningen i registret och för att publicera ovan nämnda kontaktuppgifter.", + "description4": "När samtycke till publicering av uppgifter om rättstolken efterfrågas, bes den registrerade om tillstånd på förhand och i samband med detta ges en redogörelse för hur uppgifterna behandlas på detta formulär.", + "heading": "Syftet med behandling av personuppgifter samt rättslig grund för behandlingen", + "law": { + "conclusion": { + "description": "Bestämmelser om offentligheten för och behandling av uppgifter som antecknats i registret över rättstolkar finns i lagen om offentlighet i myndigheternas verksamhet (<0>) och i dataskyddslagen (<1>).", + "law1": { + "content": "621/1999", + "link": "https://www.finlex.fi/fi/laki/ajantasa/1999/19990621" + }, + "law2": { + "content": "1050/2018", + "link": "https://www.finlex.fi/fi/laki/ajantasa/2018/20181050" + } + }, + "heading": "Rättslig grund", + "part1": { + "description": "Lag om registret över rättstolkar", + "link": "https://www.finlex.fi/fi/laki/ajantasa/2015/20151590" + }, + "part2": { + "description": "Statsrådets förordning om registret över rättstolkar", + "link": "https://www.finlex.fi/fi/laki/alkup/2016/20160177" + } + } + }, + "heading": "Dataskyddsbeskrivning / Tillkännagivande", + "holdingPeriod": { + "description1": "Uppgifterna om de rättstolkar som antecknats i registret raderas från registret utan ogrundat dröjsmål och senast 2 månader efter det att registreringen har upphört eller återkallats.", + "description2": "Lagringstiderna för personuppgifter i tjänsten är förenliga med de lagringstider som bestäms i arkivbildningsplanen och informationsstyrningsplanen.", + "description3": "Handlingar vars lagringstid gått ut förstörs årligen i systemet.", + "heading": "Lagringstid för personuppgifter" + }, + "receivers": { + "description1": "Utbildningsstyrelsen ska enligt lagen om offentlighet i myndigheternas verksamhet (21.5.1999/621) lämna ut uppgifter ur offentliga handlingar till den som begär det, även om handlingen innehåller personuppgifter. Givande eller utlämnande av sekretessbelagda uppgifter kräver en särskild grund: partsställning eller en lagstadgad rätt att få information, eller samtycke av den person som bestämmelserna om sekretess är avsedda att skydda.", + "description2": "Personuppgifter får lämnas ut för vetenskaplig eller historisk forskning eller för statistikföring under de förutsättningar som anges i 4 § i dataskyddslagen (1050/2018).", + "description3": "Informationssystemets tjänsteleverantörer (personuppgiftsbiträden) kan granska personuppgifter som ingår i registret i den omfattning som Utbildningsstyrelsen fastställt.", + "description4": "Uppgifter lämnas inte ut för direktmarknadsföring.", + "heading": "Mottagare eller grupper av mottagare av personuppgifter" + }, + "registeredRights": { + "description1": "Den registrerade har rätt att av den personuppgiftsansvarige få bekräftelse på huruvida personuppgifter som rör honom eller henne håller på att behandlas. Den personuppgiftsansvarige ska på begäran lämna en kopia på personuppgifter som behandlas.", + "description2": "Den registrerade ska ha rätt att av den personuppgiftsansvarige utan onödigt dröjsmål få felaktiga personuppgifter som rör honom eller henne rättade. Den registrerade ska specificera och motivera vilka uppgifter som ska rättas, vad som han eller hon anser är de rätta uppgifterna och hur han eller hon vill att rättelsen ska göras.", + "description3": "Om behandlingen grundar sig på artikel 6.1a eller 9.2a i dataskyddsförordningen, dvs. den registrerades samtycke, har den registrerade rätt till radering.", + "description4": "Den registrerade har rätt att begränsa behandlingen i vissa situationer.", + "description5": "Den registrerade har rätt till att den personuppgiftsansvarige meddelar om rättelse eller radering av personuppgifterna eller om begränsning av behandlingen till den som uppgifterna lämnats ut till, om uppgifter lämnas ut.", + "description6": "Den registrerade har rätt till dataportabilitet, om behandlingen utförs automatiskt.", + "description7": "Begäranden som gäller utövandet av rättigheter ska skickas till registrets kontaktperson: Utbildningsstyrelsen, PB 380, 00531 Helsingfors. Den registrerade ska till granskningsbegäran bifoga de uppgifter som behövs för att söka uppgifterna (namn och personbeteckning).", + "description8": "Om det har gått mindre än ett år sedan den registrerade utövade sin rätt att granska personuppgifter, kan den personuppgiftsansvarige ta ut en avgift för de administrativa kostnader som överlämningen av uppgifter orsakar (artikel 12 [5]).", + "heading": "Den registrerades rättigheter", + "rights": { + "right1": "Rätt till åtkomst", + "right2": "Rätt till rättelse", + "right3": "Rätt till radering", + "right4": "Rätt till begränsning av behandling", + "right5": "Rätt att göra invändningar", + "right6": "Rätt till dataportabilitet" + } + }, + "registerName": { + "contents": { + "description": "Registret består av följande", + "item1": "rättstolkar som antecknats i registret", + "item2": "offentlig sökmotor" + }, + "description": "Registret över rättstolkar.", + "heading": "Registrets namn" + }, + "registrar": { + "contact": { + "address": "PB 380, 00531 Helsingfors (besöksadress Hagnäskajen 6, 00530 Helsingfors).", + "name": "Utbildningsstyrelsen", + "otherDetails": "Övriga kontaktuppgifter", + "phoneSwitch": "Växel: 029 533 1000" + }, + "heading": "Personuppgiftsansvarig" + }, + "registrarContactPerson": { + "heading": "Den personuppgiftsansvariges representant (kontaktperson)", + "liable": { + "description": "Dataskyddsombudets kontaktuppgifter", + "name": "Jyrki Tuohela" + }, + "person": { + "name": "Saara Kalajoki" + } + } + } + } +} diff --git a/frontend/packages/otr/public/i18n/sv-SE/translation.json b/frontend/packages/otr/public/i18n/sv-SE/translation.json index 5c3a0416c..78b74f252 100644 --- a/frontend/packages/otr/public/i18n/sv-SE/translation.json +++ b/frontend/packages/otr/public/i18n/sv-SE/translation.json @@ -138,7 +138,7 @@ }, "links": { "accessibility": { - "text": "Tillgänglighetsdirektiv (på finska)" + "text": "Tillgänglighetsdirektiv" }, "contact": { "title": "Respons och utvecklingsidéer" @@ -149,7 +149,7 @@ "text": "Information om Registret över rättstolkar (oph.fi)" }, "privacy": { - "text": "Dataskyddsbeskrivning (på finska)" + "text": "Dataskyddsbeskrivning" } } },