diff --git a/packages/behandling-ungdomsytelse/src/panelDefinisjoner/prosessStegPaneler/VedtakProsessStegPanelDef.tsx b/packages/behandling-ungdomsytelse/src/panelDefinisjoner/prosessStegPaneler/VedtakProsessStegPanelDef.tsx index 95039c0c76..94397d9bb0 100644 --- a/packages/behandling-ungdomsytelse/src/panelDefinisjoner/prosessStegPaneler/VedtakProsessStegPanelDef.tsx +++ b/packages/behandling-ungdomsytelse/src/panelDefinisjoner/prosessStegPaneler/VedtakProsessStegPanelDef.tsx @@ -1,14 +1,19 @@ import aksjonspunktCodes from '@fpsak-frontend/kodeverk/src/aksjonspunktCodes'; import fagsakYtelseType from '@fpsak-frontend/kodeverk/src/fagsakYtelseType'; -import VedtakProsessIndex from '@fpsak-frontend/prosess-vedtak'; import { ProsessStegDef, ProsessStegPanelDef } from '@k9-sak-web/behandling-felles'; import { prosessStegCodes } from '@k9-sak-web/konstanter'; +import { UngVedtakIndex } from '@k9-sak-web/gui/prosess/ung-vedtak/UngVedtakIndex.js'; +import { konverterKodeverkTilKode } from '@k9-sak-web/lib/kodeverk/konverterKodeverkTilKode.js'; import { UngdomsytelseBehandlingApiKeys } from '../../data/ungdomsytelseBehandlingApi'; import findStatusForVedtak from '../vedtakStatusUtlederUngdomsytelse'; class PanelDef extends ProsessStegPanelDef { - getKomponent = props => ; + getKomponent = props => { + const deepCopyProps = JSON.parse(JSON.stringify(props)); + konverterKodeverkTilKode(deepCopyProps, false); + return ; + }; getAksjonspunktKoder = () => [ aksjonspunktCodes.FORESLA_VEDTAK, diff --git a/packages/v2/backend/package.json b/packages/v2/backend/package.json index 7adead15b9..e6686f99e8 100644 --- a/packages/v2/backend/package.json +++ b/packages/v2/backend/package.json @@ -19,6 +19,6 @@ "dependencies": { "@navikt/k9-klage-typescript-client": "1.0.20240513162434-997f3da", "@navikt/k9-sak-typescript-client": "1.0.20241028110915", - "@navikt/ung-sak-typescript-client": "0.1.20241112140012" + "@navikt/ung-sak-typescript-client": "0.1.20250107115905" } } diff --git a/packages/v2/gui/src/app/UngSakClientContext.tsx b/packages/v2/gui/src/app/UngSakClientContext.tsx index 8e51e02dc2..8f5d4d9b7a 100644 --- a/packages/v2/gui/src/app/UngSakClientContext.tsx +++ b/packages/v2/gui/src/app/UngSakClientContext.tsx @@ -5,11 +5,19 @@ import type { ApiRequestOptions } from '@k9-sak-web/backend/ungsak/generated'; import { UngSakClient } from '@k9-sak-web/backend/ungsak/generated'; import { createContext } from 'react'; +// Current client generator hardcode accept: application/json into every request, which causes requests for binary content +// (pdf) to be denied by the server. To work around this until client generator works properly, we override the accept +// header manually for the few requests that don't serve json. +// TODO Remove when generated typescript client can set correct Accept header by itself. +const acceptHeaderOverrideWorkaround = (options: ApiRequestOptions>): Record => + options.url === '/formidling/vedtaksbrev/forhaandsvis' ? { Accept: 'application/pdf' } : {}; + const headerResolver = async (options: ApiRequestOptions>): Promise> => { const { headerName, headerValue } = generateNavCallidHeader(); const { xJsonSerializerOptionHeader, xJsonSerializerOptionValue } = jsonSerializerOption; return { ...options.headers, + ...acceptHeaderOverrideWorkaround(options), [headerName]: headerValue, // Legg til nav call id header [xJsonSerializerOptionHeader]: xJsonSerializerOptionValue, // Legg til X-Json-Serializer-Option header }; diff --git "a/packages/v2/gui/src/prosess/ung-vedtak/Avslags\303\245rsakListe.tsx" "b/packages/v2/gui/src/prosess/ung-vedtak/Avslags\303\245rsakListe.tsx" new file mode 100644 index 0000000000..f867b2f5fb --- /dev/null +++ "b/packages/v2/gui/src/prosess/ung-vedtak/Avslags\303\245rsakListe.tsx" @@ -0,0 +1,55 @@ +import { vilkårStatus } from '@k9-sak-web/backend/k9sak/kodeverk/behandling/VilkårStatus.js'; +import { useKodeverkContext } from '@k9-sak-web/gui/kodeverk/index.js'; +import { KodeverkType } from '@k9-sak-web/lib/kodeverk/types.js'; +import { BodyShort } from '@navikt/ds-react'; +import type { UngVedtakVilkårDto, UngVedtakVilkårPeriodeDto } from './UngVedtakVilkårDto'; + +const finnUnikeAvslagskoder = (avslåttePerioder: UngVedtakVilkårPeriodeDto[] = []) => { + const funnedeAvslagskoder = new Set(); + const unikeAvslagskoder = avslåttePerioder.filter(el => { + const erDuplikat = funnedeAvslagskoder.has(el.avslagKode); + funnedeAvslagskoder.add(el.avslagKode); + return !erDuplikat; + }); + return unikeAvslagskoder; +}; + +interface AvslagsårsakListeProps { + vilkår: UngVedtakVilkårDto[]; +} + +const AvslagsårsakListe = ({ vilkår }: AvslagsårsakListeProps) => { + const { kodeverkNavnFraKode, kodeverkNavnFraUndertypeKode } = useKodeverkContext(); + + const visAvslåtteVilkårsperioder = (avslåttVilkår: UngVedtakVilkårDto) => { + const avslåttePerioder = avslåttVilkår?.perioder?.filter( + periode => periode.vilkarStatus === vilkårStatus.IKKE_OPPFYLT, + ); + const avslåttePerioderMedUnikeAvslagskoder = finnUnikeAvslagskoder(avslåttePerioder); + + return avslåttePerioderMedUnikeAvslagskoder.map(avslåttPeriode => ( + + {[ + kodeverkNavnFraKode(avslåttVilkår.vilkarType, KodeverkType.VILKAR_TYPE), + ': ', + kodeverkNavnFraUndertypeKode( + avslåttVilkår.vilkarType, + avslåttPeriode.avslagKode || '', + KodeverkType.AVSLAGSARSAK, + ), + ].join('')} + + )); + }; + + const avslatteVilkar = vilkår.filter( + v => Array.isArray(v.perioder) && v.perioder.some(periode => periode.vilkarStatus === vilkårStatus.IKKE_OPPFYLT), + ); + if (avslatteVilkar.length === 0) { + return Søker har ikke noen gyldig uttaksperiode; + } + + return <>{avslatteVilkar.map(avslåttVilkår => visAvslåtteVilkårsperioder(avslåttVilkår))}>; +}; + +export default AvslagsårsakListe; diff --git a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtak.stories.tsx b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtak.stories.tsx new file mode 100644 index 0000000000..40e80a3d5b --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtak.stories.tsx @@ -0,0 +1,96 @@ +import { behandlingType } from '@k9-sak-web/backend/ungsak/generated'; +import alleKodeverkV2 from '@k9-sak-web/lib/kodeverk/mocks/alleKodeverkV2.json'; +import type { Meta, StoryObj } from '@storybook/react'; +import { KodeverkProvider } from '../../kodeverk'; +import { asyncAction } from '../../storybook/asyncAction'; +import { FakeUngVedtakBackendApi } from '../../storybook/mocks/FakeUngVedtakBackendApi'; +import { UngVedtak } from './UngVedtak'; + +const api = new FakeUngVedtakBackendApi(); +const meta = { + title: 'gui/prosess/ung-vedtak/UngVedtak.tsx', + args: { + aksjonspunkter: [], + api, + submitCallback: asyncAction('button-click'), + }, + component: UngVedtak, + render: props => ( + + + + ), +} satisfies Meta; +export default meta; + +type Story = StoryObj; + +export const InnvilgetStory: Story = { + args: { + behandling: { + behandlingsresultat: { + type: 'INNVILGET', + }, + id: 3000002, + }, + vilkår: [ + { + vilkarType: 'UNG_VK_XXX', + perioder: [ + { + avslagKode: null, + + vilkarStatus: 'OPPFYLT', + }, + ], + }, + { + vilkarType: 'K9_VK_3', + perioder: [ + { + avslagKode: null, + vilkarStatus: 'OPPFYLT', + }, + ], + }, + ], + readOnly: false, + }, +}; + +export const AvslåttStory: Story = { + args: { + vilkår: [ + { + vilkarType: 'UNG_VK_XXX', + perioder: [ + { + avslagKode: null, + vilkarStatus: 'OPPFYLT', + }, + ], + }, + { + vilkarType: 'K9_VK_3', + perioder: [ + { + avslagKode: '1090', + vilkarStatus: 'IKKE_OPPFYLT', + }, + ], + }, + ], + behandling: { + behandlingsresultat: { + type: 'AVSLÅTT', + }, + id: 3000001, + }, + readOnly: false, + }, +}; diff --git a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtak.tsx b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtak.tsx new file mode 100644 index 0000000000..5f7f457ea2 --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtak.tsx @@ -0,0 +1,130 @@ +import { behandlingResultatType, type AksjonspunktDto } from '@k9-sak-web/backend/ungsak/generated'; +import { FileSearchIcon } from '@navikt/aksel-icons'; +import { BodyShort, Box, Button, Fieldset, HStack, Label, VStack } from '@navikt/ds-react'; +import { CheckboxField, Form } from '@navikt/ft-form-hooks'; +import { useQuery } from '@tanstack/react-query'; +import { useForm, useWatch } from 'react-hook-form'; +import AvslagsårsakListe from './AvslagsårsakListe'; +import styles from './ungVedtak.module.css'; +import type { UngVedtakBackendApiType } from './UngVedtakBackendApiType'; +import type { UngVedtakBehandlingDto } from './UngVedtakBehandlingDto'; +import type { UngVedtakVilkårDto } from './UngVedtakVilkårDto'; + +interface UngVedtakProps { + aksjonspunkter: AksjonspunktDto[]; + api: UngVedtakBackendApiType; + behandling: UngVedtakBehandlingDto; + submitCallback: (data: any) => Promise; + vilkår: UngVedtakVilkårDto[]; + readOnly: boolean; +} + +const buildInitialValues = () => ({ + redigerAutomatiskBrev: false, + hindreUtsendingAvBrev: false, +}); + +interface FormData { + redigerAutomatiskBrev: boolean; + hindreUtsendingAvBrev: boolean; +} + +export const UngVedtak = ({ api, behandling, aksjonspunkter, submitCallback, vilkår, readOnly }: UngVedtakProps) => { + const formMethods = useForm({ + defaultValues: buildInitialValues(), + }); + const behandlingErInnvilget = behandling.behandlingsresultat?.type === behandlingResultatType.INNVILGET; + const behandlingErAvslått = behandling.behandlingsresultat?.type === behandlingResultatType.AVSLÅTT; + const harAksjonspunkt = aksjonspunkter.filter(ap => ap.kanLoses).length > 0; + const redigerAutomatiskBrev = useWatch({ control: formMethods.control, name: 'redigerAutomatiskBrev' }); + const hindreUtsendingAvBrev = useWatch({ control: formMethods.control, name: 'hindreUtsendingAvBrev' }); + + const { refetch, isLoading: forhåndsvisningIsLoading } = useQuery({ + queryKey: ['forhandsvisVedtaksbrev', behandling.id], + queryFn: async () => { + const response = await api.forhåndsvisVedtaksbrev(behandling.id); + // Create a URL object from the PDF blob + const fileURL = window.URL.createObjectURL(response); + // Open the PDF in a new tab + window.open(fileURL, '_blank'); + return response; + }, + enabled: false, + }); + + const { data: tilgjengeligeVedtaksbrev, isLoading: tilgjengeligeVedtaksbrevIsLoading } = useQuery({ + queryKey: ['tilgjengeligeVedtaksbrev', behandling.id], + queryFn: async () => { + const response = await api.tilgjengeligeVedtaksbrev(behandling.id); + return response; + }, + }); + + const transformValues = () => aksjonspunkter.filter(ap => ap.kanLoses).map(ap => ({ kode: ap.definisjon })); + const handleSubmit = () => { + submitCallback(transformValues()); + }; + + return ( + + + + + + + Resultat + + + {behandlingErInnvilget ? 'Ungdomsytelse er innvilget' : 'Ungdomsytelse er avslått'} + + + {behandlingErAvslått && ( + + + Årsak til avslag + + + + )} + + refetch()} + size="small" + icon={} + loading={forhåndsvisningIsLoading} + type="button" + disabled={!tilgjengeligeVedtaksbrev?.harBrev || tilgjengeligeVedtaksbrevIsLoading} + > + Forhåndsvis brev + + + {harAksjonspunkt && ( + + + Fatt vedtak + + + )} + + + + + + + + + + + + + ); +}; diff --git a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBackendApiType.ts b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBackendApiType.ts new file mode 100644 index 0000000000..bff5a31c89 --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBackendApiType.ts @@ -0,0 +1,9 @@ +import type { + ForhåndsvisVedtaksbrevResponse, + TilgjengeligeVedtaksbrevResponse, +} from '@k9-sak-web/backend/ungsak/generated'; + +export type UngVedtakBackendApiType = { + forhåndsvisVedtaksbrev(behandlingUuid: number): Promise; + tilgjengeligeVedtaksbrev(behandlingUuid: number): Promise; +}; diff --git a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBackendClient.ts b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBackendClient.ts new file mode 100644 index 0000000000..d68e8b4794 --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBackendClient.ts @@ -0,0 +1,21 @@ +import type { + ForhåndsvisVedtaksbrevResponse, + TilgjengeligeVedtaksbrevResponse, + UngSakClient, +} from '@k9-sak-web/backend/ungsak/generated'; + +export default class UngVedtakBackendClient { + #ungsak: UngSakClient; + + constructor(ungsakClient: UngSakClient) { + this.#ungsak = ungsakClient; + } + + async forhåndsvisVedtaksbrev(behandlingId: number): Promise { + return this.#ungsak.formidling.forhåndsvisVedtaksbrev({ behandlingId }); + } + + async tilgjengeligeVedtaksbrev(behandlingId: number): Promise { + return this.#ungsak.formidling.tilgjengeligeVedtaksbrev(`${behandlingId}`); + } +} diff --git a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBehandlingDto.ts b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBehandlingDto.ts new file mode 100644 index 0000000000..9a01b6a05b --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakBehandlingDto.ts @@ -0,0 +1,10 @@ +import type { BehandlingsresultatDto } from '@k9-sak-web/backend/ungsak/generated'; + +type Behandlingsresultat = { + type: BehandlingsresultatDto['type']; +}; + +export type UngVedtakBehandlingDto = { + behandlingsresultat: Behandlingsresultat; + id: number; +}; diff --git a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakIndex.tsx b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakIndex.tsx new file mode 100644 index 0000000000..b2213fdb93 --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakIndex.tsx @@ -0,0 +1,42 @@ +import type { AksjonspunktDto } from '@k9-sak-web/backend/ungsak/generated'; +import { Heading } from '@navikt/ds-react'; +import { useContext } from 'react'; +import { UngSakClientContext } from '../../app/UngSakClientContext'; +import { UngVedtak } from './UngVedtak'; +import UngVedtakBackendClient from './UngVedtakBackendClient'; +import type { UngVedtakBehandlingDto } from './UngVedtakBehandlingDto'; +import type { UngVedtakVilkårDto } from './UngVedtakVilkårDto'; + +interface UngVedtakIndexProps { + aksjonspunkter: AksjonspunktDto[]; + behandling: UngVedtakBehandlingDto; + submitCallback: (data: any) => Promise; + vilkar: UngVedtakVilkårDto[]; + isReadOnly: boolean; +} + +export const UngVedtakIndex = ({ + aksjonspunkter, + behandling, + submitCallback, + vilkar, + isReadOnly, +}: UngVedtakIndexProps) => { + const ungSakClient = useContext(UngSakClientContext); + const ungVedtakBackendClient = new UngVedtakBackendClient(ungSakClient); + return ( + <> + + Vedtak + + + > + ); +}; diff --git "a/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakVilk\303\245rDto.ts" "b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakVilk\303\245rDto.ts" new file mode 100644 index 0000000000..21709ea423 --- /dev/null +++ "b/packages/v2/gui/src/prosess/ung-vedtak/UngVedtakVilk\303\245rDto.ts" @@ -0,0 +1,11 @@ +import type { VilkårMedPerioderDto, VilkårPeriodeDto } from '@k9-sak-web/backend/ungsak/generated'; + +export type UngVedtakVilkårPeriodeDto = { + avslagKode: VilkårPeriodeDto['avslagKode']; + vilkarStatus: VilkårPeriodeDto['vilkarStatus']; +}; + +export type UngVedtakVilkårDto = { + vilkarType: VilkårMedPerioderDto['vilkarType']; + perioder: UngVedtakVilkårPeriodeDto[]; +}; diff --git a/packages/v2/gui/src/prosess/ung-vedtak/ungVedtak.module.css b/packages/v2/gui/src/prosess/ung-vedtak/ungVedtak.module.css new file mode 100644 index 0000000000..552026773d --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/ungVedtak.module.css @@ -0,0 +1,9 @@ +.brevCheckboxContainer { + align-self: flex-start; + border: none; + background: var(--a-blue-50); + border-radius: 4px; + padding: 1.4375rem 1.75rem 0.9375rem; + order: 1; + margin-right: 30px; +} diff --git a/packages/v2/gui/src/prosess/ung-vedtak/ungVedtak.module.d.css.ts b/packages/v2/gui/src/prosess/ung-vedtak/ungVedtak.module.d.css.ts new file mode 100644 index 0000000000..57167290bf --- /dev/null +++ b/packages/v2/gui/src/prosess/ung-vedtak/ungVedtak.module.d.css.ts @@ -0,0 +1,5 @@ +declare const styles: { + readonly "brevCheckboxContainer": string; +}; +export = styles; + diff --git a/packages/v2/gui/src/storybook/mocks/FakeUngVedtakBackendApi.ts b/packages/v2/gui/src/storybook/mocks/FakeUngVedtakBackendApi.ts new file mode 100644 index 0000000000..2a78aaac7d --- /dev/null +++ b/packages/v2/gui/src/storybook/mocks/FakeUngVedtakBackendApi.ts @@ -0,0 +1,16 @@ +import type { + ForhåndsvisVedtaksbrevResponse, + TilgjengeligeVedtaksbrevResponse, +} from '@k9-sak-web/backend/ungsak/generated'; + +import { fakePdf } from './fakePdf.js'; + +export class FakeUngVedtakBackendApi { + async forhåndsvisVedtaksbrev(): Promise { + return fakePdf(); + } + + async tilgjengeligeVedtaksbrev(): Promise { + return { harBrev: true }; + } +} diff --git a/yarn.lock b/yarn.lock index 8c14865246..a57b7f4e46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3935,7 +3935,7 @@ __metadata: dependencies: "@navikt/k9-klage-typescript-client": 1.0.20240513162434-997f3da "@navikt/k9-sak-typescript-client": 1.0.20241028110915 - "@navikt/ung-sak-typescript-client": 0.1.20241112140012 + "@navikt/ung-sak-typescript-client": 0.1.20250107115905 languageName: unknown linkType: soft @@ -5451,10 +5451,10 @@ __metadata: languageName: node linkType: hard -"@navikt/ung-sak-typescript-client@npm:0.1.20241112140012": - version: 0.1.20241112140012 - resolution: "@navikt/ung-sak-typescript-client@npm:0.1.20241112140012::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40navikt%2Fung-sak-typescript-client%2F0.1.20241112140012%2F9904f965a85dcdc25da238ec8d0a423c76e60a7d" - checksum: 89b5c29e8f7a874b84d6bc2ed968200b4deafa7b833679786c5e1a3bb397251102999abeea4679972efc4b8ded3f6e93152aae86e62900e7083cd73bfe86333c +"@navikt/ung-sak-typescript-client@npm:0.1.20250107115905": + version: 0.1.20250107115905 + resolution: "@navikt/ung-sak-typescript-client@npm:0.1.20250107115905::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40navikt%2Fung-sak-typescript-client%2F0.1.20250107115905%2Ff92c68bbec34262e8fc0f7cc28de2bc427babed9" + checksum: c08d53d08dd2626f9475f9225bf68949949c5534ef5006d76767fb306cece3eecbe7dffe0cdd3014e84627981288f9f66d8df25d9029e357538a19675ffb2276 languageName: node linkType: hard