diff --git a/apps/service-portal/src/auth.ts b/apps/service-portal/src/auth.ts index c0a3cecdfa28..9ccc5a92278d 100644 --- a/apps/service-portal/src/auth.ts +++ b/apps/service-portal/src/auth.ts @@ -50,6 +50,7 @@ const SERVICE_PORTAL_SCOPES = [ ApiScope.healthHealthcare, ApiScope.healthRightsStatus, ApiScope.healthDentists, + ApiScope.healthOrganDonation, ApiScope.healthVaccinations, ApiScope.signatureCollection, ] diff --git a/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts b/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts index 41df1ae0a165..acd7c72f406c 100644 --- a/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts +++ b/libs/api/domains/health-directorate/src/lib/health-directorate.resolver.ts @@ -28,13 +28,17 @@ export class HealthDirectorateResolver { constructor(private api: HealthDirectorateService) {} /* Organ Donation */ - @Query(() => DonorStatus) + @Query(() => DonorStatus, { + name: 'HealthDirectorateOrganDonationGetDonorStatus', + }) @Audit() getDonorStatus(@CurrentUser() user: User): Promise { return this.api.getDonorStatus(user) } - @Query(() => DonationException) + @Query(() => DonationException, { + name: 'HealthDirectorateOrganDonationGetDonationExceptions', + }) @Audit() getDonationExceptions( @Args('locale', { type: () => String, nullable: true }) @@ -44,7 +48,10 @@ export class HealthDirectorateResolver { return this.api.getDonationExceptions(user, locale) } - @Mutation(() => Boolean) + @Mutation(() => Boolean, { + nullable: true, + name: 'HealthDirectorateOrganDonationUpdateDonorStatus', + }) @Audit() async updateDonorStatus( @Args('input') input: DonorStatusInput, @@ -54,7 +61,9 @@ export class HealthDirectorateResolver { } /* Vaccinations */ - @Query(() => [Vaccinations]) + @Query(() => [Vaccinations], { + name: 'HealthDirectorateVaccinationsGetVaccinations', + }) @Audit() getVaccinations( @Args('locale', { type: () => String, nullable: true }) diff --git a/libs/auth/scopes/src/lib/api.scope.ts b/libs/auth/scopes/src/lib/api.scope.ts index 5909569d8540..68f0cfdcbbff 100644 --- a/libs/auth/scopes/src/lib/api.scope.ts +++ b/libs/auth/scopes/src/lib/api.scope.ts @@ -23,6 +23,7 @@ export enum ApiScope { intellectualProperties = '@island.is/assets/ip', healthDentists = '@island.is/health/dentists', healthRightsStatus = '@island.is/health/rights-status', + healthOrganDonation = '@island.is/health/organ-donation', healthVaccinations = '@island.is/health/vaccinations', workMachines = '@island.is/work-machines', vinnueftirlitid = '@island.is/applications/ver', diff --git a/libs/service-portal/core/src/lib/messages.ts b/libs/service-portal/core/src/lib/messages.ts index 61b912fa4dc6..3ca5dcdd88e2 100644 --- a/libs/service-portal/core/src/lib/messages.ts +++ b/libs/service-portal/core/src/lib/messages.ts @@ -1564,4 +1564,8 @@ export const m = defineMessages({ id: 'service.portal:read-more-about', defaultMessage: 'Lesa meira um {arg}', }, + submit: { + id: 'service.portal:submit', + defaultMessage: 'Staðfesta', + }, }) diff --git a/libs/service-portal/health/src/lib/messages.ts b/libs/service-portal/health/src/lib/messages.ts index 6e726ce499a7..2c58f91532bf 100644 --- a/libs/service-portal/health/src/lib/messages.ts +++ b/libs/service-portal/health/src/lib/messages.ts @@ -944,6 +944,77 @@ export const messages = defineMessages({ id: 'sp.health:medicine-calculator-add-to-purchase-label', defaultMessage: 'Bæta {arg} við lyfjakaupalista', }, + organDonation: { + id: 'sp.health:organ-donation', + defaultMessage: 'Líffæragjöf', + }, + organDonationDescription: { + id: 'sp.health:organ-donation-description', + defaultMessage: + 'Íslendingar eru sjálfkrafa skráðir líffæragjafar. Þau sem vilja geta breytt afstöðu sinni.', + }, + readAboutOrganDonation: { + id: 'sp.health:read-about-organ-donation', + defaultMessage: 'Lesa um líffæragjöf', + }, + takeOnOrganDonation: { + id: 'sp.health:take-on-organ-donation', + defaultMessage: 'Afstaða til líffæragjafar', + }, + changeTake: { + id: 'sp.health:change-take', + defaultMessage: 'Breyta afstöðu', + }, + organDonationLink: { + id: 'sp.health:organ-donation-link', + defaultMessage: 'https://island.is/liffaeragjof', + }, + organRegistrationOtherLabel: { + id: 'sp.health:organ-registration-other-label', + defaultMessage: 'Skrifaðu hér', + }, + organRegistrationOtherText: { + id: 'sp.health:organ-registration-other-text', + defaultMessage: 'Vinsamlegast skráðu hvaða líffæri hér', + }, + organLimitationsError: { + id: 'sp.health:organ-limitations-error', + defaultMessage: + 'Textareitur má ekki vera tómur sé þessi valkostur valinn. ', + }, + other: { + id: 'sp.health:other-lower-case', + defaultMessage: 'annað', + }, + registrationComplete: { + id: 'sp.health:registration-complete', + defaultMessage: 'Skráning tókst', + }, + registrationFailed: { + id: 'sp.health:registration-failed', + defaultMessage: 'Skráning mistókst', + }, + iAmOrganDonor: { + id: 'sp.health:i-am-organ-donor', + defaultMessage: 'Ég er líffæragjafi.', + }, + iAmNotOrganDonor: { + id: 'sp.health:i-am-not-organ-donor', + defaultMessage: 'Ég banna líffæragjöf.', + }, + organDonationRegistrationOptIn: { + id: 'sp.health:organ-donation-registration-opt-in', + defaultMessage: 'Við andlát mitt má nota líffæri mín til líffæragjafa.', + }, + organDonationRegistrationException: { + id: 'sp.health:organ-donation-registration-exception', + defaultMessage: + 'Ég gef leyfi fyrir líffæragjöf að undanskildum eftirfarandi líffærum:', + }, + organDonationRegistrationOptOut: { + id: 'sp.health:organ-donation-registration-opt-out', + defaultMessage: 'Ég banna líffæragjöf.', + }, vaccinations: { id: 'sp.health:vaccinations', defaultMessage: 'Bólusetningar', diff --git a/libs/service-portal/health/src/lib/navigation.ts b/libs/service-portal/health/src/lib/navigation.ts index aea192d39b8e..e971865f72cf 100644 --- a/libs/service-portal/health/src/lib/navigation.ts +++ b/libs/service-portal/health/src/lib/navigation.ts @@ -129,6 +129,17 @@ export const healthNavigation: PortalNavigationItem = { }, ], }, + { + name: messages.organDonation, + path: HealthPaths.HealthOrganDonation, + children: [ + { + name: messages.changeRegistration, + path: HealthPaths.HealthOrganDonationRegistration, + navHide: true, + }, + ], + }, ], description: m.healthDescription, } diff --git a/libs/service-portal/health/src/lib/paths.ts b/libs/service-portal/health/src/lib/paths.ts index 7f752a87579c..a8f5ac3f7959 100644 --- a/libs/service-portal/health/src/lib/paths.ts +++ b/libs/service-portal/health/src/lib/paths.ts @@ -3,6 +3,9 @@ export enum HealthPaths { HealthOverview = '/heilsa/yfirlit', + HealthOrganDonation = '/heilsa/liffaeragjof', + HealthOrganDonationRegistration = '/heilsa/liffaeragjof/skraning', + HealthTherapies = '/heilsa/thjalfun', HealthTherapiesPhysical = '/heilsa/thjalfun/sjukrathjalfun', HealthTherapiesSpeech = '/heilsa/thjalfun/talthjalfun', diff --git a/libs/service-portal/health/src/module.tsx b/libs/service-portal/health/src/module.tsx index d70e4585ecc6..a69c0c4efcda 100644 --- a/libs/service-portal/health/src/module.tsx +++ b/libs/service-portal/health/src/module.tsx @@ -54,6 +54,14 @@ const PaymentParticipation = lazy(() => ) const PaymentOverview = lazy(() => import('./screens/Payments/PaymentOverview')) +const OrganDonation = lazy(() => + import('./screens/OrganDonation/OrganDonation'), +) + +const OrganDonationRegistration = lazy(() => + import('./screens/OrganDonationRegistration/RegistrationForm'), +) + const VaccinationsGeneral = lazy(() => import('./screens/Vaccinations/VaccinationsGeneral'), ) @@ -61,6 +69,7 @@ const VaccinationsGeneral = lazy(() => const VaccinationsOther = lazy(() => import('./screens/Vaccinations/VaccinationsOther'), ) + export const healthModule: PortalModule = { name: 'Heilsa', enabled: ({ isCompany }) => !isCompany, @@ -196,25 +205,39 @@ export const healthModule: PortalModule = { enabled: userInfo.scopes.includes(ApiScope.healthDentists), element: , }, + { + name: hm.organDonation, + path: HealthPaths.HealthOrganDonation, + key: 'HealthOrganDonation', + enabled: userInfo.scopes.includes(ApiScope.healthOrganDonation), + element: , + }, + { + name: hm.organDonation, + path: HealthPaths.HealthOrganDonationRegistration, + key: 'HealthOrganDonation', + enabled: userInfo.scopes.includes(ApiScope.healthOrganDonation), + element: , + }, { name: hm.vaccinations, path: HealthPaths.HealthVaccinations, key: 'HealthVaccinations', - enabled: userInfo.scopes.includes(ApiScope.healthVaccinations), // TODO: Add correct scope + enabled: userInfo.scopes.includes(ApiScope.healthVaccinations), element: , }, { name: hm.generalVaccinations, path: HealthPaths.HealthVaccinationsGeneral, key: 'HealthVaccinations', - enabled: userInfo.scopes.includes(ApiScope.healthVaccinations), // TODO: Add correct scope + enabled: userInfo.scopes.includes(ApiScope.healthVaccinations), element: , }, { name: hm.otherVaccinations, path: HealthPaths.HealthVaccinationsOther, key: 'HealthVaccinations', - enabled: userInfo.scopes.includes(ApiScope.healthVaccinations), // TODO: Add correct scope + enabled: userInfo.scopes.includes(ApiScope.healthVaccinations), element: , }, ], diff --git a/libs/service-portal/health/src/screens/OrganDonation/OrganDonation.graphql b/libs/service-portal/health/src/screens/OrganDonation/OrganDonation.graphql new file mode 100644 index 000000000000..17dd93a23a84 --- /dev/null +++ b/libs/service-portal/health/src/screens/OrganDonation/OrganDonation.graphql @@ -0,0 +1,23 @@ +query getDonorStatus { + HealthDirectorateOrganDonationGetDonorStatus { + isDonor + exceptions + exceptionComment + registrationDate + } +} + +query getOrganDonationExceptions($locale: String) { + HealthDirectorateOrganDonationGetDonationExceptions(locale: $locale) { + values { + id + name + } + } +} + +mutation updateOrganDonationInfo( + $input: HealthDirectorateOrganDonorStatusInput! +) { + HealthDirectorateOrganDonationUpdateDonorStatus(input: $input) +} diff --git a/libs/service-portal/health/src/screens/OrganDonation/OrganDonation.tsx b/libs/service-portal/health/src/screens/OrganDonation/OrganDonation.tsx new file mode 100644 index 000000000000..58cf1d6ac945 --- /dev/null +++ b/libs/service-portal/health/src/screens/OrganDonation/OrganDonation.tsx @@ -0,0 +1,83 @@ +import { useLocale, useNamespaces } from '@island.is/localization' +import { + ActionCard, + IntroHeader, + LinkResolver, +} from '@island.is/service-portal/core' +import { messages as m } from '../../lib/messages' +import { Button, Box, Text } from '@island.is/island-ui/core' +import { HealthPaths } from '../../lib/paths' +import { Problem } from '@island.is/react-spa/shared' +import { useGetDonorStatusQuery } from './OrganDonation.generated' +const OrganDonation = () => { + useNamespaces('sp.health') + + const { formatMessage } = useLocale() + const { data, loading, error } = useGetDonorStatusQuery() + const donorStatus = data?.HealthDirectorateOrganDonationGetDonorStatus + + const exceptionText: string = + donorStatus?.exceptions?.length && donorStatus.exceptions.length > 0 + ? [ + donorStatus?.exceptionComment, + + donorStatus?.exceptions?.join(', '), + ].join(':') ?? '' + : donorStatus?.exceptionComment ?? '' + return ( + + + {!error && !loading && donorStatus !== null && ( + <> + + + + + + + + {formatMessage(m.takeOnOrganDonation)} + + + + + )} + {error && !loading && } + {!error && !loading && data === null && ( + + )} + + ) +} + +export default OrganDonation diff --git a/libs/service-portal/health/src/screens/OrganDonationRegistration/Limitations.tsx b/libs/service-portal/health/src/screens/OrganDonationRegistration/Limitations.tsx new file mode 100644 index 000000000000..72d9566b915d --- /dev/null +++ b/libs/service-portal/health/src/screens/OrganDonationRegistration/Limitations.tsx @@ -0,0 +1,90 @@ +import { + Box, + Checkbox, + Divider, + GridColumn, + GridContainer, + GridRow, + Input, + Stack, +} from '@island.is/island-ui/core' +import React, { useState } from 'react' +import { OptionsLimitations } from '../../utils/OrganDonationMock' +import { messages } from '../..' +import { useLocale, useNamespaces } from '@island.is/localization' +import { HealthDirectorateOrganDonationExceptionObject } from '@island.is/api/schema' + +interface LimitationsProps { + data: HealthDirectorateOrganDonationExceptionObject[] +} + +const Limitations = ({ data }: LimitationsProps) => { + useNamespaces('sp.health') + const { formatMessage } = useLocale() + const [checked, setChecked] = useState>([]) + + const handleCheckboxChange = (id: string, isChecked: boolean) => { + setChecked((prevState) => + isChecked ? [...prevState, id] : prevState.filter((item) => item !== id), + ) + } + + //const input = data.find((x) => x.type === 'input') + + return ( + + + + + {data?.map( + (y, yi) => ( + // y.type === 'checkbox' && ( + + + handleCheckboxChange( + y.name?.toLowerCase() ?? '', + e.target.checked, + ) + } + /> + + ), + // ), + )} + + + {/* This is commented out because of feature that was removed. May be included later on */} + {/* {input && checked.includes(input.name.toLowerCase()) && ( + + + + + + + + + + )} */} + + ) +} + +export default Limitations diff --git a/libs/service-portal/health/src/screens/OrganDonationRegistration/OrganDonationRegistration.css.ts b/libs/service-portal/health/src/screens/OrganDonationRegistration/OrganDonationRegistration.css.ts new file mode 100644 index 000000000000..e985fe3863ac --- /dev/null +++ b/libs/service-portal/health/src/screens/OrganDonationRegistration/OrganDonationRegistration.css.ts @@ -0,0 +1,6 @@ +import { style } from '@vanilla-extract/css' +import { theme } from '@island.is/island-ui/theme' + +export const buttonContainer = style({ + gap: theme.spacing[2], +}) diff --git a/libs/service-portal/health/src/screens/OrganDonationRegistration/RegistrationForm.tsx b/libs/service-portal/health/src/screens/OrganDonationRegistration/RegistrationForm.tsx new file mode 100644 index 000000000000..92eeedf3f847 --- /dev/null +++ b/libs/service-portal/health/src/screens/OrganDonationRegistration/RegistrationForm.tsx @@ -0,0 +1,179 @@ +import { + Box, + RadioButton, + Stack, + Text, + Button, + toast, +} from '@island.is/island-ui/core' +import { useLocale, useNamespaces } from '@island.is/localization' +import { + IntroHeader, + LinkResolver, + m as coreMessages, +} from '@island.is/service-portal/core' +import { messages } from '../..' +import { useEffect, useState } from 'react' +import React from 'react' +import { HealthPaths } from '../../lib/paths' +import * as styles from './OrganDonationRegistration.css' +import Limitations from './Limitations' +import { useNavigate } from 'react-router-dom' +import { + useGetDonorStatusQuery, + useGetOrganDonationExceptionsQuery, + useUpdateOrganDonationInfoMutation, +} from '../OrganDonation/OrganDonation.generated' + +export const Form2 = () => { + useNamespaces('sp.health') + const { formatMessage, lang } = useLocale() + const navigate = useNavigate() + + const OPT_IN = 'opt-in' + const OPT_IN_EXCEPTIONS = 'opt-in-exceptions' + const OPT_OUT = 'opt-out' + + const { data, loading } = useGetOrganDonationExceptionsQuery({ + variables: { locale: lang }, + }) + + const [updateDonorStatus] = useUpdateOrganDonationInfoMutation({ + onCompleted: () => { + toast.success(formatMessage(messages.registrationComplete)) + navigate(HealthPaths.HealthOrganDonation, { replace: true }) + }, + onError: () => { + toast.error(formatMessage(messages.registrationFailed)) + }, + }) + const { data: status } = useGetDonorStatusQuery() + + const exceptions = + data?.HealthDirectorateOrganDonationGetDonationExceptions.values + const [radioValue, setRadioValue] = useState() + + useEffect(() => { + if (radioValue === undefined) { + setRadioValue( + status?.HealthDirectorateOrganDonationGetDonorStatus.isDonor + ? OPT_IN + : (status?.HealthDirectorateOrganDonationGetDonorStatus + ?.exceptionComment?.length ?? 0) > 0 + ? OPT_IN_EXCEPTIONS + : OPT_OUT, + ) + } + }, [status]) + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = Object.fromEntries(formData.entries()) + + const idKey = 'organ-donation-limitation-' + const limitations = Object.keys(data) + .filter((key) => key.includes(idKey)) + .map((key) => key.replace(idKey, '').toLowerCase()) + await updateDonorStatus({ + variables: { + input: { + isDonor: radioValue === OPT_IN || radioValue === OPT_IN_EXCEPTIONS, + exceptions: radioValue === OPT_IN_EXCEPTIONS ? limitations : [], + }, + }, + }) + } + + return ( + + + + {formatMessage(messages.changeTake)} + + +
+ + + setRadioValue(OPT_IN)} + /> + + + setRadioValue(OPT_IN_EXCEPTIONS)} + /> + {exceptions && + exceptions.length > 0 && + radioValue === OPT_IN_EXCEPTIONS && ( + + )} + + + setRadioValue(OPT_OUT)} + /> + + + + + + + + +
+
+ ) +} + +export default Form2 diff --git a/libs/service-portal/health/src/screens/Vaccinations/Vaccinations.graphql b/libs/service-portal/health/src/screens/Vaccinations/Vaccinations.graphql index c5e8f4bc2cf5..9949bf0df212 100644 --- a/libs/service-portal/health/src/screens/Vaccinations/Vaccinations.graphql +++ b/libs/service-portal/health/src/screens/Vaccinations/Vaccinations.graphql @@ -1,5 +1,5 @@ query getVaccinations { - getVaccinations { + HealthDirectorateVaccinationsGetVaccinations { diseaseId diseaseName diseaseDescription diff --git a/libs/service-portal/health/src/screens/Vaccinations/VaccinationsGeneral.tsx b/libs/service-portal/health/src/screens/Vaccinations/VaccinationsGeneral.tsx index e7f449f6938d..e5c8f7e8c4b8 100644 --- a/libs/service-portal/health/src/screens/Vaccinations/VaccinationsGeneral.tsx +++ b/libs/service-portal/health/src/screens/Vaccinations/VaccinationsGeneral.tsx @@ -13,7 +13,7 @@ export const VaccinationsGeneral = () => { const { data, loading, error } = useGetVaccinationsQuery() - const vaccinations = data?.getVaccinations + const vaccinations = data?.HealthDirectorateVaccinationsGetVaccinations return ( diff --git a/libs/service-portal/health/src/screens/Vaccinations/VaccinationsOther.tsx b/libs/service-portal/health/src/screens/Vaccinations/VaccinationsOther.tsx index 8ecf7c793065..4ef397b65f64 100644 --- a/libs/service-portal/health/src/screens/Vaccinations/VaccinationsOther.tsx +++ b/libs/service-portal/health/src/screens/Vaccinations/VaccinationsOther.tsx @@ -11,7 +11,7 @@ import { Problem } from '@island.is/react-spa/shared' export const VaccinationsOther = () => { const { formatMessage } = useLocale() const { data, loading, error } = useGetVaccinationsQuery() - const vaccinations = data?.getVaccinations + const vaccinations = data?.HealthDirectorateVaccinationsGetVaccinations return ( diff --git a/libs/service-portal/health/src/utils/OrganDonationMock.ts b/libs/service-portal/health/src/utils/OrganDonationMock.ts new file mode 100644 index 000000000000..6f10608f2408 --- /dev/null +++ b/libs/service-portal/health/src/utils/OrganDonationMock.ts @@ -0,0 +1,119 @@ +import { Locale } from '@island.is/shared/types' + +interface Data { + title: string + description: string + limitations?: string[] +} + +interface OrganDonor { + data: Data +} + +export interface OptionsLimitations { + name: string + type: string +} +export interface OptionsOptions { + id: string + title: string + limitations?: Array +} + +interface OptionsData { + options: OptionsOptions[] +} + +interface Options { + data: OptionsData +} + +export const getOrganDonor = (locale: Locale) => { + const dataIS: OrganDonor = { + data: { + title: 'Ég er líffæragjafi', + description: 'Öll mín líffæri má nota til ígræðslu.', + limitations: undefined, + }, + } + const dataEN: OrganDonor = { + data: { + title: 'I am an organ donor', + description: 'All my organs can be used for transplantation', + limitations: undefined, + }, + } + return { + data: locale === 'en' ? dataEN : dataIS, + loading: false, + error: false, + } +} + +export const getOptions = (locale: Locale) => { + const dataIS: Options = { + data: { + options: [ + { + id: '1', + title: 'Við andlát mitt má nota líffæri mín til líffæragjafa.', + }, + { + id: '2', + title: + 'Ég gef leyfi fyrir líffæragjöf að undanskildum eftirfarandi líffærum:', + limitations: [ + { name: 'Hjarta', type: 'checkbox' }, + { name: 'Lungu', type: 'checkbox' }, + { name: 'Lifur', type: 'checkbox' }, + { name: 'Nýru', type: 'checkbox' }, + { name: 'Bris', type: 'checkbox' }, + { name: 'Þarmar', type: 'checkbox' }, + // { name: 'Annað', type: 'checkbox' }, + { name: 'Hornhimna', type: 'checkbox' }, + // { name: 'Annað', type: 'input' }, + ], + }, + { + id: '3', + title: 'Ég banna líffæragjöf.', + }, + ], + }, + } + const dataEN: Options = { + data: { + options: [ + { + id: '1', + title: 'Upon my death, my organs may be used for transplantation.', + }, + { + id: '2', + title: + 'I authorize organ donation, but the authorization does not cover the following organs:', + limitations: [ + { name: 'Heart', type: 'checkbox' }, + { name: 'Lungs', type: 'checkbox' }, + { name: 'Liver', type: 'checkbox' }, + { name: 'Kidneys', type: 'checkbox' }, + { name: 'Pancreas', type: 'checkbox' }, + { name: 'Intestines', type: 'checkbox' }, + // { name: 'Other', type: 'checkbox' }, + { name: 'Cornea', type: 'checkbox' }, + // { name: 'Other', type: 'input' }, + ], + }, + { + id: '3', + title: 'I do not authorize organ donation.', + }, + ], + }, + } + return { + data: locale === 'en' ? dataEN : locale === 'is' ? dataIS : dataIS, + loading: false, + error: false, + } +} diff --git a/libs/service-portal/health/src/utils/types.ts b/libs/service-portal/health/src/utils/types.ts index 37f152f74f6b..7cea1199d843 100644 --- a/libs/service-portal/health/src/utils/types.ts +++ b/libs/service-portal/health/src/utils/types.ts @@ -1,3 +1,8 @@ +export interface FormData { + selectedChoice?: string + selectedLimitations?: string[] // is array because it can have multiple values in the future + otherLimitations?: string +} export type DetailRow = { value: string type?: 'link' | 'text'