From 7854d2d57f6c49e2cd0a05ac4175f8c5cc30c8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Mon, 2 Dec 2024 09:48:20 +0200 Subject: [PATCH 01/26] =?UTF-8?q?Sivupohja=20IB-koulutuksen=20uudelle=20k?= =?UTF-8?q?=C3=A4lille?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interoperability/uiAdapters.ts | 4 ++- web/app/ib/IBEditor.tsx | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 web/app/ib/IBEditor.tsx diff --git a/web/app/components-v2/interoperability/uiAdapters.ts b/web/app/components-v2/interoperability/uiAdapters.ts index bd57bfca4e..ba7c2ae298 100644 --- a/web/app/components-v2/interoperability/uiAdapters.ts +++ b/web/app/components-v2/interoperability/uiAdapters.ts @@ -1,8 +1,10 @@ +import { IBEditor } from '../../ib/IBEditor' import { TaiteenPerusopetusEditor } from '../../taiteenperusopetus/TaiteenPerusopetusEditor' import { VSTEditor } from '../../vst/VSTEditor' import { AdaptedOpiskeluoikeusEditorCollection } from './useUiAdapter' export const opiskeluoikeusEditors: AdaptedOpiskeluoikeusEditorCollection = { taiteenperusopetus: TaiteenPerusopetusEditor, - vapaansivistystyonkoulutus: VSTEditor + vapaansivistystyonkoulutus: VSTEditor, + ibtutkinto: IBEditor } diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx new file mode 100644 index 0000000000..1016fec0ec --- /dev/null +++ b/web/app/ib/IBEditor.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import { useSchema } from '../appstate/constraints' +import { useForm } from '../components-v2/forms/FormModel' +import { AdaptedOpiskeluoikeusEditorProps } from '../components-v2/interoperability/useUiAdapter' +import { OpiskeluoikeusTitle } from '../components-v2/opiskeluoikeus/OpiskeluoikeusTitle' +import { IBOpiskeluoikeus } from '../types/fi/oph/koski/schema/IBOpiskeluoikeus' +import { t } from '../i18n/i18n' + +export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps + +export const IBEditor: React.FC = (props) => { + const opiskeluoikeusSchema = useSchema('IBOpiskeluoikeus') + const form = useForm(props.opiskeluoikeus, false, opiskeluoikeusSchema) + + return ( + <> + + + ) +} + +const ibKoulutusNimi = (opiskeluoikeus: IBOpiskeluoikeus): string => + `${t(opiskeluoikeus.suoritukset[0]?.koulutusmoduuli.tunniste.nimi)}` From 17c7f3179e6623550d60c9ab302d1fba5a4593c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Mon, 2 Dec 2024 10:12:17 +0200 Subject: [PATCH 02/26] =?UTF-8?q?Opiskeluoikeuden=20ja=20p=C3=A4=C3=A4taso?= =?UTF-8?q?n=20suorituksen=20perustietojen=20n=C3=A4ytt=C3=A4minen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/ib/IBEditor.tsx | 50 +++++++++++++++++++++++-- web/app/ib/IBPaatasonSuoritusTiedot.tsx | 38 +++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 web/app/ib/IBPaatasonSuoritusTiedot.tsx diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index 1016fec0ec..b9949b64f1 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -1,10 +1,21 @@ import React from 'react' import { useSchema } from '../appstate/constraints' -import { useForm } from '../components-v2/forms/FormModel' +import { FormModel, useForm } from '../components-v2/forms/FormModel' import { AdaptedOpiskeluoikeusEditorProps } from '../components-v2/interoperability/useUiAdapter' import { OpiskeluoikeusTitle } from '../components-v2/opiskeluoikeus/OpiskeluoikeusTitle' import { IBOpiskeluoikeus } from '../types/fi/oph/koski/schema/IBOpiskeluoikeus' import { t } from '../i18n/i18n' +import { + EditorContainer, + usePäätasonSuoritus +} from '../components-v2/containers/EditorContainer' +import { LukionOpiskeluoikeusjakso } from '../types/fi/oph/koski/schema/LukionOpiskeluoikeusjakso' +import { + ibKoulutusNimi, + IBPäätasonSuoritusTiedot +} from './IBPaatasonSuoritusTiedot' +import { Spacer } from '../components-v2/layout/Spacer' +import { SuorituksenVahvistusField } from '../components-v2/opiskeluoikeus/SuorituksenVahvistus' export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps @@ -18,9 +29,42 @@ export const IBEditor: React.FC = (props) => { opiskeluoikeus={form.state} opiskeluoikeudenNimi={ibKoulutusNimi(form.state)} /> + ) } -const ibKoulutusNimi = (opiskeluoikeus: IBOpiskeluoikeus): string => - `${t(opiskeluoikeus.suoritukset[0]?.koulutusmoduuli.tunniste.nimi)}` +const IBPäätasonSuoritusEditor: React.FC< + IBEditorProps & { + form: FormModel + } +> = ({ form, oppijaOid, invalidatable, opiskeluoikeus }) => { + const [päätasonSuoritus, setPäätasonSuoritus] = usePäätasonSuoritus(form) + const organisaatio = + opiskeluoikeus.oppilaitos || opiskeluoikeus.koulutustoimija + + return ( + console.log('todo: onChangeSuoritus')} + createOpiskeluoikeusjakso={LukionOpiskeluoikeusjakso} + > + + + + + + + + ) +} diff --git a/web/app/ib/IBPaatasonSuoritusTiedot.tsx b/web/app/ib/IBPaatasonSuoritusTiedot.tsx new file mode 100644 index 0000000000..2f1836ccc6 --- /dev/null +++ b/web/app/ib/IBPaatasonSuoritusTiedot.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { ActivePäätasonSuoritus } from '../components-v2/containers/EditorContainer' +import { + KeyValueRow, + KeyValueTable +} from '../components-v2/containers/KeyValueTable' +import { FormModel } from '../components-v2/forms/FormModel' +import { t } from '../i18n/i18n' +import { IBOpiskeluoikeus } from '../types/fi/oph/koski/schema/IBOpiskeluoikeus' + +export type IBTutkintTiedotProps = { + form: FormModel + päätasonSuoritus: ActivePäätasonSuoritus +} + +export const IBPäätasonSuoritusTiedot: React.FC = ({ + form, + päätasonSuoritus +}) => { + const opiskeluoikeus = form.state + + return ( + + + {t(ibKoulutusNimi(opiskeluoikeus))} + + + {t(päätasonSuoritus.suoritus.toimipiste.nimi)} + + + {t(päätasonSuoritus.suoritus.suorituskieli.nimi)} + + + ) +} + +export const ibKoulutusNimi = (opiskeluoikeus: IBOpiskeluoikeus): string => + `${t(opiskeluoikeus.suoritukset[0]?.koulutusmoduuli.tunniste.nimi)}` From 94276b8e0399b1641a47d46bd9b393245e4ab036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Mon, 2 Dec 2024 14:53:05 +0200 Subject: [PATCH 03/26] Oppiaine- ja kurssitaulukko --- .../oph/koski/typemodel/TsFileUpdater.scala | 3 + web/app/components-v2/main.less | 1 + .../opiskeluoikeus/OppiaineTable.less | 60 +++ .../opiskeluoikeus/OppiaineTable.tsx | 122 ++++++ web/app/ib/IBEditor.tsx | 4 + .../types/fi/oph/koski/schema/Arvioinniton.ts | 369 ++++++++++++++++++ .../koski/schema/MahdollisestiArvioinniton.ts | 35 ++ .../oph/koski/schema/ValinnanMahdollisuus.ts | 37 ++ web/app/util/suoritus.ts | 27 ++ 9 files changed, 658 insertions(+) create mode 100644 web/app/components-v2/opiskeluoikeus/OppiaineTable.less create mode 100644 web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx create mode 100644 web/app/types/fi/oph/koski/schema/Arvioinniton.ts create mode 100644 web/app/types/fi/oph/koski/schema/MahdollisestiArvioinniton.ts create mode 100644 web/app/types/fi/oph/koski/schema/ValinnanMahdollisuus.ts create mode 100644 web/app/util/suoritus.ts diff --git a/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala b/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala index 05f635de5d..84cd46355e 100644 --- a/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala +++ b/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala @@ -82,4 +82,7 @@ case class AdditionalExports( vapaanSivistystyönKoulutuksenPäätasonSuoritus: VapaanSivistystyönKoulutuksenPäätasonSuoritus, suorituskielellinen: Suorituskielellinen, maksuttomuustieto: MaksuttomuusTieto, + valinnanMahdollisuus: ValinnanMahdollisuus, + arvioinniton: Arvioinniton, + mahdollisestiArvioinniton: MahdollisestiArvioinniton, ) diff --git a/web/app/components-v2/main.less b/web/app/components-v2/main.less index 39eca884aa..259143e296 100644 --- a/web/app/components-v2/main.less +++ b/web/app/components-v2/main.less @@ -62,6 +62,7 @@ @import 'opiskeluoikeus/SuorituksenVahvistus'; @import 'opiskeluoikeus/TunnustusField'; @import 'opiskeluoikeus/OpiskeluoikeudenToimipiste'; +@import 'opiskeluoikeus/OppiaineTable'; @import 'texts/Icon'; @import 'texts/Spinner'; diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.less b/web/app/components-v2/opiskeluoikeus/OppiaineTable.less new file mode 100644 index 0000000000..724b599b13 --- /dev/null +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.less @@ -0,0 +1,60 @@ +.OppiaineTable { + width: 100%; + + &__oppiaine { + width: 90%; + } + + td, + th { + padding: @baseline; + } + + thead { + th { + vertical-align: bottom; + } + .OppiaineTable__laajuus, + .OppiaineTable__arvosana { + text-align: right; + } + } + + tbody { + td { + background-color: @color-blue-background; + } + + .OppiaineRow { + &__icon { + font-family: FontAwesome; + } + + &__nimi { + opacity: 66%; + line-height: 2 * @baseline; + margin-bottom: @baseline; + } + + &__kurssit { + line-height: 2 * @baseline; + + .Kurssi { + display: inline-block; + width: 4 * @baseline; + margin-right: @baseline; + + &__tunniste { + color: @color-blue; + font-weight: 600; + } + } + } + + &__laajuus, + &__arvosana { + text-align: right; + } + } + } +} diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx new file mode 100644 index 0000000000..6c83f4aa24 --- /dev/null +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx @@ -0,0 +1,122 @@ +import React from 'react' +import { t } from '../../i18n/i18n' +import { Arviointi } from '../../types/fi/oph/koski/schema/Arviointi' +import { IBPäätasonSuoritus } from '../../types/fi/oph/koski/schema/IBPaatasonSuoritus' +import { isMuidenLukioOpintojenPreIBSuoritus2019 } from '../../types/fi/oph/koski/schema/MuidenLukioOpintojenPreIBSuoritus2019' +import { Suoritus } from '../../types/fi/oph/koski/schema/Suoritus' +import { isValinnanMahdollisuus } from '../../types/fi/oph/koski/schema/ValinnanMahdollisuus' +import { parasArviointi } from '../../util/arvioinnit' +import { sum } from '../../util/numbers' +import { KoulutusmoduuliOf, OsasuoritusOf } from '../../util/schema' +import { suoritusValmis } from '../../util/suoritus' +import { notUndefined } from '../../util/util' + +// Vain OppiaineTablen tukemat päätason suoritukset (tätä komponenttia tullaan myöhemmin käyttämään ainakin lukion näkymille) +export type OppiainePäätasonSuoritus = IBPäätasonSuoritus + +export type OppiaineOsasuoritus = OsasuoritusOf + +export type OppiaineTableProps = { + suoritus: OppiainePäätasonSuoritus +} + +export const OppiaineTable: React.FC = ({ suoritus }) => { + const oppiaineet = suoritus.osasuoritukset || [] + + return oppiaineet.length === 0 ? null : ( + + + + + + + + + + + {oppiaineet.map((oppiaine, i) => ( + + ))} + +
{t('Oppiaine')}{t('Laajuus (kurssia)')}{t('Arvosana')}
+ ) +} + +type OppiaineRowProps = { + oppiaine: OppiaineOsasuoritus +} + +const OppiaineRow: React.FC = ({ oppiaine }) => { + const kurssit = oppiaine.osasuoritukset || [] + + return ( + + + + + +
+ {oppiaineenNimi(oppiaine.koulutusmoduuli)} +
+
+ {kurssit.map((kurssi, index) => ( + + ))} +
+ + {kurssit.length} + {oppiaineenArvosana(oppiaine)} + + ) +} + +const oppiaineenNimi = ( + koulutusmoduuli: KoulutusmoduuliOf +) => + [ + koulutusmoduuli.tunniste.nimi, + (koulutusmoduuli as any)?.kieli?.nimi, + (koulutusmoduuli as any)?.oppimäärä?.nimi + ] + .filter(notUndefined) + .map((s) => t(s)) + .join(', ') + +const oppiaineenArvosana = (oppiaine: OppiaineOsasuoritus) => + isMuidenLukioOpintojenPreIBSuoritus2019(oppiaine) || !oppiaine.arviointi + ? null + : parasArviointi(oppiaine.arviointi as Arviointi[])?.arvosana.koodiarvo + +type KurssiProps = { + kurssi: OsasuoritusOf +} + +const Kurssi: React.FC = ({ kurssi }) => { + return ( +
+
+ {kurssi.koulutusmoduuli.tunniste.koodiarvo} +
+
+ {kurssi.arviointi + ? parasArviointi(kurssi.arviointi as Arviointi[])?.arvosana.koodiarvo + : null} +
+
+ ) +} + +type SuorituksenTilaIconProps = { + suoritus: Suoritus +} + +const SuorituksenTilaIcon: React.FC = ({ + suoritus +}) => + isValinnanMahdollisuus(suoritus) ? null : suoritusValmis(suoritus) ? ( + // eslint-disable-next-line react/jsx-no-literals +
+ ) : ( + // eslint-disable-next-line react/jsx-no-literals +
+ ) diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index b9949b64f1..85c510c202 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -16,6 +16,7 @@ import { } from './IBPaatasonSuoritusTiedot' import { Spacer } from '../components-v2/layout/Spacer' import { SuorituksenVahvistusField } from '../components-v2/opiskeluoikeus/SuorituksenVahvistus' +import { OppiaineTable } from '../components-v2/opiskeluoikeus/OppiaineTable' export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps @@ -64,7 +65,10 @@ const IBPäätasonSuoritusEditor: React.FC< organisaatio={organisaatio} disableAdd={true} // TODO /> + + + ) } diff --git a/web/app/types/fi/oph/koski/schema/Arvioinniton.ts b/web/app/types/fi/oph/koski/schema/Arvioinniton.ts new file mode 100644 index 0000000000..e65a8a7d99 --- /dev/null +++ b/web/app/types/fi/oph/koski/schema/Arvioinniton.ts @@ -0,0 +1,369 @@ +import { + AikuistenPerusopetuksenAlkuvaiheenSuoritus, + isAikuistenPerusopetuksenAlkuvaiheenSuoritus +} from './AikuistenPerusopetuksenAlkuvaiheenSuoritus' +import { + AikuistenPerusopetuksenOppimääränSuoritus, + isAikuistenPerusopetuksenOppimääränSuoritus +} from './AikuistenPerusopetuksenOppimaaranSuoritus' +import { + AmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus, + isAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus +} from './AmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus' +import { + AmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus, + isAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus +} from './AmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus' +import { + AmmatillisenTutkinnonOsittainenSuoritus, + isAmmatillisenTutkinnonOsittainenSuoritus +} from './AmmatillisenTutkinnonOsittainenSuoritus' +import { + AmmatillisenTutkinnonSuoritus, + isAmmatillisenTutkinnonSuoritus +} from './AmmatillisenTutkinnonSuoritus' +import { + DIAOppiaineenTutkintovaiheenSuoritus, + isDIAOppiaineenTutkintovaiheenSuoritus +} from './DIAOppiaineenTutkintovaiheenSuoritus' +import { + DIAOppiaineenValmistavanVaiheenSuoritus, + isDIAOppiaineenValmistavanVaiheenSuoritus +} from './DIAOppiaineenValmistavanVaiheenSuoritus' +import { + DIATutkinnonSuoritus, + isDIATutkinnonSuoritus +} from './DIATutkinnonSuoritus' +import { + DIAValmistavanVaiheenSuoritus, + isDIAValmistavanVaiheenSuoritus +} from './DIAValmistavanVaiheenSuoritus' +import { + DiplomaVuosiluokanSuoritus, + isDiplomaVuosiluokanSuoritus +} from './DiplomaVuosiluokanSuoritus' +import { + EBTutkinnonOsasuoritus, + isEBTutkinnonOsasuoritus +} from './EBTutkinnonOsasuoritus' +import { + EBTutkinnonSuoritus, + isEBTutkinnonSuoritus +} from './EBTutkinnonSuoritus' +import { + EsiopetuksenSuoritus, + isEsiopetuksenSuoritus +} from './EsiopetuksenSuoritus' +import { + IBTutkinnonSuoritus, + isIBTutkinnonSuoritus +} from './IBTutkinnonSuoritus' +import { + LukionOppiaineidenOppimäärienSuoritus2019, + isLukionOppiaineidenOppimäärienSuoritus2019 +} from './LukionOppiaineidenOppimaarienSuoritus2019' +import { + LukionOppimääränSuoritus2015, + isLukionOppimääränSuoritus2015 +} from './LukionOppimaaranSuoritus2015' +import { + LukionOppimääränSuoritus2019, + isLukionOppimääränSuoritus2019 +} from './LukionOppimaaranSuoritus2019' +import { + LukioonValmistavanKoulutuksenSuoritus, + isLukioonValmistavanKoulutuksenSuoritus +} from './LukioonValmistavanKoulutuksenSuoritus' +import { + MYPVuosiluokanSuoritus, + isMYPVuosiluokanSuoritus +} from './MYPVuosiluokanSuoritus' +import { + MuidenLukioOpintojenPreIBSuoritus2019, + isMuidenLukioOpintojenPreIBSuoritus2019 +} from './MuidenLukioOpintojenPreIBSuoritus2019' +import { + MuidenLukioOpintojenSuoritus2019, + isMuidenLukioOpintojenSuoritus2019 +} from './MuidenLukioOpintojenSuoritus2019' +import { + MuuKorkeakoulunSuoritus, + isMuuKorkeakoulunSuoritus +} from './MuuKorkeakoulunSuoritus' +import { + MuunAmmatillisenKoulutuksenSuoritus, + isMuunAmmatillisenKoulutuksenSuoritus +} from './MuunAmmatillisenKoulutuksenSuoritus' +import { + NuortenPerusopetuksenOppimääränSuoritus, + isNuortenPerusopetuksenOppimääränSuoritus +} from './NuortenPerusopetuksenOppimaaranSuoritus' +import { + NurseryVuosiluokanSuoritus, + isNurseryVuosiluokanSuoritus +} from './NurseryVuosiluokanSuoritus' +import { + NäyttötutkintoonValmistavanKoulutuksenOsanSuoritus, + isNäyttötutkintoonValmistavanKoulutuksenOsanSuoritus +} from './NayttotutkintoonValmistavanKoulutuksenOsanSuoritus' +import { + NäyttötutkintoonValmistavanKoulutuksenSuoritus, + isNäyttötutkintoonValmistavanKoulutuksenSuoritus +} from './NayttotutkintoonValmistavanKoulutuksenSuoritus' +import { + OppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus, + isOppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus +} from './OppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus' +import { + OppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus2022, + isOppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus2022 +} from './OppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus2022' +import { + OppivelvollisilleSuunnattuVapaanSivistystyönKoulutuksenSuoritus, + isOppivelvollisilleSuunnattuVapaanSivistystyönKoulutuksenSuoritus +} from './OppivelvollisilleSuunnattuVapaanSivistystyonKoulutuksenSuoritus' +import { + OppivelvollisilleSuunnatunVapaanSivistystyönOsaamiskokonaisuudenSuoritus, + isOppivelvollisilleSuunnatunVapaanSivistystyönOsaamiskokonaisuudenSuoritus +} from './OppivelvollisilleSuunnatunVapaanSivistystyonOsaamiskokonaisuudenSuoritus' +import { + OppivelvollisilleSuunnatunVapaanSivistystyönValinnaistenSuuntautumisopintojenSuoritus, + isOppivelvollisilleSuunnatunVapaanSivistystyönValinnaistenSuuntautumisopintojenSuoritus +} from './OppivelvollisilleSuunnatunVapaanSivistystyonValinnaistenSuuntautumisopintojenSuoritus' +import { + OsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus, + isOsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus +} from './OsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus' +import { + OsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus, + isOsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus +} from './OsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus' +import { + PYPVuosiluokanSuoritus, + isPYPVuosiluokanSuoritus +} from './PYPVuosiluokanSuoritus' +import { + PerusopetukseenValmistavanOpetuksenSuoritus, + isPerusopetukseenValmistavanOpetuksenSuoritus +} from './PerusopetukseenValmistavanOpetuksenSuoritus' +import { + PerusopetuksenLisäopetuksenSuoritus, + isPerusopetuksenLisäopetuksenSuoritus +} from './PerusopetuksenLisaopetuksenSuoritus' +import { + PerusopetuksenVuosiluokanSuoritus, + isPerusopetuksenVuosiluokanSuoritus +} from './PerusopetuksenVuosiluokanSuoritus' +import { PreIBSuoritus2015, isPreIBSuoritus2015 } from './PreIBSuoritus2015' +import { PreIBSuoritus2019, isPreIBSuoritus2019 } from './PreIBSuoritus2019' +import { + PrimaryVuosiluokanSuoritus, + isPrimaryVuosiluokanSuoritus +} from './PrimaryVuosiluokanSuoritus' +import { + SecondaryLowerVuosiluokanSuoritus, + isSecondaryLowerVuosiluokanSuoritus +} from './SecondaryLowerVuosiluokanSuoritus' +import { + SecondaryUpperOppiaineenSuoritusS7, + isSecondaryUpperOppiaineenSuoritusS7 +} from './SecondaryUpperOppiaineenSuoritusS7' +import { + SecondaryUpperVuosiluokanSuoritus, + isSecondaryUpperVuosiluokanSuoritus +} from './SecondaryUpperVuosiluokanSuoritus' +import { + TaiteenPerusopetuksenLaajanOppimääränPerusopintojenSuoritus, + isTaiteenPerusopetuksenLaajanOppimääränPerusopintojenSuoritus +} from './TaiteenPerusopetuksenLaajanOppimaaranPerusopintojenSuoritus' +import { + TaiteenPerusopetuksenLaajanOppimääränSyventävienOpintojenSuoritus, + isTaiteenPerusopetuksenLaajanOppimääränSyventävienOpintojenSuoritus +} from './TaiteenPerusopetuksenLaajanOppimaaranSyventavienOpintojenSuoritus' +import { + TaiteenPerusopetuksenYleisenOppimääränTeemaopintojenSuoritus, + isTaiteenPerusopetuksenYleisenOppimääränTeemaopintojenSuoritus +} from './TaiteenPerusopetuksenYleisenOppimaaranTeemaopintojenSuoritus' +import { + TaiteenPerusopetuksenYleisenOppimääränYhteistenOpintojenSuoritus, + isTaiteenPerusopetuksenYleisenOppimääränYhteistenOpintojenSuoritus +} from './TaiteenPerusopetuksenYleisenOppimaaranYhteistenOpintojenSuoritus' +import { + TelmaKoulutuksenSuoritus, + isTelmaKoulutuksenSuoritus +} from './TelmaKoulutuksenSuoritus' +import { + TutkinnonOsaaPienemmistäKokonaisuuksistaKoostuvaSuoritus, + isTutkinnonOsaaPienemmistäKokonaisuuksistaKoostuvaSuoritus +} from './TutkinnonOsaaPienemmistaKokonaisuuksistaKoostuvaSuoritus' +import { + TutkintokoulutukseenValmentavanKoulutuksenSuoritus, + isTutkintokoulutukseenValmentavanKoulutuksenSuoritus +} from './TutkintokoulutukseenValmentavanKoulutuksenSuoritus' +import { + VSTKotoutumiskoulutuksenYhteiskuntaJaTyöelämäosaaminenAlaosasuoritus, + isVSTKotoutumiskoulutuksenYhteiskuntaJaTyöelämäosaaminenAlaosasuoritus +} from './VSTKotoutumiskoulutuksenYhteiskuntaJaTyoelamaosaaminenAlaosasuoritus' +import { + VSTKotoutumiskoulutusValinnaistenOpintojenAlaosasuoritus, + isVSTKotoutumiskoulutusValinnaistenOpintojenAlaosasuoritus +} from './VSTKotoutumiskoulutusValinnaistenOpintojenAlaosasuoritus' +import { + ValmaKoulutuksenSuoritus, + isValmaKoulutuksenSuoritus +} from './ValmaKoulutuksenSuoritus' +import { + VapaanSivistystyönJotpaKoulutuksenSuoritus, + isVapaanSivistystyönJotpaKoulutuksenSuoritus +} from './VapaanSivistystyonJotpaKoulutuksenSuoritus' +import { + VapaanSivistystyönLukutaitokoulutuksenSuoritus, + isVapaanSivistystyönLukutaitokoulutuksenSuoritus +} from './VapaanSivistystyonLukutaitokoulutuksenSuoritus' +import { + VapaanSivistystyönVapaatavoitteisenKoulutuksenSuoritus, + isVapaanSivistystyönVapaatavoitteisenKoulutuksenSuoritus +} from './VapaanSivistystyonVapaatavoitteisenKoulutuksenSuoritus' +import { + YlioppilastutkinnonSuoritus, + isYlioppilastutkinnonSuoritus +} from './YlioppilastutkinnonSuoritus' + +/** + * Arvioinniton + * + * @see `fi.oph.koski.schema.Arvioinniton` + */ +export type Arvioinniton = + | AikuistenPerusopetuksenAlkuvaiheenSuoritus + | AikuistenPerusopetuksenOppimääränSuoritus + | AmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus + | AmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus + | AmmatillisenTutkinnonOsittainenSuoritus + | AmmatillisenTutkinnonSuoritus + | DIAOppiaineenTutkintovaiheenSuoritus + | DIAOppiaineenValmistavanVaiheenSuoritus + | DIATutkinnonSuoritus + | DIAValmistavanVaiheenSuoritus + | DiplomaVuosiluokanSuoritus + | EBTutkinnonOsasuoritus + | EBTutkinnonSuoritus + | EsiopetuksenSuoritus + | IBTutkinnonSuoritus + | LukionOppiaineidenOppimäärienSuoritus2019 + | LukionOppimääränSuoritus2015 + | LukionOppimääränSuoritus2019 + | LukioonValmistavanKoulutuksenSuoritus + | MYPVuosiluokanSuoritus + | MuidenLukioOpintojenPreIBSuoritus2019 + | MuidenLukioOpintojenSuoritus2019 + | MuuKorkeakoulunSuoritus + | MuunAmmatillisenKoulutuksenSuoritus + | NuortenPerusopetuksenOppimääränSuoritus + | NurseryVuosiluokanSuoritus + | NäyttötutkintoonValmistavanKoulutuksenOsanSuoritus + | NäyttötutkintoonValmistavanKoulutuksenSuoritus + | OppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus + | OppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus2022 + | OppivelvollisilleSuunnattuVapaanSivistystyönKoulutuksenSuoritus + | OppivelvollisilleSuunnatunVapaanSivistystyönOsaamiskokonaisuudenSuoritus + | OppivelvollisilleSuunnatunVapaanSivistystyönValinnaistenSuuntautumisopintojenSuoritus + | OsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus + | OsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus + | PYPVuosiluokanSuoritus + | PerusopetukseenValmistavanOpetuksenSuoritus + | PerusopetuksenLisäopetuksenSuoritus + | PerusopetuksenVuosiluokanSuoritus + | PreIBSuoritus2015 + | PreIBSuoritus2019 + | PrimaryVuosiluokanSuoritus + | SecondaryLowerVuosiluokanSuoritus + | SecondaryUpperOppiaineenSuoritusS7 + | SecondaryUpperVuosiluokanSuoritus + | TaiteenPerusopetuksenLaajanOppimääränPerusopintojenSuoritus + | TaiteenPerusopetuksenLaajanOppimääränSyventävienOpintojenSuoritus + | TaiteenPerusopetuksenYleisenOppimääränTeemaopintojenSuoritus + | TaiteenPerusopetuksenYleisenOppimääränYhteistenOpintojenSuoritus + | TelmaKoulutuksenSuoritus + | TutkinnonOsaaPienemmistäKokonaisuuksistaKoostuvaSuoritus + | TutkintokoulutukseenValmentavanKoulutuksenSuoritus + | VSTKotoutumiskoulutuksenYhteiskuntaJaTyöelämäosaaminenAlaosasuoritus + | VSTKotoutumiskoulutusValinnaistenOpintojenAlaosasuoritus + | ValmaKoulutuksenSuoritus + | VapaanSivistystyönJotpaKoulutuksenSuoritus + | VapaanSivistystyönLukutaitokoulutuksenSuoritus + | VapaanSivistystyönVapaatavoitteisenKoulutuksenSuoritus + | YlioppilastutkinnonSuoritus + +export const isArvioinniton = (a: any): a is Arvioinniton => + isAikuistenPerusopetuksenAlkuvaiheenSuoritus(a) || + isAikuistenPerusopetuksenOppimääränSuoritus(a) || + isAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus( + a + ) || + isAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus(a) || + isAmmatillisenTutkinnonOsittainenSuoritus(a) || + isAmmatillisenTutkinnonSuoritus(a) || + isDIAOppiaineenTutkintovaiheenSuoritus(a) || + isDIAOppiaineenValmistavanVaiheenSuoritus(a) || + isDIATutkinnonSuoritus(a) || + isDIAValmistavanVaiheenSuoritus(a) || + isDiplomaVuosiluokanSuoritus(a) || + isEBTutkinnonOsasuoritus(a) || + isEBTutkinnonSuoritus(a) || + isEsiopetuksenSuoritus(a) || + isIBTutkinnonSuoritus(a) || + isLukionOppiaineidenOppimäärienSuoritus2019(a) || + isLukionOppimääränSuoritus2015(a) || + isLukionOppimääränSuoritus2019(a) || + isLukioonValmistavanKoulutuksenSuoritus(a) || + isMYPVuosiluokanSuoritus(a) || + isMuidenLukioOpintojenPreIBSuoritus2019(a) || + isMuidenLukioOpintojenSuoritus2019(a) || + isMuuKorkeakoulunSuoritus(a) || + isMuunAmmatillisenKoulutuksenSuoritus(a) || + isNuortenPerusopetuksenOppimääränSuoritus(a) || + isNurseryVuosiluokanSuoritus(a) || + isNäyttötutkintoonValmistavanKoulutuksenOsanSuoritus(a) || + isNäyttötutkintoonValmistavanKoulutuksenSuoritus(a) || + isOppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus( + a + ) || + isOppivelvollisilleSuunnattuMaahanmuuttajienKotoutumiskoulutuksenSuoritus2022( + a + ) || + isOppivelvollisilleSuunnattuVapaanSivistystyönKoulutuksenSuoritus(a) || + isOppivelvollisilleSuunnatunVapaanSivistystyönOsaamiskokonaisuudenSuoritus( + a + ) || + isOppivelvollisilleSuunnatunVapaanSivistystyönValinnaistenSuuntautumisopintojenSuoritus( + a + ) || + isOsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus( + a + ) || + isOsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus(a) || + isPYPVuosiluokanSuoritus(a) || + isPerusopetukseenValmistavanOpetuksenSuoritus(a) || + isPerusopetuksenLisäopetuksenSuoritus(a) || + isPerusopetuksenVuosiluokanSuoritus(a) || + isPreIBSuoritus2015(a) || + isPreIBSuoritus2019(a) || + isPrimaryVuosiluokanSuoritus(a) || + isSecondaryLowerVuosiluokanSuoritus(a) || + isSecondaryUpperOppiaineenSuoritusS7(a) || + isSecondaryUpperVuosiluokanSuoritus(a) || + isTaiteenPerusopetuksenLaajanOppimääränPerusopintojenSuoritus(a) || + isTaiteenPerusopetuksenLaajanOppimääränSyventävienOpintojenSuoritus(a) || + isTaiteenPerusopetuksenYleisenOppimääränTeemaopintojenSuoritus(a) || + isTaiteenPerusopetuksenYleisenOppimääränYhteistenOpintojenSuoritus(a) || + isTelmaKoulutuksenSuoritus(a) || + isTutkinnonOsaaPienemmistäKokonaisuuksistaKoostuvaSuoritus(a) || + isTutkintokoulutukseenValmentavanKoulutuksenSuoritus(a) || + isVSTKotoutumiskoulutuksenYhteiskuntaJaTyöelämäosaaminenAlaosasuoritus(a) || + isVSTKotoutumiskoulutusValinnaistenOpintojenAlaosasuoritus(a) || + isValmaKoulutuksenSuoritus(a) || + isVapaanSivistystyönJotpaKoulutuksenSuoritus(a) || + isVapaanSivistystyönLukutaitokoulutuksenSuoritus(a) || + isVapaanSivistystyönVapaatavoitteisenKoulutuksenSuoritus(a) || + isYlioppilastutkinnonSuoritus(a) diff --git a/web/app/types/fi/oph/koski/schema/MahdollisestiArvioinniton.ts b/web/app/types/fi/oph/koski/schema/MahdollisestiArvioinniton.ts new file mode 100644 index 0000000000..8404ef9921 --- /dev/null +++ b/web/app/types/fi/oph/koski/schema/MahdollisestiArvioinniton.ts @@ -0,0 +1,35 @@ +import { + AikuistenPerusopetuksenAlkuvaiheenOppiaineenSuoritus, + isAikuistenPerusopetuksenAlkuvaiheenOppiaineenSuoritus +} from './AikuistenPerusopetuksenAlkuvaiheenOppiaineenSuoritus' +import { + MuunKuinSäännellynKoulutuksenOsasuoritus, + isMuunKuinSäännellynKoulutuksenOsasuoritus +} from './MuunKuinSaannellynKoulutuksenOsasuoritus' +import { + MuunKuinSäännellynKoulutuksenPäätasonSuoritus, + isMuunKuinSäännellynKoulutuksenPäätasonSuoritus +} from './MuunKuinSaannellynKoulutuksenPaatasonSuoritus' +import { + VSTKotoutumiskoulutuksenOhjauksenSuoritus2022, + isVSTKotoutumiskoulutuksenOhjauksenSuoritus2022 +} from './VSTKotoutumiskoulutuksenOhjauksenSuoritus2022' + +/** + * MahdollisestiArvioinniton + * + * @see `fi.oph.koski.schema.MahdollisestiArvioinniton` + */ +export type MahdollisestiArvioinniton = + | AikuistenPerusopetuksenAlkuvaiheenOppiaineenSuoritus + | MuunKuinSäännellynKoulutuksenOsasuoritus + | MuunKuinSäännellynKoulutuksenPäätasonSuoritus + | VSTKotoutumiskoulutuksenOhjauksenSuoritus2022 + +export const isMahdollisestiArvioinniton = ( + a: any +): a is MahdollisestiArvioinniton => + isAikuistenPerusopetuksenAlkuvaiheenOppiaineenSuoritus(a) || + isMuunKuinSäännellynKoulutuksenOsasuoritus(a) || + isMuunKuinSäännellynKoulutuksenPäätasonSuoritus(a) || + isVSTKotoutumiskoulutuksenOhjauksenSuoritus2022(a) diff --git a/web/app/types/fi/oph/koski/schema/ValinnanMahdollisuus.ts b/web/app/types/fi/oph/koski/schema/ValinnanMahdollisuus.ts new file mode 100644 index 0000000000..ec864538fe --- /dev/null +++ b/web/app/types/fi/oph/koski/schema/ValinnanMahdollisuus.ts @@ -0,0 +1,37 @@ +import { + AmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus, + isAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus +} from './AmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus' +import { + AmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus, + isAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus +} from './AmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus' +import { + OsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus, + isOsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus +} from './OsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus' +import { + OsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus, + isOsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus +} from './OsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus' + +/** + * ValinnanMahdollisuus + * + * @see `fi.oph.koski.schema.ValinnanMahdollisuus` + */ +export type ValinnanMahdollisuus = + | AmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus + | AmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus + | OsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus + | OsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus + +export const isValinnanMahdollisuus = (a: any): a is ValinnanMahdollisuus => + isAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus( + a + ) || + isAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus(a) || + isOsittaisenAmmatillisenTutkinnonOsanJatkoOpintovalmiuksiaTukevienOpintojenSuoritus( + a + ) || + isOsittaisenAmmatillisenTutkinnonOsanKorkeakouluopintoSuoritus(a) diff --git a/web/app/util/suoritus.ts b/web/app/util/suoritus.ts new file mode 100644 index 0000000000..2e1924a56a --- /dev/null +++ b/web/app/util/suoritus.ts @@ -0,0 +1,27 @@ +import { parseISODate } from '../date/date' +import { isArvioinniton } from '../types/fi/oph/koski/schema/Arvioinniton' +import { Arviointi } from '../types/fi/oph/koski/schema/Arviointi' +import { isMahdollisestiArvioinniton } from '../types/fi/oph/koski/schema/MahdollisestiArvioinniton' +import { isPäätasonSuoritus } from '../types/fi/oph/koski/schema/PaatasonSuoritus' +import { Suoritus } from '../types/fi/oph/koski/schema/Suoritus' +import { parasArviointi } from './arvioinnit' + +export const suoritusValmis = (suoritus: Suoritus) => { + if (isPäätasonSuoritus(suoritus)) { + const vahvistuspäivä = suoritus.vahvistus?.päivä + return vahvistuspäivä && isInPast(vahvistuspäivä) + } else if ( + isArvioinniton(suoritus) || + isMahdollisestiArvioinniton(suoritus) + ) { + return true + } else { + const arviointi = + suoritus.arviointi && parasArviointi(suoritus.arviointi as Arviointi[]) + const arviointiPäivä = (arviointi as any)?.päivä + return arviointi && arviointiPäivä ? isInPast(arviointiPäivä) : !!arviointi + } +} + +const isInPast = (dateStr?: string) => + dateStr !== undefined && parseISODate(dateStr) < new Date() From 03c9d4c39b26dc4a5b2d22bbb8e329b2843b88cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Tue, 3 Dec 2024 12:18:43 +0200 Subject: [PATCH 04/26] =?UTF-8?q?Mahdollista=20KeyValueTablen=20upottamine?= =?UTF-8?q?n=20toisen=20samanlaisen=20sis=C3=A4=C3=A4n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components-v2/containers/KeyValueTable.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/web/app/components-v2/containers/KeyValueTable.tsx b/web/app/components-v2/containers/KeyValueTable.tsx index 15639f4a64..ab54af62f3 100644 --- a/web/app/components-v2/containers/KeyValueTable.tsx +++ b/web/app/components-v2/containers/KeyValueTable.tsx @@ -25,16 +25,26 @@ export const KeyValueTable = (props: KeyValueTableProps) => ( export type KeyValueRowProps = CommonPropsWithChildren<{ localizableLabel?: string | LocalizedString indent?: number + innerKeyValueTable?: boolean }> export const KeyValueRow = (props: KeyValueRowProps) => { const indent = props.indent || 0 + const nameSpans = props.innerKeyValueTable + ? { default: 8, small: 12, phone: 16 } + : { default: 4, small: 8, phone: 12 } + const valueSpans = { + default: 24 - nameSpans.default - indent, + small: 24 - nameSpans.small - indent, + phone: 24 - nameSpans.phone - indent + } + return props.children ? ( {props.indent && } @@ -42,11 +52,7 @@ export const KeyValueRow = (props: KeyValueRowProps) => { From b26dab5c962d04983ad7ff83079d4d3f09fa7a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Tue, 3 Dec 2024 12:19:10 +0200 Subject: [PATCH 05/26] =?UTF-8?q?Helppok=C3=A4ytt=C3=B6isempi=20statehook?= =?UTF-8?q?=20boolean-arvoille?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/util/useBooleanState.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 web/app/util/useBooleanState.ts diff --git a/web/app/util/useBooleanState.ts b/web/app/util/useBooleanState.ts new file mode 100644 index 0000000000..70212725dd --- /dev/null +++ b/web/app/util/useBooleanState.ts @@ -0,0 +1,8 @@ +import { useCallback, useMemo, useState } from "react"; + +export function useBooleanState(initialState: boolean): [boolean, () => void, () => void] { + const [state, setState] = useState(initialState); + const setTrue = useCallback(() => setState(true), []) + const setFalse = useCallback(() => setState(false), []) + return useMemo(() => [state, setTrue, setFalse], [setFalse, setTrue, state]) +} \ No newline at end of file From 5223e745aa47fd9801a1c74ff057e0d66e069c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Tue, 3 Dec 2024 12:19:48 +0200 Subject: [PATCH 06/26] =?UTF-8?q?N=C3=A4yt=C3=A4=20kursseista=20lis=C3=A4t?= =?UTF-8?q?ietoja=20tooltipiss=C3=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oph/koski/typemodel/TsFileUpdater.scala | 1 + .../opiskeluoikeus/OppiaineTable.less | 45 +++ .../opiskeluoikeus/OppiaineTable.tsx | 107 ++++++- .../types/fi/oph/koski/schema/Valinnaisuus.ts | 276 ++++++++++++++++++ 4 files changed, 425 insertions(+), 4 deletions(-) create mode 100644 web/app/types/fi/oph/koski/schema/Valinnaisuus.ts diff --git a/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala b/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala index 84cd46355e..0f9bf192c7 100644 --- a/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala +++ b/src/main/scala/fi/oph/koski/typemodel/TsFileUpdater.scala @@ -85,4 +85,5 @@ case class AdditionalExports( valinnanMahdollisuus: ValinnanMahdollisuus, arvioinniton: Arvioinniton, mahdollisestiArvioinniton: MahdollisestiArvioinniton, + valinnaisuus: Valinnaisuus, ) diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.less b/web/app/components-v2/opiskeluoikeus/OppiaineTable.less index 724b599b13..a448c27f54 100644 --- a/web/app/components-v2/opiskeluoikeus/OppiaineTable.less +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.less @@ -41,6 +41,7 @@ .Kurssi { display: inline-block; + position: relative; width: 4 * @baseline; margin-right: @baseline; @@ -58,3 +59,47 @@ } } } + +.KurssiDetails { + position: absolute; + z-index: 100; + + width: 550px; + padding: @padding; + + background: white; + box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.2); + + // .KurssiTooltip(@x, @y) tulee kurssi.less -tiedostosta + &-middle { + &-bottom { + .KurssiTooltip(middle, bottom); + } + &-top { + .KurssiTooltip(middle, topV2); + } + } + &-left { + &-bottom { + .KurssiTooltip(left, bottom); + } + &-top { + .KurssiTooltip(left, topV2); + } + } + &-right { + &-bottom { + .KurssiTooltip(right, bottom); + } + &-top { + .KurssiTooltip(right, topV2); + } + } +} + +.KurssiTooltip(@x-alignment, @y-alignment) when (@y-alignment = topV2) { + bottom: 10px + 4 * @baseline; + &:after { + .KurssiTriangle(@x-alignment, top); + } +} diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx index 6c83f4aa24..eac758372e 100644 --- a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx @@ -1,15 +1,19 @@ -import React from 'react' +import React, { useEffect, useRef, useState } from 'react' +import { ISO2FinnishDate } from '../../date/date' import { t } from '../../i18n/i18n' import { Arviointi } from '../../types/fi/oph/koski/schema/Arviointi' import { IBPäätasonSuoritus } from '../../types/fi/oph/koski/schema/IBPaatasonSuoritus' import { isMuidenLukioOpintojenPreIBSuoritus2019 } from '../../types/fi/oph/koski/schema/MuidenLukioOpintojenPreIBSuoritus2019' import { Suoritus } from '../../types/fi/oph/koski/schema/Suoritus' +import { isValinnaisuus } from '../../types/fi/oph/koski/schema/Valinnaisuus' import { isValinnanMahdollisuus } from '../../types/fi/oph/koski/schema/ValinnanMahdollisuus' import { parasArviointi } from '../../util/arvioinnit' import { sum } from '../../util/numbers' import { KoulutusmoduuliOf, OsasuoritusOf } from '../../util/schema' import { suoritusValmis } from '../../util/suoritus' +import { useBooleanState } from '../../util/useBooleanState' import { notUndefined } from '../../util/util' +import { KeyValueRow, KeyValueTable } from '../containers/KeyValueTable' // Vain OppiaineTablen tukemat päätason suoritukset (tätä komponenttia tullaan myöhemmin käyttämään ainakin lukion näkymille) export type OppiainePäätasonSuoritus = IBPäätasonSuoritus @@ -60,7 +64,7 @@ const OppiaineRow: React.FC = ({ oppiaine }) => {
{kurssit.map((kurssi, index) => ( - + ))}
@@ -88,13 +92,24 @@ const oppiaineenArvosana = (oppiaine: OppiaineOsasuoritus) => : parasArviointi(oppiaine.arviointi as Arviointi[])?.arvosana.koodiarvo type KurssiProps = { + oppiaine: OppiaineOsasuoritus kurssi: OsasuoritusOf } -const Kurssi: React.FC = ({ kurssi }) => { +const Kurssi: React.FC = ({ kurssi, oppiaine }) => { + const [tooltipVisible, openTooltip, closeTooltip] = useBooleanState(false) + const tooltipId = `kurssi-${oppiaine.koulutusmoduuli.tunniste.koodiarvo}-${kurssi.koulutusmoduuli.tunniste.koodiarvo}` + return (
-
+
{kurssi.koulutusmoduuli.tunniste.koodiarvo}
@@ -102,6 +117,7 @@ const Kurssi: React.FC = ({ kurssi }) => { ? parasArviointi(kurssi.arviointi as Arviointi[])?.arvosana.koodiarvo : null}
+ {tooltipVisible && }
) } @@ -120,3 +136,86 @@ const SuorituksenTilaIcon: React.FC = ({ // eslint-disable-next-line react/jsx-no-literals
) + +type TooltipXPosition = 'left' | 'right' | 'middle' +type TooltipYPosition = 'top' | 'bottom' + +type KurssiTooltipProps = { + id: string + kurssi: OsasuoritusOf +} + +const KurssiDetails: React.FC = ({ kurssi, id }) => { + const [xPos, setXPos] = useState() + const [yPos, setYPos] = useState() + const self = useRef(null) + + useEffect(() => { + const updatePosition = () => { + if (self.current) { + const rect = self.current.getBoundingClientRect() + setXPos( + rect.x < window.innerWidth / 3 + ? 'left' + : rect.x > (window.innerWidth * 2) / 3 + ? 'right' + : 'middle' + ) + setYPos(rect.y > window.innerHeight / 2 ? 'top' : 'bottom') + } + } + + updatePosition() + document.addEventListener('scroll', updatePosition) + document.body.addEventListener('resize', updatePosition) + return () => { + document.removeEventListener('scroll', updatePosition) + document.body.removeEventListener('resize', updatePosition) + } + }, []) + + return ( +
+ {xPos && yPos && ( + + )} +
+ ) +} diff --git a/web/app/types/fi/oph/koski/schema/Valinnaisuus.ts b/web/app/types/fi/oph/koski/schema/Valinnaisuus.ts new file mode 100644 index 0000000000..1461510224 --- /dev/null +++ b/web/app/types/fi/oph/koski/schema/Valinnaisuus.ts @@ -0,0 +1,276 @@ +import { + AikuistenPerusopetuksenPaikallinenOppiaine, + isAikuistenPerusopetuksenPaikallinenOppiaine +} from './AikuistenPerusopetuksenPaikallinenOppiaine' +import { + AikuistenPerusopetuksenUskonto, + isAikuistenPerusopetuksenUskonto +} from './AikuistenPerusopetuksenUskonto' +import { + AikuistenPerusopetuksenVierasTaiToinenKotimainenKieli, + isAikuistenPerusopetuksenVierasTaiToinenKotimainenKieli +} from './AikuistenPerusopetuksenVierasTaiToinenKotimainenKieli' +import { + AikuistenPerusopetuksenÄidinkieliJaKirjallisuus, + isAikuistenPerusopetuksenÄidinkieliJaKirjallisuus +} from './AikuistenPerusopetuksenAidinkieliJaKirjallisuus' +import { + AmmatillisenTutkinnonVierasTaiToinenKotimainenKieli, + isAmmatillisenTutkinnonVierasTaiToinenKotimainenKieli +} from './AmmatillisenTutkinnonVierasTaiToinenKotimainenKieli' +import { + AmmatillisenTutkinnonViestintäJaVuorovaikutusKielivalinnalla, + isAmmatillisenTutkinnonViestintäJaVuorovaikutusKielivalinnalla +} from './AmmatillisenTutkinnonViestintaJaVuorovaikutusKielivalinnalla' +import { + AmmatillisenTutkinnonÄidinkieli, + isAmmatillisenTutkinnonÄidinkieli +} from './AmmatillisenTutkinnonAidinkieli' +import { IBKurssi, isIBKurssi } from './IBKurssi' +import { IBOppiaineCAS, isIBOppiaineCAS } from './IBOppiaineCAS' +import { + IBOppiaineExtendedEssay, + isIBOppiaineExtendedEssay +} from './IBOppiaineExtendedEssay' +import { IBOppiaineLanguage, isIBOppiaineLanguage } from './IBOppiaineLanguage' +import { IBOppiaineMuu, isIBOppiaineMuu } from './IBOppiaineMuu' +import { + IBOppiaineTheoryOfKnowledge, + isIBOppiaineTheoryOfKnowledge +} from './IBOppiaineTheoryOfKnowledge' +import { + LukionMatematiikka2015, + isLukionMatematiikka2015 +} from './LukionMatematiikka2015' +import { + LukionMatematiikka2019, + isLukionMatematiikka2019 +} from './LukionMatematiikka2019' +import { + LukionMuuModuuliMuissaOpinnoissa2019, + isLukionMuuModuuliMuissaOpinnoissa2019 +} from './LukionMuuModuuliMuissaOpinnoissa2019' +import { + LukionMuuModuuliOppiaineissa2019, + isLukionMuuModuuliOppiaineissa2019 +} from './LukionMuuModuuliOppiaineissa2019' +import { + LukionMuuValtakunnallinenOppiaine2015, + isLukionMuuValtakunnallinenOppiaine2015 +} from './LukionMuuValtakunnallinenOppiaine2015' +import { + LukionMuuValtakunnallinenOppiaine2019, + isLukionMuuValtakunnallinenOppiaine2019 +} from './LukionMuuValtakunnallinenOppiaine2019' +import { + LukionOmanÄidinkielenOpinto, + isLukionOmanÄidinkielenOpinto +} from './LukionOmanAidinkielenOpinto' +import { + LukionPaikallinenOpintojakso2019, + isLukionPaikallinenOpintojakso2019 +} from './LukionPaikallinenOpintojakso2019' +import { LukionUskonto2015, isLukionUskonto2015 } from './LukionUskonto2015' +import { LukionUskonto2019, isLukionUskonto2019 } from './LukionUskonto2019' +import { + LukionVieraanKielenModuuliMuissaOpinnoissa2019, + isLukionVieraanKielenModuuliMuissaOpinnoissa2019 +} from './LukionVieraanKielenModuuliMuissaOpinnoissa2019' +import { + LukionVieraanKielenModuuliOppiaineissa2019, + isLukionVieraanKielenModuuliOppiaineissa2019 +} from './LukionVieraanKielenModuuliOppiaineissa2019' +import { + LukionÄidinkieliJaKirjallisuus2015, + isLukionÄidinkieliJaKirjallisuus2015 +} from './LukionAidinkieliJaKirjallisuus2015' +import { + LukionÄidinkieliJaKirjallisuus2019, + isLukionÄidinkieliJaKirjallisuus2019 +} from './LukionAidinkieliJaKirjallisuus2019' +import { + LukioonValmistavaÄidinkieliJaKirjallisuus, + isLukioonValmistavaÄidinkieliJaKirjallisuus +} from './LukioonValmistavaAidinkieliJaKirjallisuus' +import { + MuuAikuistenPerusopetuksenOppiaine, + isMuuAikuistenPerusopetuksenOppiaine +} from './MuuAikuistenPerusopetuksenOppiaine' +import { + MuuNuortenPerusopetuksenOppiaine, + isMuuNuortenPerusopetuksenOppiaine +} from './MuuNuortenPerusopetuksenOppiaine' +import { + MuuValtakunnallinenLukioonValmistavanKoulutuksenOppiaine, + isMuuValtakunnallinenLukioonValmistavanKoulutuksenOppiaine +} from './MuuValtakunnallinenLukioonValmistavanKoulutuksenOppiaine' +import { + MuuValtakunnallinenTutkinnonOsa, + isMuuValtakunnallinenTutkinnonOsa +} from './MuuValtakunnallinenTutkinnonOsa' +import { MuutKielet, isMuutKielet } from './MuutKielet' +import { + NuortenPerusopetuksenPaikallinenOppiaine, + isNuortenPerusopetuksenPaikallinenOppiaine +} from './NuortenPerusopetuksenPaikallinenOppiaine' +import { + NuortenPerusopetuksenUskonto, + isNuortenPerusopetuksenUskonto +} from './NuortenPerusopetuksenUskonto' +import { + NuortenPerusopetuksenVierasTaiToinenKotimainenKieli, + isNuortenPerusopetuksenVierasTaiToinenKotimainenKieli +} from './NuortenPerusopetuksenVierasTaiToinenKotimainenKieli' +import { + NuortenPerusopetuksenÄidinkieliJaKirjallisuus, + isNuortenPerusopetuksenÄidinkieliJaKirjallisuus +} from './NuortenPerusopetuksenAidinkieliJaKirjallisuus' +import { + PaikallinenAmmatillisenTutkinnonOsanOsaAlue, + isPaikallinenAmmatillisenTutkinnonOsanOsaAlue +} from './PaikallinenAmmatillisenTutkinnonOsanOsaAlue' +import { + PaikallinenLukionOppiaine2015, + isPaikallinenLukionOppiaine2015 +} from './PaikallinenLukionOppiaine2015' +import { + PaikallinenLukionOppiaine2019, + isPaikallinenLukionOppiaine2019 +} from './PaikallinenLukionOppiaine2019' +import { + PaikallinenLukioonValmistavanKoulutuksenOppiaine, + isPaikallinenLukioonValmistavanKoulutuksenOppiaine +} from './PaikallinenLukioonValmistavanKoulutuksenOppiaine' +import { + PaikallinenTelmaKoulutuksenOsa, + isPaikallinenTelmaKoulutuksenOsa +} from './PaikallinenTelmaKoulutuksenOsa' +import { + PaikallinenTutkinnonOsa, + isPaikallinenTutkinnonOsa +} from './PaikallinenTutkinnonOsa' +import { + PaikallinenValmaKoulutuksenOsa, + isPaikallinenValmaKoulutuksenOsa +} from './PaikallinenValmaKoulutuksenOsa' +import { + ValtakunnallinenAmmatillisenTutkinnonOsanOsaAlue, + isValtakunnallinenAmmatillisenTutkinnonOsanOsaAlue +} from './ValtakunnallinenAmmatillisenTutkinnonOsanOsaAlue' +import { + VierasTaiToinenKotimainenKieli2015, + isVierasTaiToinenKotimainenKieli2015 +} from './VierasTaiToinenKotimainenKieli2015' +import { + VierasTaiToinenKotimainenKieli2019, + isVierasTaiToinenKotimainenKieli2019 +} from './VierasTaiToinenKotimainenKieli2019' +import { + YhteinenTutkinnonOsa, + isYhteinenTutkinnonOsa +} from './YhteinenTutkinnonOsa' + +/** + * Valinnaisuus + * + * @see `fi.oph.koski.schema.Valinnaisuus` + */ +export type Valinnaisuus = + | AikuistenPerusopetuksenPaikallinenOppiaine + | AikuistenPerusopetuksenUskonto + | AikuistenPerusopetuksenVierasTaiToinenKotimainenKieli + | AikuistenPerusopetuksenÄidinkieliJaKirjallisuus + | AmmatillisenTutkinnonVierasTaiToinenKotimainenKieli + | AmmatillisenTutkinnonViestintäJaVuorovaikutusKielivalinnalla + | AmmatillisenTutkinnonÄidinkieli + | IBKurssi + | IBOppiaineCAS + | IBOppiaineExtendedEssay + | IBOppiaineLanguage + | IBOppiaineMuu + | IBOppiaineTheoryOfKnowledge + | LukionMatematiikka2015 + | LukionMatematiikka2019 + | LukionMuuModuuliMuissaOpinnoissa2019 + | LukionMuuModuuliOppiaineissa2019 + | LukionMuuValtakunnallinenOppiaine2015 + | LukionMuuValtakunnallinenOppiaine2019 + | LukionOmanÄidinkielenOpinto + | LukionPaikallinenOpintojakso2019 + | LukionUskonto2015 + | LukionUskonto2019 + | LukionVieraanKielenModuuliMuissaOpinnoissa2019 + | LukionVieraanKielenModuuliOppiaineissa2019 + | LukionÄidinkieliJaKirjallisuus2015 + | LukionÄidinkieliJaKirjallisuus2019 + | LukioonValmistavaÄidinkieliJaKirjallisuus + | MuuAikuistenPerusopetuksenOppiaine + | MuuNuortenPerusopetuksenOppiaine + | MuuValtakunnallinenLukioonValmistavanKoulutuksenOppiaine + | MuuValtakunnallinenTutkinnonOsa + | MuutKielet + | NuortenPerusopetuksenPaikallinenOppiaine + | NuortenPerusopetuksenUskonto + | NuortenPerusopetuksenVierasTaiToinenKotimainenKieli + | NuortenPerusopetuksenÄidinkieliJaKirjallisuus + | PaikallinenAmmatillisenTutkinnonOsanOsaAlue + | PaikallinenLukionOppiaine2015 + | PaikallinenLukionOppiaine2019 + | PaikallinenLukioonValmistavanKoulutuksenOppiaine + | PaikallinenTelmaKoulutuksenOsa + | PaikallinenTutkinnonOsa + | PaikallinenValmaKoulutuksenOsa + | ValtakunnallinenAmmatillisenTutkinnonOsanOsaAlue + | VierasTaiToinenKotimainenKieli2015 + | VierasTaiToinenKotimainenKieli2019 + | YhteinenTutkinnonOsa + +export const isValinnaisuus = (a: any): a is Valinnaisuus => + isAikuistenPerusopetuksenPaikallinenOppiaine(a) || + isAikuistenPerusopetuksenUskonto(a) || + isAikuistenPerusopetuksenVierasTaiToinenKotimainenKieli(a) || + isAikuistenPerusopetuksenÄidinkieliJaKirjallisuus(a) || + isAmmatillisenTutkinnonVierasTaiToinenKotimainenKieli(a) || + isAmmatillisenTutkinnonViestintäJaVuorovaikutusKielivalinnalla(a) || + isAmmatillisenTutkinnonÄidinkieli(a) || + isIBKurssi(a) || + isIBOppiaineCAS(a) || + isIBOppiaineExtendedEssay(a) || + isIBOppiaineLanguage(a) || + isIBOppiaineMuu(a) || + isIBOppiaineTheoryOfKnowledge(a) || + isLukionMatematiikka2015(a) || + isLukionMatematiikka2019(a) || + isLukionMuuModuuliMuissaOpinnoissa2019(a) || + isLukionMuuModuuliOppiaineissa2019(a) || + isLukionMuuValtakunnallinenOppiaine2015(a) || + isLukionMuuValtakunnallinenOppiaine2019(a) || + isLukionOmanÄidinkielenOpinto(a) || + isLukionPaikallinenOpintojakso2019(a) || + isLukionUskonto2015(a) || + isLukionUskonto2019(a) || + isLukionVieraanKielenModuuliMuissaOpinnoissa2019(a) || + isLukionVieraanKielenModuuliOppiaineissa2019(a) || + isLukionÄidinkieliJaKirjallisuus2015(a) || + isLukionÄidinkieliJaKirjallisuus2019(a) || + isLukioonValmistavaÄidinkieliJaKirjallisuus(a) || + isMuuAikuistenPerusopetuksenOppiaine(a) || + isMuuNuortenPerusopetuksenOppiaine(a) || + isMuuValtakunnallinenLukioonValmistavanKoulutuksenOppiaine(a) || + isMuuValtakunnallinenTutkinnonOsa(a) || + isMuutKielet(a) || + isNuortenPerusopetuksenPaikallinenOppiaine(a) || + isNuortenPerusopetuksenUskonto(a) || + isNuortenPerusopetuksenVierasTaiToinenKotimainenKieli(a) || + isNuortenPerusopetuksenÄidinkieliJaKirjallisuus(a) || + isPaikallinenAmmatillisenTutkinnonOsanOsaAlue(a) || + isPaikallinenLukionOppiaine2015(a) || + isPaikallinenLukionOppiaine2019(a) || + isPaikallinenLukioonValmistavanKoulutuksenOppiaine(a) || + isPaikallinenTelmaKoulutuksenOsa(a) || + isPaikallinenTutkinnonOsa(a) || + isPaikallinenValmaKoulutuksenOsa(a) || + isValtakunnallinenAmmatillisenTutkinnonOsanOsaAlue(a) || + isVierasTaiToinenKotimainenKieli2015(a) || + isVierasTaiToinenKotimainenKieli2019(a) || + isYhteinenTutkinnonOsa(a) From 0282886df10b095083248c3755d39fe0aa94f8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Tue, 3 Dec 2024 12:45:44 +0200 Subject: [PATCH 07/26] =?UTF-8?q?N=C3=A4yt=C3=A4=20kurssien=20m=C3=A4?= =?UTF-8?q?=C3=A4r=C3=A4t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../opiskeluoikeus/OppiaineTable.tsx | 5 +++- web/app/ib/IB.less | 13 +++++++++ web/app/ib/IBEditor.tsx | 29 +++++++++++++++++++ web/app/style/main.less | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 web/app/ib/IB.less diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx index eac758372e..550539c55d 100644 --- a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx @@ -52,6 +52,9 @@ type OppiaineRowProps = { const OppiaineRow: React.FC = ({ oppiaine }) => { const kurssit = oppiaine.osasuoritukset || [] + const kurssejaYhteensä = sum( + kurssit.map((k) => k.koulutusmoduuli.laajuus?.arvo || 0) + ) return ( @@ -68,7 +71,7 @@ const OppiaineRow: React.FC = ({ oppiaine }) => { ))}
- {kurssit.length} + {kurssejaYhteensä} {oppiaineenArvosana(oppiaine)} ) diff --git a/web/app/ib/IB.less b/web/app/ib/IB.less new file mode 100644 index 0000000000..f2bd0ea174 --- /dev/null +++ b/web/app/ib/IB.less @@ -0,0 +1,13 @@ +.IBPäätasonSuoritusEditor__footer { + display: flex; + justify-content: space-between; + padding: (@v-padding / 2) (@h-padding / 2); + + &_uusiOppiaine { + } + + &_yhteensä { + line-height: 2 * @baseline; + text-align: right; + } +} diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index 85c510c202..3c5f535294 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -17,6 +17,11 @@ import { import { Spacer } from '../components-v2/layout/Spacer' import { SuorituksenVahvistusField } from '../components-v2/opiskeluoikeus/SuorituksenVahvistus' import { OppiaineTable } from '../components-v2/opiskeluoikeus/OppiaineTable' +import { sum } from '../util/numbers' +import { PäätasonSuoritusOf } from '../util/opiskeluoikeus' +import { isLaajuusKursseissa } from '../types/fi/oph/koski/schema/LaajuusKursseissa' +import { OsasuoritusOf } from '../util/schema' +import { isEmpty } from 'fp-ts/lib/Array' export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps @@ -43,6 +48,9 @@ const IBPäätasonSuoritusEditor: React.FC< const [päätasonSuoritus, setPäätasonSuoritus] = usePäätasonSuoritus(form) const organisaatio = opiskeluoikeus.oppilaitos || opiskeluoikeus.koulutustoimija + const kurssejaYhteensä = useSuoritetutKurssitYhteensä( + päätasonSuoritus.suoritus + ) return ( + + {kurssejaYhteensä !== null && ( +
+ {t('Suoritettujen kurssien määrä yhteensä')} + {': '} + {kurssejaYhteensä} +
+ )}
) } + +const useSuoritetutKurssitYhteensä = ( + pts: PäätasonSuoritusOf +): number | null => { + const laajuudet = (pts.osasuoritukset || []).flatMap((oppiaine) => + (oppiaine.osasuoritukset || []).flatMap((kurssi) => + isLaajuusKursseissa(kurssi.koulutusmoduuli.laajuus) + ? [kurssi.koulutusmoduuli.laajuus.arvo] + : [] + ) + ) + return isEmpty(laajuudet) ? null : sum(laajuudet) +} diff --git a/web/app/style/main.less b/web/app/style/main.less index de22f9f833..bc4daf61e8 100644 --- a/web/app/style/main.less +++ b/web/app/style/main.less @@ -63,6 +63,7 @@ @import '../vst/common/VSTLisatiedot.less'; @import '../vst/osaamismerkki/OsaamismerkkiKuva.less'; @import '../vst/osaamismerkki/OsaamismerkkiTitle.less'; +@import '../ib/IB.less'; body { font-family: 'Source Sans Pro', sans-serif; From f37c58b4c1c453b8f44f6a96fc45df7b9634d0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Wed, 4 Dec 2024 10:33:30 +0200 Subject: [PATCH 08/26] Pre-IB-oppiaineosasuoritusten luonti (tietomalli) --- web/app/ib/oppiaineet/preIBOppiaine2015.ts | 115 ++++++++++++++++ web/app/ib/oppiaineet/preIBOppiaine2019.ts | 132 ++++++++++++++++++ web/app/ib/oppiaineet/tunnisteet.ts | 148 +++++++++++++++++++++ web/app/ib/state/preIBOppiaine.ts | 29 ++++ web/app/util/types.ts | 6 +- 5 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 web/app/ib/oppiaineet/preIBOppiaine2015.ts create mode 100644 web/app/ib/oppiaineet/preIBOppiaine2019.ts create mode 100644 web/app/ib/oppiaineet/tunnisteet.ts create mode 100644 web/app/ib/state/preIBOppiaine.ts diff --git a/web/app/ib/oppiaineet/preIBOppiaine2015.ts b/web/app/ib/oppiaineet/preIBOppiaine2015.ts new file mode 100644 index 0000000000..16282e42a2 --- /dev/null +++ b/web/app/ib/oppiaineet/preIBOppiaine2015.ts @@ -0,0 +1,115 @@ +import { IBOppiaineLanguage } from '../../types/fi/oph/koski/schema/IBOppiaineLanguage' +import { IBOppiaineMuu } from '../../types/fi/oph/koski/schema/IBOppiaineMuu' +import { LukionÄidinkieliJaKirjallisuus2015 } from '../../types/fi/oph/koski/schema/LukionAidinkieliJaKirjallisuus2015' +import { LukionMatematiikka2015 } from '../../types/fi/oph/koski/schema/LukionMatematiikka2015' +import { LukionMuuValtakunnallinenOppiaine2015 } from '../../types/fi/oph/koski/schema/LukionMuuValtakunnallinenOppiaine2015' +import { LukionUskonto2015 } from '../../types/fi/oph/koski/schema/LukionUskonto2015' +import { PaikallinenLukionOppiaine2015 } from '../../types/fi/oph/koski/schema/PaikallinenLukionOppiaine2015' +import { PreIBOppiaine2015 } from '../../types/fi/oph/koski/schema/PreIBOppiaine2015' +import { PreIBOppiaineenSuoritus2015 } from '../../types/fi/oph/koski/schema/PreIBOppiaineenSuoritus2015' +import { PreIBSuorituksenOsasuoritus2015 } from '../../types/fi/oph/koski/schema/PreIBSuorituksenOsasuoritus2015' +import { VierasTaiToinenKotimainenKieli2015 } from '../../types/fi/oph/koski/schema/VierasTaiToinenKotimainenKieli2015' +import { PreIBOppiaineProps } from '../state/preIBOppiaine' +import { + isIBOppiaineLanguageTunniste, + isIBOppiaineMuuTunniste, + isLukionMatematiikka2015Tunniste, + isLukionMuuValtakunnallinenOppiaine2015Tunniste, + isLukionUskonto2015Tunniste, + isLukionÄidinkieliJaKirjallisuus2015Tunniste, + isVierasTaiToinenKotimainenKieli2015Tunniste +} from './tunnisteet' + +export const createPreIBSuorituksenOsasuoritus2015 = ( + props: PreIBOppiaineProps +): PreIBSuorituksenOsasuoritus2015 | null => { + const koulutusmoduuli = createPreIBOppiaine2015(props) + return ( + koulutusmoduuli && + PreIBOppiaineenSuoritus2015({ + koulutusmoduuli + }) + ) +} + +const createPreIBOppiaine2015 = ({ + tunniste, + paikallinenTunniste, + kieli, + ryhmä, + matematiikanOppimäärä, + äidinkielenKieli, + paikallinenKuvaus +}: PreIBOppiaineProps): PreIBOppiaine2015 | null => { + if (isIBOppiaineLanguageTunniste(tunniste)) { + return kieli && ryhmä + ? IBOppiaineLanguage({ + pakollinen: true, + kieli, + ryhmä, + tunniste + }) + : null + } + + if (isIBOppiaineMuuTunniste(tunniste)) { + return ryhmä + ? IBOppiaineMuu({ + pakollinen: false, + ryhmä, + tunniste + }) + : null + } + + if (isLukionMatematiikka2015Tunniste(tunniste)) { + return matematiikanOppimäärä + ? LukionMatematiikka2015({ + pakollinen: true, + oppimäärä: matematiikanOppimäärä + }) + : null + } + + if (isLukionMuuValtakunnallinenOppiaine2015Tunniste(tunniste)) { + return LukionMuuValtakunnallinenOppiaine2015({ + tunniste, + pakollinen: true + }) + } + + if (isLukionUskonto2015Tunniste(tunniste)) { + return LukionUskonto2015({ pakollinen: true }) + } + + if (isLukionÄidinkieliJaKirjallisuus2015Tunniste(tunniste)) { + return äidinkielenKieli + ? LukionÄidinkieliJaKirjallisuus2015({ + pakollinen: true, + kieli: äidinkielenKieli + }) + : null + } + + if (isVierasTaiToinenKotimainenKieli2015Tunniste(tunniste)) { + return kieli + ? VierasTaiToinenKotimainenKieli2015({ + pakollinen: true, + kieli, + tunniste + }) + : null + } + + if (paikallinenTunniste) { + return paikallinenKuvaus + ? PaikallinenLukionOppiaine2015({ + pakollinen: true, + kuvaus: paikallinenKuvaus, + tunniste: paikallinenTunniste + }) + : null + } + + return null +} diff --git a/web/app/ib/oppiaineet/preIBOppiaine2019.ts b/web/app/ib/oppiaineet/preIBOppiaine2019.ts new file mode 100644 index 0000000000..3f86718044 --- /dev/null +++ b/web/app/ib/oppiaineet/preIBOppiaine2019.ts @@ -0,0 +1,132 @@ +import { Lukiodiplomit2019 } from '../../types/fi/oph/koski/schema/Lukiodiplomit2019' +import { LukionÄidinkieliJaKirjallisuus2019 } from '../../types/fi/oph/koski/schema/LukionAidinkieliJaKirjallisuus2019' +import { LukionMatematiikka2019 } from '../../types/fi/oph/koski/schema/LukionMatematiikka2019' +import { LukionMuuValtakunnallinenOppiaine2019 } from '../../types/fi/oph/koski/schema/LukionMuuValtakunnallinenOppiaine2019' +import { LukionOppiaineenPreIBSuoritus2019 } from '../../types/fi/oph/koski/schema/LukionOppiaineenPreIBSuoritus2019' +import { LukionUskonto2019 } from '../../types/fi/oph/koski/schema/LukionUskonto2019' +import { MuidenLukioOpintojenPreIBSuoritus2019 } from '../../types/fi/oph/koski/schema/MuidenLukioOpintojenPreIBSuoritus2019' +import { MuutLukionSuoritukset2019 } from '../../types/fi/oph/koski/schema/MuutLukionSuoritukset2019' +import { PaikallinenLukionOppiaine2019 } from '../../types/fi/oph/koski/schema/PaikallinenLukionOppiaine2019' +import { PreIBLukionOppiaine2019 } from '../../types/fi/oph/koski/schema/PreIBLukionOppiaine2019' +import { PreIBMuutSuorituksetTaiVastaavat2019 } from '../../types/fi/oph/koski/schema/PreIBMuutSuorituksetTaiVastaavat2019' +import { PreIBSuorituksenOsasuoritus2019 } from '../../types/fi/oph/koski/schema/PreIBSuorituksenOsasuoritus2019' +import { TemaattisetOpinnot2019 } from '../../types/fi/oph/koski/schema/TemaattisetOpinnot2019' +import { VierasTaiToinenKotimainenKieli2019 } from '../../types/fi/oph/koski/schema/VierasTaiToinenKotimainenKieli2019' +import { PreIBOppiaineProps } from '../state/preIBOppiaine' +import { + isLukiodiplomit2019Tunniste, + isLukionMatematiikka2019Tunniste, + isLukionMuuValtakunnallinenOppiaine2019Tunniste, + isLukionUskonto2019Tunniste, + isLukionÄidinkieliJaKirjallisuus2019Tunniste, + isMuutLukionSuoritukset2019Tunniste, + isTemaattisetOpinnot2019Tunniste, + isVierasTaiToinenKotimainenKieli2019Tunniste +} from './tunnisteet' + +export const createPreIBSuorituksenOsasuoritus2019 = ( + props: PreIBOppiaineProps +): PreIBSuorituksenOsasuoritus2019 | null => + createLukionOppiaineenPreIBSuoritus2019(props) || + createMuidenLukioOpintojenPreIBSuoritus2019(props) + +const createLukionOppiaineenPreIBSuoritus2019 = ( + props: PreIBOppiaineProps +): LukionOppiaineenPreIBSuoritus2019 | null => { + const koulutusmoduuli = createPreIBLukionOppiaine2019(props) + return ( + koulutusmoduuli && + LukionOppiaineenPreIBSuoritus2019({ + koulutusmoduuli + }) + ) +} + +const createPreIBLukionOppiaine2019 = ({ + tunniste, + matematiikanOppimäärä, + äidinkielenKieli, + paikallinenTunniste, + paikallinenKuvaus, + kieli +}: PreIBOppiaineProps): PreIBLukionOppiaine2019 | null => { + if (isLukionMatematiikka2019Tunniste(tunniste)) { + return matematiikanOppimäärä + ? LukionMatematiikka2019({ + pakollinen: true, + oppimäärä: matematiikanOppimäärä + }) + : null + } + + if (isLukionMuuValtakunnallinenOppiaine2019Tunniste(tunniste)) { + return LukionMuuValtakunnallinenOppiaine2019({ + tunniste, + pakollinen: true + }) + } + + if (isLukionUskonto2019Tunniste(tunniste)) { + return LukionUskonto2019({ + pakollinen: true + }) + } + + if (isLukionÄidinkieliJaKirjallisuus2019Tunniste(tunniste)) { + return äidinkielenKieli + ? LukionÄidinkieliJaKirjallisuus2019({ + kieli: äidinkielenKieli, + pakollinen: true + }) + : null + } + + if (isVierasTaiToinenKotimainenKieli2019Tunniste(tunniste)) { + return kieli + ? VierasTaiToinenKotimainenKieli2019({ + tunniste, + kieli, + pakollinen: true + }) + : null + } + + if (paikallinenTunniste && paikallinenKuvaus) { + return PaikallinenLukionOppiaine2019({ + tunniste: paikallinenTunniste, + kuvaus: paikallinenKuvaus, + pakollinen: true + }) + } + + return null +} + +const createMuidenLukioOpintojenPreIBSuoritus2019 = ( + props: PreIBOppiaineProps +): MuidenLukioOpintojenPreIBSuoritus2019 | null => { + const koulutusmoduuli = createPreIBMuutSuorituksetTaiVastaavat2019(props) + return ( + koulutusmoduuli && + MuidenLukioOpintojenPreIBSuoritus2019({ + koulutusmoduuli + }) + ) +} + +const createPreIBMuutSuorituksetTaiVastaavat2019 = ({ + tunniste +}: PreIBOppiaineProps): PreIBMuutSuorituksetTaiVastaavat2019 | null => { + if (isLukiodiplomit2019Tunniste(tunniste)) { + return Lukiodiplomit2019({ tunniste }) + } + + if (isMuutLukionSuoritukset2019Tunniste(tunniste)) { + return MuutLukionSuoritukset2019({ tunniste }) + } + + if (isTemaattisetOpinnot2019Tunniste(tunniste)) { + return TemaattisetOpinnot2019({ tunniste }) + } + return null +} diff --git a/web/app/ib/oppiaineet/tunnisteet.ts b/web/app/ib/oppiaineet/tunnisteet.ts new file mode 100644 index 0000000000..46d63d413c --- /dev/null +++ b/web/app/ib/oppiaineet/tunnisteet.ts @@ -0,0 +1,148 @@ +import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' +import { ItemOf } from '../../util/types' + +export const tunnisteGuard = + (uri: U, koodiarvot: T) => + (k?: Koodistokoodiviite): k is Koodistokoodiviite> => + !!k && k.koodistoUri === uri && koodiarvot.includes(k.koodiarvo) + +export const tunnisteUriGuard = + (uri: U) => + (k?: Koodistokoodiviite): k is Koodistokoodiviite => + !!k && k.koodistoUri === uri + +export const isIBOppiaineLanguageTunniste = tunnisteGuard('oppiaineetib', [ + 'A', + 'A2', + 'B', + 'AB', + 'CLA' +] as const) + +export const isIBOppiaineMuuTunniste = tunnisteGuard('oppiaineetib', [ + 'BIO', + 'BU', + 'CHE', + 'DAN', + 'DIS', + 'ECO', + 'FIL', + 'GEO', + 'HIS', + 'MAT', + 'MATFT', + 'MATST', + 'MUS', + 'PHI', + 'PHY', + 'POL', + 'PSY', + 'REL', + 'SOC', + 'ESS', + 'THE', + 'VA', + 'CS', + 'LIT', + 'INF', + 'DES', + 'SPO', + 'MATAA', + 'MATAI' +] as const) + +export const isLukionMatematiikka2015Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['MA'] +) + +export const isLukionMuuValtakunnallinenOppiaine2015Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + [ + 'HI', + 'MU', + 'BI', + 'PS', + 'ET', + 'KO', + 'FI', + 'KE', + 'YH', + 'TE', + 'KS', + 'FY', + 'GE', + 'LI', + 'KU', + 'OP' + ] as const +) + +export const isLukionUskonto2015Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['KT'] as const +) + +export const isLukionÄidinkieliJaKirjallisuus2015Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['AI'] as const +) + +export const isVierasTaiToinenKotimainenKieli2015Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['A1', 'A2', 'B1', 'B2', 'B3', 'AOM'] as const +) + +export const isLukionMatematiikka2019Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['MA'] as const +) + +export const isLukionMuuValtakunnallinenOppiaine2019Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + [ + 'HI', + 'MU', + 'BI', + 'PS', + 'ET', + 'FI', + 'KE', + 'YH', + 'TE', + 'FY', + 'GE', + 'LI', + 'KU', + 'OP' + ] as const +) + +export const isLukionUskonto2019Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['KT'] as const +) + +export const isLukionÄidinkieliJaKirjallisuus2019Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['AI'] as const +) + +export const isVierasTaiToinenKotimainenKieli2019Tunniste = tunnisteGuard( + 'koskioppiaineetyleissivistava', + ['A', 'B1', 'B2', 'B3', 'AOM'] as const +) + +export const isLukiodiplomit2019Tunniste = tunnisteGuard('lukionmuutopinnot', [ + 'LD' +] as const) + +export const isMuutLukionSuoritukset2019Tunniste = tunnisteGuard( + 'lukionmuutopinnot', + ['MS'] as const +) + +export const isTemaattisetOpinnot2019Tunniste = tunnisteGuard( + 'lukionmuutopinnot', + ['TO'] as const +) diff --git a/web/app/ib/state/preIBOppiaine.ts b/web/app/ib/state/preIBOppiaine.ts new file mode 100644 index 0000000000..51d904a729 --- /dev/null +++ b/web/app/ib/state/preIBOppiaine.ts @@ -0,0 +1,29 @@ +import { useMemo } from 'react' +import { useKoodistot } from '../../appstate/koodisto' +import { + DialogField, + useDialogField +} from '../../components-v2/createdialog/DialogField' +import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' +import { LocalizedString } from '../../types/fi/oph/koski/schema/LocalizedString' +import { PaikallinenKoodi } from '../../types/fi/oph/koski/schema/PaikallinenKoodi' +import { PreIBSuorituksenOsasuoritus2015 } from '../../types/fi/oph/koski/schema/PreIBSuorituksenOsasuoritus2015' +import { createPreIBSuorituksenOsasuoritus2015 } from '../oppiaineet/preIBOppiaine2015' +import { + isIBOppiaineLanguageTunniste, + isIBOppiaineMuuTunniste, + isLukionMatematiikka2015Tunniste, + isLukionÄidinkieliJaKirjallisuus2015Tunniste, + isVierasTaiToinenKotimainenKieli2015Tunniste +} from '../oppiaineet/tunnisteet' + +export type PreIBOppiaineProps = { + tunniste?: PreIBOppiaineTunniste + paikallinenTunniste?: PaikallinenKoodi + kieli?: Koodistokoodiviite<'kielivalikoima'> + ryhmä?: Koodistokoodiviite<'aineryhmaib'> + matematiikanOppimäärä?: Koodistokoodiviite<'oppiainematematiikka'> + äidinkielenKieli?: Koodistokoodiviite<'oppiaineaidinkielijakirjallisuus'> + paikallinenKuvaus?: LocalizedString +} + diff --git a/web/app/util/types.ts b/web/app/util/types.ts index 7665879345..b4c6c83f9f 100644 --- a/web/app/util/types.ts +++ b/web/app/util/types.ts @@ -17,7 +17,7 @@ export type CollectableOptic = | $.Traversal | $.Fold -export type ItemOf = S[0] +export type ItemOf = S[0] export type OpiskeluoikeudenTyyppiOf = KoodiarvotOf< T['tyyppi'] @@ -35,5 +35,5 @@ export const shortClassName = (fullClassName: string): string => { export const isKoodistoOf = (koodistoUri: T) => - (koodiviite?: Koodistokoodiviite): koodiviite is Koodistokoodiviite => - koodiviite?.koodistoUri === koodistoUri + (koodiviite?: Koodistokoodiviite): koodiviite is Koodistokoodiviite => + koodiviite?.koodistoUri === koodistoUri From cfdaf6138b1c7f0fba8e39f0f86c4804754d73f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Thu, 5 Dec 2024 09:53:02 +0200 Subject: [PATCH 09/26] =?UTF-8?q?Poista=20popupin=20h=C3=A4iritsev=C3=A4?= =?UTF-8?q?=20"h=C3=B6lskyminen"=20valikkojen=20avautuessa=20ja=20sulkeutu?= =?UTF-8?q?essa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/components-v2/containers/Modal.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components-v2/containers/Modal.less b/web/app/components-v2/containers/Modal.less index 5248b21e8b..f4abff421b 100644 --- a/web/app/components-v2/containers/Modal.less +++ b/web/app/components-v2/containers/Modal.less @@ -5,11 +5,11 @@ left: 0; right: 0; margin: 0 !important; - padding: @padding !important; + padding: (@v-padding * 3) @h-padding !important; display: flex; justify-content: center; - align-items: center; + align-items: flex-start; background: rgba(255, 255, 255, 0.85); From cc10a5979ea1157fe33daf2022ac9b14edd438d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Thu, 5 Dec 2024 10:28:54 +0200 Subject: [PATCH 10/26] =?UTF-8?q?Uuden=20oppiaineen=20lis=C3=A4ysdialogi?= =?UTF-8?q?=20pre-ib=202015:lle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components-v2/createdialog/DialogField.ts | 25 +++ web/app/ib/IBEditor.tsx | 201 ++++++++++++++++-- web/app/ib/state/options.ts | 40 ++++ web/app/ib/state/preIBOppiaine.ts | 82 +++++++ web/app/util/patternmatch.ts | 34 +++ .../components/DialogKoodistoSelect.tsx | 2 +- .../components/OpintokokonaisuusSelect.tsx | 2 +- ...sivist\303\244v\303\244OppiaineSelect.tsx" | 12 +- web/app/uusiopiskeluoikeus/state/state.ts | 30 +-- 9 files changed, 373 insertions(+), 55 deletions(-) create mode 100644 web/app/components-v2/createdialog/DialogField.ts create mode 100644 web/app/ib/state/options.ts create mode 100644 web/app/util/patternmatch.ts diff --git a/web/app/components-v2/createdialog/DialogField.ts b/web/app/components-v2/createdialog/DialogField.ts new file mode 100644 index 0000000000..ae773c595b --- /dev/null +++ b/web/app/components-v2/createdialog/DialogField.ts @@ -0,0 +1,25 @@ +import { useState, useEffect } from 'react' + +export type DialogField = { + value?: T + set: (t?: T) => void + visible: boolean +} + +export const useDialogField = ( + isVisible: boolean, + defaultValue?: () => T +): DialogField => { + const [value, set] = useState(defaultValue) + const [visible, setVisible] = useState(false) + + useEffect(() => { + setVisible(isVisible) + if (!isVisible) { + set(defaultValue) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isVisible]) + + return { value, set, visible } +} diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index 3c5f535294..44debe8316 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -1,27 +1,51 @@ -import React from 'react' +import { isEmpty } from 'fp-ts/lib/Array' +import React, { useCallback } from 'react' import { useSchema } from '../appstate/constraints' -import { FormModel, useForm } from '../components-v2/forms/FormModel' -import { AdaptedOpiskeluoikeusEditorProps } from '../components-v2/interoperability/useUiAdapter' -import { OpiskeluoikeusTitle } from '../components-v2/opiskeluoikeus/OpiskeluoikeusTitle' -import { IBOpiskeluoikeus } from '../types/fi/oph/koski/schema/IBOpiskeluoikeus' -import { t } from '../i18n/i18n' import { EditorContainer, usePäätasonSuoritus } from '../components-v2/containers/EditorContainer' -import { LukionOpiskeluoikeusjakso } from '../types/fi/oph/koski/schema/LukionOpiskeluoikeusjakso' import { - ibKoulutusNimi, - IBPäätasonSuoritusTiedot -} from './IBPaatasonSuoritusTiedot' + Modal, + ModalBody, + ModalFooter, + ModalTitle +} from '../components-v2/containers/Modal' +import { FlatButton } from '../components-v2/controls/FlatButton' +import { RaisedButton } from '../components-v2/controls/RaisedButton' +import { FormModel, useForm } from '../components-v2/forms/FormModel' +import { AdaptedOpiskeluoikeusEditorProps } from '../components-v2/interoperability/useUiAdapter' import { Spacer } from '../components-v2/layout/Spacer' -import { SuorituksenVahvistusField } from '../components-v2/opiskeluoikeus/SuorituksenVahvistus' +import { OpiskeluoikeusTitle } from '../components-v2/opiskeluoikeus/OpiskeluoikeusTitle' import { OppiaineTable } from '../components-v2/opiskeluoikeus/OppiaineTable' +import { SuorituksenVahvistusField } from '../components-v2/opiskeluoikeus/SuorituksenVahvistus' +import { t } from '../i18n/i18n' +import { IBOpiskeluoikeus } from '../types/fi/oph/koski/schema/IBOpiskeluoikeus' +import { IBTutkinto } from '../types/fi/oph/koski/schema/IBTutkinto' +import { isLaajuusKursseissa } from '../types/fi/oph/koski/schema/LaajuusKursseissa' +import { LukionOpiskeluoikeusjakso } from '../types/fi/oph/koski/schema/LukionOpiskeluoikeusjakso' +import { PreIBKoulutusmoduuli2015 } from '../types/fi/oph/koski/schema/PreIBKoulutusmoduuli2015' +import { PreIBKoulutusmoduuli2019 } from '../types/fi/oph/koski/schema/PreIBKoulutusmoduuli2019' +import { PreIBSuorituksenOsasuoritus2015 } from '../types/fi/oph/koski/schema/PreIBSuorituksenOsasuoritus2015' +import { appendOptional } from '../util/array' +import { koodiviiteId } from '../util/koodisto' import { sum } from '../util/numbers' import { PäätasonSuoritusOf } from '../util/opiskeluoikeus' -import { isLaajuusKursseissa } from '../types/fi/oph/koski/schema/LaajuusKursseissa' -import { OsasuoritusOf } from '../util/schema' -import { isEmpty } from 'fp-ts/lib/Array' +import { match } from '../util/patternmatch' +import { useBooleanState } from '../util/useBooleanState' +import { DialogSelect } from '../uusiopiskeluoikeus/components/DialogSelect' +import { + ibKoulutusNimi, + IBPäätasonSuoritusTiedot +} from './IBPaatasonSuoritusTiedot' +import { + useAineryhmäOptions, + useKielivalikoimaOptions, + useMatematiikanOppimääräOptions, + usePreIBTunnisteOptions, + useÄidinkielenKieliOptions +} from './state/options' +import { useUusiPreIB2015OppiaineState } from './state/preIBOppiaine' export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps @@ -51,6 +75,19 @@ const IBPäätasonSuoritusEditor: React.FC< const kurssejaYhteensä = useSuoritetutKurssitYhteensä( päätasonSuoritus.suoritus ) + const [addOppiaineVisible, showAddOppiaineDialog, hideAddOppiaineDialog] = + useBooleanState(false) + + const addOppiaine = useCallback( + (oppiaine: PreIBSuorituksenOsasuoritus2015) => { + form.updateAt( + päätasonSuoritus.path.prop('osasuoritukset') as any, + appendOptional(oppiaine) + ) + hideAddOppiaineDialog() + }, + [form, hideAddOppiaineDialog, päätasonSuoritus.path] + ) return ( {kurssejaYhteensä !== null && ( -
- {t('Suoritettujen kurssien määrä yhteensä')} - {': '} - {kurssejaYhteensä} -
+
+ {form.editMode && ( + + {t('Lisää oppiaine')} + + )} +
+ {t('Suoritettujen kurssien määrä yhteensä')} + {': '} + {kurssejaYhteensä} +
+
)} + + {addOppiaineVisible && + match(päätasonSuoritus.suoritus.koulutusmoduuli) + .isClass(PreIBKoulutusmoduuli2015, () => ( + + )) + .isClass(PreIBKoulutusmoduuli2019, () =>

TODO

) + .isClass(IBTutkinto, () =>

TODO

) + .getOrNull()}
) } @@ -101,3 +157,110 @@ const useSuoritetutKurssitYhteensä = ( ) return isEmpty(laajuudet) ? null : sum(laajuudet) } + +type UusiPreIB2015OppiaineDialogProps = { + onClose: () => void + onSubmit: (oppiaine: PreIBSuorituksenOsasuoritus2015) => void +} + +const UusiPreIB2015OppiaineDialog: React.FC< + UusiPreIB2015OppiaineDialogProps +> = (props) => { + const state = useUusiPreIB2015OppiaineState() + const tunnisteet = usePreIBTunnisteOptions() + const kielet = useKielivalikoimaOptions(state.kieli.visible) + const matematiikanOppimäärät = useMatematiikanOppimääräOptions( + state.matematiikanOppimäärä.visible + ) + const ryhmät = useAineryhmäOptions(state.ryhmä.visible) + const äidinkielenKielet = useÄidinkielenKieliOptions( + state.äidinkielenKieli.visible + ) + + const onSubmit = useCallback(() => { + if (state.result) { + props.onSubmit(state.result) + } + }, [props, state.result]) + + return ( + + {t('Oppiaineen lisäys')} + + {tunnisteet && ( + + )} + {state.kieli.visible && kielet && ( + + )} + {state.ryhmä.visible && ryhmät && ( + + )} + {state.matematiikanOppimäärä.visible && matematiikanOppimäärät && ( + + )} + {state.äidinkielenKieli.visible && äidinkielenKielet && ( + + )} + + + + {t('Peruuta')} + + + {t('Lisää opiskeluoikeus')} + + + + ) +} diff --git a/web/app/ib/state/options.ts b/web/app/ib/state/options.ts new file mode 100644 index 0000000000..baee601020 --- /dev/null +++ b/web/app/ib/state/options.ts @@ -0,0 +1,40 @@ +import { useMemo } from 'react' +import { useKoodisto, useKoodistot } from '../../appstate/koodisto' +import { + groupKoodistoToOptions, + SelectOption +} from '../../components-v2/controls/Select' +import { + PreIBOppiaineTunniste, + PreIBOppiaineTunnisteKoodistoUri +} from './preIBOppiaine' +import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' + +export const usePreIBTunnisteOptions = (): + | SelectOption[] + | null => { + const tunnisteet = useKoodistot( + 'oppiaineetib', + 'koskioppiaineetyleissivistava' + ) + return useMemo( + () => tunnisteet && groupKoodistoToOptions(tunnisteet), + [tunnisteet] + ) +} + +const optionLoader = + (koodistoUri: T) => + (required: boolean): SelectOption>[] | null => { + const kielet = useKoodisto(required ? koodistoUri : null) + return useMemo(() => kielet && groupKoodistoToOptions(kielet), [kielet]) + } + +export const useKielivalikoimaOptions = optionLoader('kielivalikoima') +export const useMatematiikanOppimääräOptions = optionLoader( + 'oppiainematematiikka' +) +export const useAineryhmäOptions = optionLoader('aineryhmaib') +export const useÄidinkielenKieliOptions = optionLoader( + 'oppiaineaidinkielijakirjallisuus' +) diff --git a/web/app/ib/state/preIBOppiaine.ts b/web/app/ib/state/preIBOppiaine.ts index 51d904a729..1c7d21fdf1 100644 --- a/web/app/ib/state/preIBOppiaine.ts +++ b/web/app/ib/state/preIBOppiaine.ts @@ -27,3 +27,85 @@ export type PreIBOppiaineProps = { paikallinenKuvaus?: LocalizedString } +export type PreIBOppiaineTunnisteKoodistoUri = + | 'oppiaineetib' + | 'koskioppiaineetyleissivistava' + +export type PreIBOppiaineTunniste = + Koodistokoodiviite + +export type UusiPreIBOppiaineState = { + tunniste: DialogField + paikallinenTunniste: DialogField + kieli: DialogField> + ryhmä: DialogField> + matematiikanOppimäärä: DialogField> + äidinkielenKieli: DialogField< + Koodistokoodiviite<'oppiaineaidinkielijakirjallisuus'> + > + paikallinenKuvaus: DialogField + result: T | null +} + +export const useUusiPreIB2015OppiaineState = + (): UusiPreIBOppiaineState => { + const tunnisteOptions = useKoodistot< + 'oppiaineetib' | 'oppiaine' | 'koskioppiaineetyleissivistava' + >('oppiaineetib', 'oppiaine', 'koskioppiaineetyleissivistava') + + const tunniste = useDialogField(true) + + const kieli = useDialogField>( + isIBOppiaineLanguageTunniste(tunniste.value) || + isVierasTaiToinenKotimainenKieli2015Tunniste(tunniste.value) + ) + + const ryhmä = useDialogField>( + isIBOppiaineLanguageTunniste(tunniste.value) || + isIBOppiaineMuuTunniste(tunniste.value) + ) + + const matematiikanOppimäärä = useDialogField< + Koodistokoodiviite<'oppiainematematiikka'> + >(isLukionMatematiikka2015Tunniste(tunniste.value)) + + const äidinkielenKieli = useDialogField< + Koodistokoodiviite<'oppiaineaidinkielijakirjallisuus'> + >(isLukionÄidinkieliJaKirjallisuus2015Tunniste(tunniste.value)) + + const paikallinenTunniste = useDialogField(false) + const paikallinenKuvaus = useDialogField(false) + + const result = useMemo( + () => + createPreIBSuorituksenOsasuoritus2015({ + tunniste: tunniste.value, + paikallinenTunniste: paikallinenTunniste.value, + kieli: kieli.value, + ryhmä: ryhmä.value, + matematiikanOppimäärä: matematiikanOppimäärä.value, + äidinkielenKieli: äidinkielenKieli.value, + paikallinenKuvaus: paikallinenKuvaus.value + }), + [ + kieli.value, + matematiikanOppimäärä.value, + paikallinenKuvaus.value, + paikallinenTunniste.value, + ryhmä.value, + tunniste.value, + äidinkielenKieli.value + ] + ) + + return { + tunniste, + paikallinenTunniste, + kieli, + ryhmä, + matematiikanOppimäärä, + äidinkielenKieli, + paikallinenKuvaus, + result + } + } diff --git a/web/app/util/patternmatch.ts b/web/app/util/patternmatch.ts new file mode 100644 index 0000000000..5563fde0a9 --- /dev/null +++ b/web/app/util/patternmatch.ts @@ -0,0 +1,34 @@ +export type PatternMatcher = { + case: (when: (a: T) => boolean, then: (a: T) => R) => PatternMatcher + isClass: ( + classDef: { className: string }, + then: (a: T) => R + ) => PatternMatcher + getOrNull: () => R | null +} + +export const match = (value: T): PatternMatcher => { + let firstResult: R | undefined + + return { + case(when, then) { + if (firstResult === undefined && when(value)) { + firstResult = then(value) + } + return this + }, + isClass(classDef, then) { + if ( + firstResult === undefined && + typeof value === 'object' && + (value as any).$class === classDef.className + ) { + firstResult = then(value) + } + return this + }, + getOrNull() { + return firstResult === undefined ? null : firstResult + } + } +} diff --git a/web/app/uusiopiskeluoikeus/components/DialogKoodistoSelect.tsx b/web/app/uusiopiskeluoikeus/components/DialogKoodistoSelect.tsx index 0b970905d5..531d86e0bc 100644 --- a/web/app/uusiopiskeluoikeus/components/DialogKoodistoSelect.tsx +++ b/web/app/uusiopiskeluoikeus/components/DialogKoodistoSelect.tsx @@ -9,7 +9,7 @@ import { } from '../../components-v2/controls/Select' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' import { koodistokoodiviiteId } from '../../util/koodisto' -import { DialogField } from '../state/state' +import { DialogField } from '../../components-v2/createdialog/DialogField' import { DialogSelect } from './DialogSelect' export type DialogKoodistoSelectProps = { diff --git a/web/app/uusiopiskeluoikeus/components/OpintokokonaisuusSelect.tsx b/web/app/uusiopiskeluoikeus/components/OpintokokonaisuusSelect.tsx index a278a85714..d5fb83fa54 100644 --- a/web/app/uusiopiskeluoikeus/components/OpintokokonaisuusSelect.tsx +++ b/web/app/uusiopiskeluoikeus/components/OpintokokonaisuusSelect.tsx @@ -1,6 +1,6 @@ import React from 'react' import { DialogKoodistoSelect } from './DialogKoodistoSelect' -import { DialogField } from '../state/state' +import { DialogField } from '../../components-v2/createdialog/DialogField' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' import { KoodistokoodiviiteKoodistonNimellä } from '../../appstate/koodisto' import { t } from '../../i18n/i18n' diff --git "a/web/app/uusiopiskeluoikeus/components/Yleissivist\303\244v\303\244OppiaineSelect.tsx" "b/web/app/uusiopiskeluoikeus/components/Yleissivist\303\244v\303\244OppiaineSelect.tsx" index 3484c4ad74..2e01b3f62f 100644 --- "a/web/app/uusiopiskeluoikeus/components/Yleissivist\303\244v\303\244OppiaineSelect.tsx" +++ "b/web/app/uusiopiskeluoikeus/components/Yleissivist\303\244v\303\244OppiaineSelect.tsx" @@ -1,4 +1,5 @@ import { flow } from 'fp-ts/lib/function' +import * as Ord from 'fp-ts/Ord' import React, { useMemo } from 'react' import { useSchema } from '../../appstate/constraints' import { @@ -6,21 +7,14 @@ import { KoodistokoodiviiteKoodistonNimelläOrd, useKoodisto } from '../../appstate/koodisto' -import { - SelectOption, - groupKoodistoToOptions -} from '../../components-v2/controls/Select' +import { groupKoodistoToOptions } from '../../components-v2/controls/Select' import { t } from '../../i18n/i18n' +import { valuesFirst } from '../../util/array' import * as C from '../../util/constraints' import { koodistokoodiviiteId } from '../../util/koodisto' import { DialogKoodistoSelect } from '../components/DialogKoodistoSelect' import { DialogSelect } from '../components/DialogSelect' import { SuoritusFieldsProps } from '../opiskeluoikeusSpecificFields' -import * as A from 'fp-ts/Array' -import * as Ord from 'fp-ts/Ord' -import * as string from 'fp-ts/string' -import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' -import { valuesFirst } from '../../util/array' export type YleissivistäväOppiaineSelectProps = SuoritusFieldsProps & { koulutusmoduuliClassName: string diff --git a/web/app/uusiopiskeluoikeus/state/state.ts b/web/app/uusiopiskeluoikeus/state/state.ts index a7b43f9688..fd2fd18f94 100644 --- a/web/app/uusiopiskeluoikeus/state/state.ts +++ b/web/app/uusiopiskeluoikeus/state/state.ts @@ -1,7 +1,11 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useMemo } from 'react' import { isSuccess, useApiOnce } from '../../api-fetch' import { useSchema } from '../../appstate/constraints' import { Peruste } from '../../appstate/peruste' +import { + DialogField, + useDialogField +} from '../../components-v2/createdialog/DialogField' import { todayISODate } from '../../date/date' import { OrganisaatioHierarkia } from '../../types/fi/oph/koski/organisaatio/OrganisaatioHierarkia' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' @@ -407,30 +411,6 @@ export const useUusiOpiskeluoikeusDialogState = } } -export type DialogField = { - value?: T - set: (t?: T) => void - visible: boolean -} - -export const useDialogField = ( - isVisible: boolean, - defaultValue?: () => T -): DialogField => { - const [value, set] = useState(defaultValue) - const [visible, setVisible] = useState(false) - - useEffect(() => { - setVisible(isVisible) - if (!isVisible) { - set(defaultValue) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isVisible]) - - return { value, set, visible } -} - export const asObject = (className?: string) => className ? { $class: className } : undefined From 8172920f186364f106e8299f85ac8f93d1afd3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Thu, 5 Dec 2024 13:52:12 +0200 Subject: [PATCH 11/26] =?UTF-8?q?N=C3=A4yt=C3=A4=20vain=20oikeat=20pre-ib:?= =?UTF-8?q?lle=20valittavat=20oppiaineet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../localization/koski-default-texts.json | 4 +- web/app/components-v2/controls/Select.tsx | 20 +++++ web/app/ib/IBEditor.tsx | 11 ++- web/app/ib/state/options.ts | 78 ++++++++++++++++--- web/app/util/objects.ts | 3 + 5 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/main/resources/localization/koski-default-texts.json b/src/main/resources/localization/koski-default-texts.json index fd6ca54582..c11e88bcdb 100644 --- a/src/main/resources/localization/koski-default-texts.json +++ b/src/main/resources/localization/koski-default-texts.json @@ -2761,5 +2761,7 @@ "raportti-excel-must-tiedoston-etuliite": "muks_suoritustiedot", "raportti-excel-kolumni-yhteislaajuusKaikkiTuntia": "Yhteislaajuus (tuntia)", "Siirtynyt uusiin tutkinnon perusteisiin": "Siirtynyt uusiin tutkinnon perusteisiin", - "description:Onko opiskeluoikeuden päättymisen syy siirtyminen...": "Onko opiskeluoikeuden päättymisen syy siirtyminen tutkinnon uusiin perusteisiin." + "description:Onko opiskeluoikeuden päättymisen syy siirtyminen...": "Onko opiskeluoikeuden päättymisen syy siirtyminen tutkinnon uusiin perusteisiin.", + "IB-oppiaine": "IB-oppiaine", + "Lukion oppiaine": "Lukion oppiaine" } diff --git a/web/app/components-v2/controls/Select.tsx b/web/app/components-v2/controls/Select.tsx index 4b5e1f1f5d..adb0f0b143 100644 --- a/web/app/components-v2/controls/Select.tsx +++ b/web/app/components-v2/controls/Select.tsx @@ -427,6 +427,15 @@ const useSelectState = (props: SelectProps) => { // Exported utils +export const regroupKoodisto = ( + koodit: KoodistokoodiviiteKoodistonNimellä[], + getGroup: (k: KoodistokoodiviiteKoodistonNimellä) => string | null +) => + koodit.flatMap((k) => { + const group = getGroup(k) + return group === null ? [] : [{ ...k, koodistoNimi: group }] + }) + export const groupKoodistoToOptions = ( koodit: KoodistokoodiviiteKoodistonNimellä[], ords?: Array>, @@ -470,6 +479,17 @@ export const SelectOptionOrd = Ord.contramap((o: SelectOption) => o.label)( export const sortOptions = (options: Array>) => A.sort(SelectOptionOrd)(options) +export const mapOptions = + (f: (o: SelectOption) => SelectOption) => + (options: Array>): Array> => + options.map((o) => ({ + ...f(o), + children: o.children && mapOptions(f)(o.children) + })) + +export const mapOptionLabels = (f: (o: SelectOption) => string) => + mapOptions((o: SelectOption) => ({ ...o, label: f(o) })) + // Internal utils const selectOption = ( diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index 44debe8316..f77713d1dd 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -39,6 +39,7 @@ import { IBPäätasonSuoritusTiedot } from './IBPaatasonSuoritusTiedot' import { + preIB2015Oppiainekategoriat, useAineryhmäOptions, useKielivalikoimaOptions, useMatematiikanOppimääräOptions, @@ -46,6 +47,7 @@ import { useÄidinkielenKieliOptions } from './state/options' import { useUusiPreIB2015OppiaineState } from './state/preIBOppiaine' +import { useKoodistoFiller } from '../appstate/koodisto' export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps @@ -77,16 +79,17 @@ const IBPäätasonSuoritusEditor: React.FC< ) const [addOppiaineVisible, showAddOppiaineDialog, hideAddOppiaineDialog] = useBooleanState(false) + const fillKoodistot = useKoodistoFiller() const addOppiaine = useCallback( - (oppiaine: PreIBSuorituksenOsasuoritus2015) => { + async (oppiaine: PreIBSuorituksenOsasuoritus2015) => { form.updateAt( päätasonSuoritus.path.prop('osasuoritukset') as any, - appendOptional(oppiaine) + appendOptional(await fillKoodistot(oppiaine)) ) hideAddOppiaineDialog() }, - [form, hideAddOppiaineDialog, päätasonSuoritus.path] + [fillKoodistot, form, hideAddOppiaineDialog, päätasonSuoritus.path] ) return ( @@ -167,7 +170,7 @@ const UusiPreIB2015OppiaineDialog: React.FC< UusiPreIB2015OppiaineDialogProps > = (props) => { const state = useUusiPreIB2015OppiaineState() - const tunnisteet = usePreIBTunnisteOptions() + const tunnisteet = usePreIBTunnisteOptions(preIB2015Oppiainekategoriat) const kielet = useKielivalikoimaOptions(state.kieli.visible) const matematiikanOppimäärät = useMatematiikanOppimääräOptions( state.matematiikanOppimäärä.visible diff --git a/web/app/ib/state/options.ts b/web/app/ib/state/options.ts index baee601020..e1a979746e 100644 --- a/web/app/ib/state/options.ts +++ b/web/app/ib/state/options.ts @@ -1,26 +1,84 @@ +import * as A from 'fp-ts/Array' +import { pipe } from 'fp-ts/lib/function' +import * as O from 'fp-ts/Option' import { useMemo } from 'react' -import { useKoodisto, useKoodistot } from '../../appstate/koodisto' +import { + KoodistokoodiviiteKoodistonNimellä, + useKoodisto, + useKoodistot +} from '../../appstate/koodisto' import { groupKoodistoToOptions, + mapOptionLabels, + regroupKoodisto, SelectOption } from '../../components-v2/controls/Select' +import { t } from '../../i18n/i18n' +import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' +import { entries } from '../../util/objects' +import { + isIBOppiaineLanguageTunniste, + isIBOppiaineMuuTunniste, + isLukionMatematiikka2015Tunniste, + isLukionMuuValtakunnallinenOppiaine2015Tunniste, + isLukionUskonto2015Tunniste, + isLukionÄidinkieliJaKirjallisuus2015Tunniste, + isVierasTaiToinenKotimainenKieli2015Tunniste +} from '../oppiaineet/tunnisteet' import { PreIBOppiaineTunniste, PreIBOppiaineTunnisteKoodistoUri } from './preIBOppiaine' -import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' +import { KoodistoUriOf } from '../../util/koodisto' -export const usePreIBTunnisteOptions = (): - | SelectOption[] - | null => { - const tunnisteet = useKoodistot( +export const preIB2015Oppiainekategoriat = { + 'IB-oppiaine': [isIBOppiaineLanguageTunniste, isIBOppiaineMuuTunniste], + 'Lukion oppiaine': [ + isLukionMatematiikka2015Tunniste, + isLukionMuuValtakunnallinenOppiaine2015Tunniste, + isLukionUskonto2015Tunniste, + isLukionÄidinkieliJaKirjallisuus2015Tunniste, + isVierasTaiToinenKotimainenKieli2015Tunniste + ] +} + +const oppiaineCategoryResolver = + ( + types: Record boolean>> + ) => + (k: K) => + pipe( + types, + entries, + A.findFirst(([_, guards]) => guards.some((g) => g(k))), + O.map(([name, _]) => t(name)), + O.toNullable + ) + +export const usePreIBTunnisteOptions = ( + kategoriat: Record boolean>> +): SelectOption[] | null => { + const tunnisteet = useKoodistot>( 'oppiaineetib', 'koskioppiaineetyleissivistava' ) - return useMemo( - () => tunnisteet && groupKoodistoToOptions(tunnisteet), - [tunnisteet] - ) + return useMemo(() => { + if (tunnisteet) { + const getCategory = oppiaineCategoryResolver(kategoriat) + return pipe( + regroupKoodisto(tunnisteet, (tunniste) => + getCategory(tunniste.koodiviite as K) + ), + // @ts-expect-error + groupKoodistoToOptions, + mapOptionLabels((k) => + k.value?.koodiarvo ? `${k.label} (${k.value.koodiarvo})` : k.label + ) + ) + } else { + return null + } + }, [kategoriat, tunnisteet]) } const optionLoader = diff --git a/web/app/util/objects.ts b/web/app/util/objects.ts index 25701a24f3..50db2299dd 100644 --- a/web/app/util/objects.ts +++ b/web/app/util/objects.ts @@ -1,5 +1,8 @@ export type EmptyObject = { _?: never } +export const entries = (obj: Record): Array<[string, T]> => + Object.entries(obj) + export const withoutNullValues = (obj: T): Partial => Object.fromEntries( Object.entries(obj).filter( From 51ecaae73edf6028601370872bc2d44a76840f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Thu, 5 Dec 2024 15:16:13 +0200 Subject: [PATCH 12/26] =?UTF-8?q?Paikallisen=20koulutuksen=20lis=C3=A4?= =?UTF-8?q?=C3=A4minen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components-v2/createdialog/DialogField.ts | 3 +- .../PaikallinenKoulutusFields.tsx | 67 +++++++++++++++++++ web/app/ib/IBEditor.tsx | 44 ++++++++++-- web/app/ib/state/options.ts | 44 ++++++------ web/app/ib/state/preIBOppiaine.ts | 9 ++- .../AmmatillinenKoulutusFields.tsx | 67 ++----------------- 6 files changed, 139 insertions(+), 95 deletions(-) create mode 100644 web/app/components-v2/opiskeluoikeus/PaikallinenKoulutusFields.tsx diff --git a/web/app/components-v2/createdialog/DialogField.ts b/web/app/components-v2/createdialog/DialogField.ts index ae773c595b..77379a4ec6 100644 --- a/web/app/components-v2/createdialog/DialogField.ts +++ b/web/app/components-v2/createdialog/DialogField.ts @@ -4,6 +4,7 @@ export type DialogField = { value?: T set: (t?: T) => void visible: boolean + setVisible: (visible: boolean) => void } export const useDialogField = ( @@ -21,5 +22,5 @@ export const useDialogField = ( // eslint-disable-next-line react-hooks/exhaustive-deps }, [isVisible]) - return { value, set, visible } + return { value, set, visible, setVisible } } diff --git a/web/app/components-v2/opiskeluoikeus/PaikallinenKoulutusFields.tsx b/web/app/components-v2/opiskeluoikeus/PaikallinenKoulutusFields.tsx new file mode 100644 index 0000000000..8ddd055b6f --- /dev/null +++ b/web/app/components-v2/opiskeluoikeus/PaikallinenKoulutusFields.tsx @@ -0,0 +1,67 @@ +import React, { useState } from 'react' +import { TestIdLayer } from '../../appstate/useTestId' +import { t } from '../../i18n/i18n' +import { TextEdit } from '../controls/TextField' + +export type PaikallinenKoulutusFieldsProps = { + onChange: (values?: PaikallinenKoulutus) => void +} + +export type PaikallinenKoulutus = { + nimi: string + koodiarvo: string + kuvaus: string +} + +const emptyPaikallinenKoulutus: PaikallinenKoulutus = { + nimi: '', + koodiarvo: '', + kuvaus: '' +} + +export const PaikallinenKoulutusFields = ( + props: PaikallinenKoulutusFieldsProps +) => { + const [koulutus, setKoulutus] = useState( + emptyPaikallinenKoulutus + ) + + const update = (field: keyof PaikallinenKoulutus) => (value?: string) => { + const patched: PaikallinenKoulutus = { ...koulutus, [field]: value } + setKoulutus(patched) + props.onChange( + patched.nimi && patched.koodiarvo && patched.kuvaus ? patched : undefined + ) + } + + return ( + +
+ + + +
+
+ ) +} diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index f77713d1dd..2679c7df8e 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -1,6 +1,7 @@ import { isEmpty } from 'fp-ts/lib/Array' import React, { useCallback } from 'react' import { useSchema } from '../appstate/constraints' +import { useKoodistoFiller } from '../appstate/koodisto' import { EditorContainer, usePäätasonSuoritus @@ -13,17 +14,23 @@ import { } from '../components-v2/containers/Modal' import { FlatButton } from '../components-v2/controls/FlatButton' import { RaisedButton } from '../components-v2/controls/RaisedButton' +import { SelectOption } from '../components-v2/controls/Select' import { FormModel, useForm } from '../components-v2/forms/FormModel' import { AdaptedOpiskeluoikeusEditorProps } from '../components-v2/interoperability/useUiAdapter' import { Spacer } from '../components-v2/layout/Spacer' import { OpiskeluoikeusTitle } from '../components-v2/opiskeluoikeus/OpiskeluoikeusTitle' import { OppiaineTable } from '../components-v2/opiskeluoikeus/OppiaineTable' +import { + PaikallinenKoulutus, + PaikallinenKoulutusFields +} from '../components-v2/opiskeluoikeus/PaikallinenKoulutusFields' import { SuorituksenVahvistusField } from '../components-v2/opiskeluoikeus/SuorituksenVahvistus' -import { t } from '../i18n/i18n' +import { localize, t } from '../i18n/i18n' import { IBOpiskeluoikeus } from '../types/fi/oph/koski/schema/IBOpiskeluoikeus' import { IBTutkinto } from '../types/fi/oph/koski/schema/IBTutkinto' import { isLaajuusKursseissa } from '../types/fi/oph/koski/schema/LaajuusKursseissa' import { LukionOpiskeluoikeusjakso } from '../types/fi/oph/koski/schema/LukionOpiskeluoikeusjakso' +import { PaikallinenKoodi } from '../types/fi/oph/koski/schema/PaikallinenKoodi' import { PreIBKoulutusmoduuli2015 } from '../types/fi/oph/koski/schema/PreIBKoulutusmoduuli2015' import { PreIBKoulutusmoduuli2019 } from '../types/fi/oph/koski/schema/PreIBKoulutusmoduuli2019' import { PreIBSuorituksenOsasuoritus2015 } from '../types/fi/oph/koski/schema/PreIBSuorituksenOsasuoritus2015' @@ -39,6 +46,7 @@ import { IBPäätasonSuoritusTiedot } from './IBPaatasonSuoritusTiedot' import { + PaikallinenKey, preIB2015Oppiainekategoriat, useAineryhmäOptions, useKielivalikoimaOptions, @@ -46,8 +54,10 @@ import { usePreIBTunnisteOptions, useÄidinkielenKieliOptions } from './state/options' -import { useUusiPreIB2015OppiaineState } from './state/preIBOppiaine' -import { useKoodistoFiller } from '../appstate/koodisto' +import { + PreIBOppiaineTunniste, + useUusiPreIB2015OppiaineState +} from './state/preIBOppiaine' export type IBEditorProps = AdaptedOpiskeluoikeusEditorProps @@ -180,6 +190,29 @@ const UusiPreIB2015OppiaineDialog: React.FC< state.äidinkielenKieli.visible ) + const onTunniste = useCallback( + (option?: SelectOption) => { + state.tunniste.set(option?.value) + state.paikallinenTunniste.setVisible(option?.key === PaikallinenKey) + }, + [state.paikallinenTunniste, state.tunniste] + ) + + const onPaikallinenKoulutus = useCallback( + (paikallinen?: PaikallinenKoulutus) => { + if (paikallinen) { + state.paikallinenTunniste.set( + PaikallinenKoodi({ + koodiarvo: paikallinen.koodiarvo, + nimi: localize(paikallinen.nimi) + }) + ) + state.paikallinenKuvaus.set(localize(paikallinen.kuvaus)) + } + }, + [state.paikallinenKuvaus, state.paikallinenTunniste] + ) + const onSubmit = useCallback(() => { if (state.result) { props.onSubmit(state.result) @@ -196,7 +229,7 @@ const UusiPreIB2015OppiaineDialog: React.FC< state.tunniste.set(o?.value)} + onChange={onTunniste} testId="tunniste" /> @@ -251,6 +284,9 @@ const UusiPreIB2015OppiaineDialog: React.FC< /> )} + {state.paikallinenTunniste && ( + + )} diff --git a/web/app/ib/state/options.ts b/web/app/ib/state/options.ts index e1a979746e..a597a00aaa 100644 --- a/web/app/ib/state/options.ts +++ b/web/app/ib/state/options.ts @@ -2,11 +2,7 @@ import * as A from 'fp-ts/Array' import { pipe } from 'fp-ts/lib/function' import * as O from 'fp-ts/Option' import { useMemo } from 'react' -import { - KoodistokoodiviiteKoodistonNimellä, - useKoodisto, - useKoodistot -} from '../../appstate/koodisto' +import { useKoodisto, useKoodistot } from '../../appstate/koodisto' import { groupKoodistoToOptions, mapOptionLabels, @@ -15,6 +11,7 @@ import { } from '../../components-v2/controls/Select' import { t } from '../../i18n/i18n' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' +import { KoodistoUriOf } from '../../util/koodisto' import { entries } from '../../util/objects' import { isIBOppiaineLanguageTunniste, @@ -25,11 +22,7 @@ import { isLukionÄidinkieliJaKirjallisuus2015Tunniste, isVierasTaiToinenKotimainenKieli2015Tunniste } from '../oppiaineet/tunnisteet' -import { - PreIBOppiaineTunniste, - PreIBOppiaineTunnisteKoodistoUri -} from './preIBOppiaine' -import { KoodistoUriOf } from '../../util/koodisto' +import { PreIBOppiaineTunniste } from './preIBOppiaine' export const preIB2015Oppiainekategoriat = { 'IB-oppiaine': [isIBOppiaineLanguageTunniste, isIBOppiaineMuuTunniste], @@ -42,6 +35,8 @@ export const preIB2015Oppiainekategoriat = { ] } +export const PaikallinenKey = '__paikallinen__' + const oppiaineCategoryResolver = ( types: Record boolean>> @@ -63,21 +58,20 @@ export const usePreIBTunnisteOptions = ( 'koskioppiaineetyleissivistava' ) return useMemo(() => { - if (tunnisteet) { - const getCategory = oppiaineCategoryResolver(kategoriat) - return pipe( - regroupKoodisto(tunnisteet, (tunniste) => - getCategory(tunniste.koodiviite as K) - ), - // @ts-expect-error - groupKoodistoToOptions, - mapOptionLabels((k) => - k.value?.koodiarvo ? `${k.label} (${k.value.koodiarvo})` : k.label - ) - ) - } else { - return null - } + const getCategory = oppiaineCategoryResolver(kategoriat) + return pipe( + regroupKoodisto(tunnisteet || [], (tunniste) => + getCategory(tunniste.koodiviite as K) + ), + groupKoodistoToOptions, + mapOptionLabels((k) => + k.value?.koodiarvo ? `${k.label} (${k.value.koodiarvo})` : k.label + ), + A.prepend({ + key: PaikallinenKey, + label: t('Paikallinen') + }) + ) }, [kategoriat, tunnisteet]) } diff --git a/web/app/ib/state/preIBOppiaine.ts b/web/app/ib/state/preIBOppiaine.ts index 1c7d21fdf1..baec0dce88 100644 --- a/web/app/ib/state/preIBOppiaine.ts +++ b/web/app/ib/state/preIBOppiaine.ts @@ -4,6 +4,7 @@ import { DialogField, useDialogField } from '../../components-v2/createdialog/DialogField' +import { localize } from '../../i18n/i18n' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' import { LocalizedString } from '../../types/fi/oph/koski/schema/LocalizedString' import { PaikallinenKoodi } from '../../types/fi/oph/koski/schema/PaikallinenKoodi' @@ -73,8 +74,12 @@ export const useUusiPreIB2015OppiaineState = Koodistokoodiviite<'oppiaineaidinkielijakirjallisuus'> >(isLukionÄidinkieliJaKirjallisuus2015Tunniste(tunniste.value)) - const paikallinenTunniste = useDialogField(false) - const paikallinenKuvaus = useDialogField(false) + const paikallinenTunniste = useDialogField(false, () => + PaikallinenKoodi({ koodiarvo: '', nimi: localize('') }) + ) + const paikallinenKuvaus = useDialogField(false, () => + localize('') + ) const result = useMemo( () => diff --git a/web/app/uusiopiskeluoikeus/opiskeluoikeusSpecificFields/AmmatillinenKoulutusFields.tsx b/web/app/uusiopiskeluoikeus/opiskeluoikeusSpecificFields/AmmatillinenKoulutusFields.tsx index 9478a807c4..a7f93a5f55 100644 --- a/web/app/uusiopiskeluoikeus/opiskeluoikeusSpecificFields/AmmatillinenKoulutusFields.tsx +++ b/web/app/uusiopiskeluoikeus/opiskeluoikeusSpecificFields/AmmatillinenKoulutusFields.tsx @@ -2,12 +2,14 @@ import React, { useCallback, useMemo, useState } from 'react' import { SuoritusFieldsProps } from '.' import { isSuccess, useApiWithParams } from '../../api-fetch' import { useKoodisto } from '../../appstate/koodisto' -import { TestIdLayer } from '../../appstate/useTestId' import { SelectOption, groupKoodistoToOptions } from '../../components-v2/controls/Select' -import { TextEdit } from '../../components-v2/controls/TextField' +import { + PaikallinenKoulutus, + PaikallinenKoulutusFields +} from '../../components-v2/opiskeluoikeus/PaikallinenKoulutusFields' import { t } from '../../i18n/i18n' import { isAmmatilliseenTehtäväänValmistavaKoulutus } from '../../types/fi/oph/koski/schema/AmmatilliseenTehtavaanValmistavaKoulutus' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' @@ -204,67 +206,6 @@ const MuuAmmatillinenKoulutusFields = (props: SuoritusFieldsProps) => { ) } -type PaikallinenKoulutusFieldsProps = { - onChange: (values?: PaikallinenKoulutus) => void -} - -type PaikallinenKoulutus = { - nimi: string - koodiarvo: string - kuvaus: string -} - -const emptyPaikallinenKoulutus: PaikallinenKoulutus = { - nimi: '', - koodiarvo: '', - kuvaus: '' -} - -const PaikallinenKoulutusFields = (props: PaikallinenKoulutusFieldsProps) => { - const [koulutus, setKoulutus] = useState( - emptyPaikallinenKoulutus - ) - - const update = (field: keyof PaikallinenKoulutus) => (value?: string) => { - const patched: PaikallinenKoulutus = { ...koulutus, [field]: value } - setKoulutus(patched) - props.onChange( - patched.nimi && patched.koodiarvo && patched.kuvaus ? patched : undefined - ) - } - - return ( - -
- - - -
-
- ) -} - type MuuAmmatillinenKoulutusmoduuliKey = | 'paikallinen' | 'ammatilliseentehtavaanvalmistavakoulutus' From 1178bf896990e2260a63610c0b8ea75842b67df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Thu, 5 Dec 2024 15:33:29 +0200 Subject: [PATCH 13/26] =?UTF-8?q?=C3=84l=C3=A4=20n=C3=A4yt=C3=A4=20jo=20va?= =?UTF-8?q?littuja=20oppiaineita=20lis=C3=A4ysvalikossa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/app/components-v2/controls/Select.tsx | 12 ++++++++++-- web/app/ib/IBEditor.tsx | 9 +++++++-- web/app/ib/state/options.ts | 24 ++++++++++++++++++++--- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/web/app/components-v2/controls/Select.tsx b/web/app/components-v2/controls/Select.tsx index adb0f0b143..6ba783057b 100644 --- a/web/app/components-v2/controls/Select.tsx +++ b/web/app/components-v2/controls/Select.tsx @@ -317,7 +317,7 @@ const useSelectState = (props: SelectProps) => { const opts = filter === '' || filter === null ? props.options - : filterOptions(props.options, filter) + : queryOptions(props.options, filter) // Remove one level of grouping if only one group is present return opts.length === 1 && opts[0].isGroup ? opts[0].children || [] : opts }, [filter, props.options]) @@ -490,6 +490,14 @@ export const mapOptions = export const mapOptionLabels = (f: (o: SelectOption) => string) => mapOptions((o: SelectOption) => ({ ...o, label: f(o) })) +export const filterOptions = + (f: (o: SelectOption) => boolean) => + (options: Array>): Array> => + options.flatMap((o) => { + const children = o.children && filterOptions(f)(o.children) + return (children?.length || 0) > 0 || f(o) ? [{ ...o, children }] : [] + }) + // Internal utils const selectOption = ( @@ -514,7 +522,7 @@ const flattenOptions = (options: OptionList): FlatOptionList => { return { arr: options.flatMap(flatten) } } -const filterOptions = ( +const queryOptions = ( options: OptionList, query: string ): OptionList => { diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index 2679c7df8e..2c7bb8185a 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -147,6 +147,7 @@ const IBPäätasonSuoritusEditor: React.FC< match(päätasonSuoritus.suoritus.koulutusmoduuli) .isClass(PreIBKoulutusmoduuli2015, () => ( @@ -172,6 +173,7 @@ const useSuoritetutKurssitYhteensä = ( } type UusiPreIB2015OppiaineDialogProps = { + päätasonSuoritus: PäätasonSuoritusOf onClose: () => void onSubmit: (oppiaine: PreIBSuorituksenOsasuoritus2015) => void } @@ -180,7 +182,10 @@ const UusiPreIB2015OppiaineDialog: React.FC< UusiPreIB2015OppiaineDialogProps > = (props) => { const state = useUusiPreIB2015OppiaineState() - const tunnisteet = usePreIBTunnisteOptions(preIB2015Oppiainekategoriat) + const tunnisteet = usePreIBTunnisteOptions( + preIB2015Oppiainekategoriat, + props.päätasonSuoritus + ) const kielet = useKielivalikoimaOptions(state.kieli.visible) const matematiikanOppimäärät = useMatematiikanOppimääräOptions( state.matematiikanOppimäärä.visible @@ -284,7 +289,7 @@ const UusiPreIB2015OppiaineDialog: React.FC< /> )} - {state.paikallinenTunniste && ( + {state.paikallinenTunniste.visible && ( )} diff --git a/web/app/ib/state/options.ts b/web/app/ib/state/options.ts index a597a00aaa..9df7aaf091 100644 --- a/web/app/ib/state/options.ts +++ b/web/app/ib/state/options.ts @@ -4,15 +4,18 @@ import * as O from 'fp-ts/Option' import { useMemo } from 'react' import { useKoodisto, useKoodistot } from '../../appstate/koodisto' import { + filterOptions, groupKoodistoToOptions, mapOptionLabels, regroupKoodisto, SelectOption } from '../../components-v2/controls/Select' import { t } from '../../i18n/i18n' +import { IBOpiskeluoikeus } from '../../types/fi/oph/koski/schema/IBOpiskeluoikeus' import { Koodistokoodiviite } from '../../types/fi/oph/koski/schema/Koodistokoodiviite' import { KoodistoUriOf } from '../../util/koodisto' import { entries } from '../../util/objects' +import { PäätasonSuoritusOf } from '../../util/opiskeluoikeus' import { isIBOppiaineLanguageTunniste, isIBOppiaineMuuTunniste, @@ -51,19 +54,34 @@ const oppiaineCategoryResolver = ) export const usePreIBTunnisteOptions = ( - kategoriat: Record boolean>> + categories: Record boolean>>, + päätasonSuoritus: PäätasonSuoritusOf ): SelectOption[] | null => { const tunnisteet = useKoodistot>( 'oppiaineetib', 'koskioppiaineetyleissivistava' ) + + const existingOppiaineet = useMemo( + () => + (päätasonSuoritus.osasuoritukset || []).map( + (os) => os.koulutusmoduuli.tunniste.koodiarvo + ), + [päätasonSuoritus.osasuoritukset] + ) + return useMemo(() => { - const getCategory = oppiaineCategoryResolver(kategoriat) + const getCategory = oppiaineCategoryResolver(categories) return pipe( regroupKoodisto(tunnisteet || [], (tunniste) => getCategory(tunniste.koodiviite as K) ), groupKoodistoToOptions, + filterOptions( + (k) => + k.value?.koodiarvo === undefined || + !existingOppiaineet.includes(k.value?.koodiarvo) + ), mapOptionLabels((k) => k.value?.koodiarvo ? `${k.label} (${k.value.koodiarvo})` : k.label ), @@ -72,7 +90,7 @@ export const usePreIBTunnisteOptions = ( label: t('Paikallinen') }) ) - }, [kategoriat, tunnisteet]) + }, [categories, existingOppiaineet, tunnisteet]) } const optionLoader = From 93c3aadc3c853a6bd8d68b3fad30dae15110042e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20H=C3=A4nninen?= Date: Mon, 9 Dec 2024 08:43:48 +0200 Subject: [PATCH 14/26] Oppiaineen poisto --- .../opiskeluoikeus/OppiaineTable.less | 3 ++ .../opiskeluoikeus/OppiaineTable.tsx | 41 +++++++++++++++++-- web/app/ib/IBEditor.tsx | 18 +++++++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.less b/web/app/components-v2/opiskeluoikeus/OppiaineTable.less index a448c27f54..81fcb2825f 100644 --- a/web/app/components-v2/opiskeluoikeus/OppiaineTable.less +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.less @@ -56,6 +56,9 @@ &__arvosana { text-align: right; } + &__poisto { + background: none; + } } } } diff --git a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx index 550539c55d..6ad814c33a 100644 --- a/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx +++ b/web/app/components-v2/opiskeluoikeus/OppiaineTable.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useRef, useState } from 'react' +import React, { useCallback, useEffect, useRef, useState } from 'react' import { ISO2FinnishDate } from '../../date/date' import { t } from '../../i18n/i18n' import { Arviointi } from '../../types/fi/oph/koski/schema/Arviointi' +import { IBOpiskeluoikeus } from '../../types/fi/oph/koski/schema/IBOpiskeluoikeus' import { IBPäätasonSuoritus } from '../../types/fi/oph/koski/schema/IBPaatasonSuoritus' import { isMuidenLukioOpintojenPreIBSuoritus2019 } from '../../types/fi/oph/koski/schema/MuidenLukioOpintojenPreIBSuoritus2019' import { Suoritus } from '../../types/fi/oph/koski/schema/Suoritus' @@ -14,6 +15,9 @@ import { suoritusValmis } from '../../util/suoritus' import { useBooleanState } from '../../util/useBooleanState' import { notUndefined } from '../../util/util' import { KeyValueRow, KeyValueTable } from '../containers/KeyValueTable' +import { IconButton } from '../controls/IconButton' +import { FormModel } from '../forms/FormModel' +import { CHARCODE_REMOVE } from '../texts/Icon' // Vain OppiaineTablen tukemat päätason suoritukset (tätä komponenttia tullaan myöhemmin käyttämään ainakin lukion näkymille) export type OppiainePäätasonSuoritus = IBPäätasonSuoritus @@ -21,10 +25,16 @@ export type OppiainePäätasonSuoritus = IBPäätasonSuoritus export type OppiaineOsasuoritus = OsasuoritusOf export type OppiaineTableProps = { + form: FormModel suoritus: OppiainePäätasonSuoritus + onDelete: (index: number) => void } -export const OppiaineTable: React.FC = ({ suoritus }) => { +export const OppiaineTable: React.FC = ({ + suoritus, + form, + onDelete +}) => { const oppiaineet = suoritus.osasuoritukset || [] return oppiaineet.length === 0 ? null : ( @@ -35,11 +45,17 @@ export const OppiaineTable: React.FC = ({ suoritus }) => { {t('Oppiaine')} {t('Laajuus (kurssia)')} {t('Arvosana')} + {form.editMode && } {oppiaineet.map((oppiaine, i) => ( - + onDelete(i)} + /> ))} @@ -47,10 +63,16 @@ export const OppiaineTable: React.FC = ({ suoritus }) => { } type OppiaineRowProps = { + form: FormModel oppiaine: OppiaineOsasuoritus + onDelete: () => void } -const OppiaineRow: React.FC = ({ oppiaine }) => { +const OppiaineRow: React.FC = ({ + oppiaine, + form, + onDelete +}) => { const kurssit = oppiaine.osasuoritukset || [] const kurssejaYhteensä = sum( kurssit.map((k) => k.koulutusmoduuli.laajuus?.arvo || 0) @@ -73,6 +95,17 @@ const OppiaineRow: React.FC = ({ oppiaine }) => { {kurssejaYhteensä} {oppiaineenArvosana(oppiaine)} + {form.editMode && ( + + + + )} ) } diff --git a/web/app/ib/IBEditor.tsx b/web/app/ib/IBEditor.tsx index 2c7bb8185a..e25e3c8192 100644 --- a/web/app/ib/IBEditor.tsx +++ b/web/app/ib/IBEditor.tsx @@ -34,7 +34,7 @@ import { PaikallinenKoodi } from '../types/fi/oph/koski/schema/PaikallinenKoodi' import { PreIBKoulutusmoduuli2015 } from '../types/fi/oph/koski/schema/PreIBKoulutusmoduuli2015' import { PreIBKoulutusmoduuli2019 } from '../types/fi/oph/koski/schema/PreIBKoulutusmoduuli2019' import { PreIBSuorituksenOsasuoritus2015 } from '../types/fi/oph/koski/schema/PreIBSuorituksenOsasuoritus2015' -import { appendOptional } from '../util/array' +import { appendOptional, deleteAt } from '../util/array' import { koodiviiteId } from '../util/koodisto' import { sum } from '../util/numbers' import { PäätasonSuoritusOf } from '../util/opiskeluoikeus' @@ -102,6 +102,16 @@ const IBPäätasonSuoritusEditor: React.FC< [fillKoodistot, form, hideAddOppiaineDialog, päätasonSuoritus.path] ) + const deleteOppiaine = useCallback( + (index: number) => { + form.updateAt( + päätasonSuoritus.path.prop('osasuoritukset').optional(), + (ts) => deleteAt(index)(ts as any[]) + ) + }, + [form, päätasonSuoritus.path] + ) + return ( - + {kurssejaYhteensä !== null && (