From 5c1be7db9fcbb6980a7e6afbe79783596b400b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindre=20S=C3=A6grov?= Date: Tue, 20 Feb 2024 14:57:43 +0100 Subject: [PATCH] Legg til filtrering for oppgaver --- .../oppgave-filter/OppgaveFilter.module.css | 9 ++ .../oppgave-filter/OppgaveFilter.tsx | 120 ++++++++++++++++++ .../oppgave-filter/OppgaveFilterText.ts | 8 ++ .../OppgaveLagretFilter.tsx | 19 +++ app/components/oppgave-liste/OppgaveListe.tsx | 55 ++++++++ app/models/oppgave.server.ts | 8 +- .../saksbehandling-index.module.css | 33 ++++- app/routes/saksbehandling._index.tsx | 80 +++++------- mocks/data/mock-filter.ts | 10 ++ 9 files changed, 283 insertions(+), 59 deletions(-) create mode 100644 app/components/oppgave-filter/OppgaveFilter.module.css create mode 100644 app/components/oppgave-filter/OppgaveFilter.tsx create mode 100644 app/components/oppgave-filter/OppgaveFilterText.ts create mode 100644 app/components/oppgave-lagret-filter/OppgaveLagretFilter.tsx create mode 100644 app/components/oppgave-liste/OppgaveListe.tsx create mode 100644 mocks/data/mock-filter.ts diff --git a/app/components/oppgave-filter/OppgaveFilter.module.css b/app/components/oppgave-filter/OppgaveFilter.module.css new file mode 100644 index 00000000..98c85242 --- /dev/null +++ b/app/components/oppgave-filter/OppgaveFilter.module.css @@ -0,0 +1,9 @@ +/* Increased specificity to override Aksel component */ +.emneknaggCheckbox.emneknaggCheckbox { + margin: 0 0 0 var(--a-spacing-3); +} + +/* Override for Aksel component*/ +.emneknaggCheckbox.emneknaggCheckbox > :not(:first-child):not(:empty) { + margin-top: 0; +} diff --git a/app/components/oppgave-filter/OppgaveFilter.tsx b/app/components/oppgave-filter/OppgaveFilter.tsx new file mode 100644 index 00000000..5b4ccf3f --- /dev/null +++ b/app/components/oppgave-filter/OppgaveFilter.tsx @@ -0,0 +1,120 @@ +import React from "react"; +import { Checkbox, CheckboxGroup } from "@navikt/ds-react"; +import { useLoaderData, useSearchParams } from "@remix-run/react"; +import type { IOppgaveFilter } from "../../../mocks/data/mock-filter"; +import type { loader } from "~/routes/saksbehandling._index"; +import styles from "./OppgaveFIlter.module.css"; +import { oppgaveFilterText } from "~/components/oppgave-filter/OppgaveFilterText"; + +interface IProps {} + +export function OppgaveFilter(props: IProps) { + const [searchParams, setSearchParams] = useSearchParams(); + const { oppgaveFilter } = useLoaderData(); + let aktiveFilter = parseUrlParamsToOppgaveFilter(searchParams); + + function oppdaterUrlSearchParams() { + searchParams.delete("oppgaveType"); + + aktiveFilter.forEach((filter) => { + if (filter.emneknagger.length > 0) { + filter.emneknagger.forEach((knagg) => { + searchParams.append("oppgaveType", `${filter.id}.${knagg}`); + }); + } else { + searchParams.append("oppgaveType", filter.id); + } + }); + + setSearchParams(searchParams); + } + + function toggleOppgaveFilter(oppgaveTyper: string[]) { + aktiveFilter = oppgaveTyper.map((id) => { + const existingFilter = aktiveFilter.find((filter) => filter.id === id); + return existingFilter ? existingFilter : { id, emneknagger: [] }; + }); + + oppdaterUrlSearchParams(); + } + + function toggleEmneknagger(emneknagger: string[], oppgaveType: string) { + aktiveFilter = aktiveFilter.map((filter) => { + if (filter.id === oppgaveType) { + return { id: filter.id, emneknagger: emneknagger }; + } + return filter; + }); + + oppdaterUrlSearchParams(); + } + + function hentAktiveOppgaveFilter() { + return aktiveFilter.map((filter) => filter.id); + } + + function hentAktiveEmneknaggerForOppgaveFilter(filterId: string) { + return aktiveFilter.find((filter) => filter.id === filterId)?.emneknagger; + } + + function visEmneknaggerForOppgaveFilter(filter: IOppgaveFilter): boolean { + if (filter.emneknagger.length > 0) { + return !!aktiveFilter.find((f) => f.id === filter.id); + } + return false; + } + return ( + <> + toggleOppgaveFilter(verdi)} + defaultValue={hentAktiveOppgaveFilter()} + > + {oppgaveFilter.map((filter) => ( +
+ {oppgaveFilterText[filter.id]} + + {visEmneknaggerForOppgaveFilter(filter) && ( + toggleEmneknagger(verdi, filter.id)} + > + {filter.emneknagger.map((knagg) => ( + + {oppgaveFilterText[knagg]} + + ))} + + )} +
+ ))} +
+ + ); +} + +function parseUrlParamsToOppgaveFilter(searchParams: URLSearchParams): IOppgaveFilter[] { + const oppgaveFilter: IOppgaveFilter[] = []; + for (const [, value] of searchParams.entries()) { + if (value.includes(".")) { + const string = value.split("."); + const oppgaveId = string[0]; + const emneknagg = string[1]; + + const existingFilterIndex = oppgaveFilter.findIndex((filter) => filter.id === oppgaveId); + if (existingFilterIndex !== -1) { + oppgaveFilter[existingFilterIndex].emneknagger.push(emneknagg); + } else { + oppgaveFilter.push({ id: oppgaveId, emneknagger: [emneknagg] }); + } + } else { + oppgaveFilter.push({ id: value, emneknagger: [] }); + } + } + + return oppgaveFilter; +} diff --git a/app/components/oppgave-filter/OppgaveFilterText.ts b/app/components/oppgave-filter/OppgaveFilterText.ts new file mode 100644 index 00000000..f523c08c --- /dev/null +++ b/app/components/oppgave-filter/OppgaveFilterText.ts @@ -0,0 +1,8 @@ +export const oppgaveFilterText: { [key: string]: string } = { + soknad: "Søknad", + "klage-og-anke": "Klage og Anke", + "korrigerte-meldekort": "Korrigerte meldekort", + eos: "EØS", + "avslag-minsteinntekt": "Avslag minsteinntekt", + permitert: "Permitert", +}; diff --git a/app/components/oppgave-lagret-filter/OppgaveLagretFilter.tsx b/app/components/oppgave-lagret-filter/OppgaveLagretFilter.tsx new file mode 100644 index 00000000..7f888380 --- /dev/null +++ b/app/components/oppgave-lagret-filter/OppgaveLagretFilter.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Button } from "@navikt/ds-react"; + +export function OppgaveLagretFilter() { + return ( +
+ + + + +
+ ); +} diff --git a/app/components/oppgave-liste/OppgaveListe.tsx b/app/components/oppgave-liste/OppgaveListe.tsx new file mode 100644 index 00000000..639e5e6b --- /dev/null +++ b/app/components/oppgave-liste/OppgaveListe.tsx @@ -0,0 +1,55 @@ +import { Table, Tag } from "@navikt/ds-react"; +import React from "react"; +import { hentFormattertDato } from "~/utils/dato.utils"; +import { RemixLink } from "../RemixLink"; +import { oppgaveErFerdigBehandlet } from "~/routes/saksbehandling.oppgave.$oppgaveId"; +import { useTypedRouteLoaderData } from "~/hooks/useTypedRouteLoaderData"; + +export function OppgaveListe() { + const { oppgaver } = useTypedRouteLoaderData("routes/saksbehandling"); + const aapneSaker = oppgaver.filter((oppgave) => !oppgaveErFerdigBehandlet(oppgave)); + + return ( + + + + Opprettet + Oppgave + Status + Personnummer + + + + + + {aapneSaker?.map((oppgave) => { + const { oppgaveId, personIdent, datoOpprettet, tilstand, emneknagger, steg } = oppgave; + return ( + + {hentFormattertDato(datoOpprettet)} + + {emneknagger.map((emneknagg) => ( + + {emneknagg} + + ))} + + {tilstand} + + {personIdent} + + + + Behandle + + + + ); + })} + +
+ ); +} diff --git a/app/models/oppgave.server.ts b/app/models/oppgave.server.ts index 07994b8a..5fc5aad0 100644 --- a/app/models/oppgave.server.ts +++ b/app/models/oppgave.server.ts @@ -35,9 +35,13 @@ export type IOppgaveTilstand = "TilBehandling" | "FerdigBehandlet"; export type IOppgaveStegTilstand = "Groenn" | "Gul" | "Roed"; export type IOpplysningType = "Int" | "Double" | "Boolean" | "LocalDate" | "String"; -export async function hentOppgaver(session: SessionWithOboProvider): Promise { +export async function hentOppgaver( + session: SessionWithOboProvider, + urlParams?: string, +): Promise { const onBehalfOfToken = await getBehandlingOboToken(session); - const url = `${getEnv("DP_SAKSBEHANDLING_URL")}/oppgave`; + const url = `${getEnv("DP_SAKSBEHANDLING_URL")}/oppgave${urlParams || ""}`; + const response = await fetch(url, { method: "GET", headers: { diff --git a/app/route-styles/saksbehandling-index.module.css b/app/route-styles/saksbehandling-index.module.css index 7d9b661d..8fad5d40 100644 --- a/app/route-styles/saksbehandling-index.module.css +++ b/app/route-styles/saksbehandling-index.module.css @@ -1,8 +1,29 @@ -.emneknaggListe { - list-style-type: none; +.container { + display: grid; + grid-template-columns: 250px auto; + grid-template-rows: auto; } -.emneknagg { - float: left; - display: block; - padding-right: 0.3rem; + +.filterMeny { + grid-column: 1 / 2; + grid-row: 1 / span 3; + + padding: var(--a-spacing-4); +} + +.lagretFilter { + grid-column: 2 / 3; + background-color: var(--a-blue-100); + + margin-top: var(--a-spacing-4); + padding: var(--a-spacing-4); + border-radius: var(--a-border-radius-large); +} + +.lagretFilter button { + margin-right: var(--a-spacing-2); +} + +.oppgaveListe { + grid-column: 2 / 3; } diff --git a/app/routes/saksbehandling._index.tsx b/app/routes/saksbehandling._index.tsx index edd49bd5..ce118bca 100644 --- a/app/routes/saksbehandling._index.tsx +++ b/app/routes/saksbehandling._index.tsx @@ -1,58 +1,36 @@ -import { Table, Tag } from "@navikt/ds-react"; -import { RemixLink } from "~/components/RemixLink"; -import { hentFormattertDato } from "~/utils/dato.utils"; -import { useTypedRouteLoaderData } from "~/hooks/useTypedRouteLoaderData"; -import { oppgaveErFerdigBehandlet } from "~/routes/saksbehandling.oppgave.$oppgaveId"; +import styles from "~/route-styles/saksbehandling-index.module.css"; +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { json } from "@remix-run/node"; +import { getSession } from "~/models/auth.server"; +import { hentOppgaver } from "~/models/oppgave.server"; +import { mockOppgaveFilter } from "../../mocks/data/mock-filter"; +import { OppgaveListe } from "~/components/oppgave-liste/OppgaveListe"; +import { OppgaveFilter } from "~/components/oppgave-filter/OppgaveFilter"; +import { OppgaveLagretFilter } from "~/components/oppgave-lagret-filter/OppgaveLagretFilter"; -export default function Saksbehandling() { - const { oppgaver } = useTypedRouteLoaderData("routes/saksbehandling"); - const aapneSaker = oppgaver.filter((oppgave) => !oppgaveErFerdigBehandlet(oppgave)); +export async function loader({ request }: LoaderFunctionArgs) { + const url = new URL(request.url); + + const session = await getSession(request); + const oppgaver = await hentOppgaver(session, url.search); + + return json({ oppgaver, oppgaveFilter: mockOppgaveFilter }); +} +export default function Saksbehandling() { return ( -
- - - - Opprettet - Oppgave - Status - Personnummer - - - +
+
+ +
+ +
+ +
- - {aapneSaker?.map((oppgave) => { - const { oppgaveId, personIdent, datoOpprettet, tilstand, emneknagger, steg } = oppgave; - return ( - - {hentFormattertDato(datoOpprettet)} - - {emneknagger.map((emneknagg) => ( - - {emneknagg} - - ))} - - {tilstand} - - {personIdent} - - - {steg[0] && ( - - Behandle - - )} - - - ); - })} - -
+
+ +
); } diff --git a/mocks/data/mock-filter.ts b/mocks/data/mock-filter.ts new file mode 100644 index 00000000..19a91a1d --- /dev/null +++ b/mocks/data/mock-filter.ts @@ -0,0 +1,10 @@ +export interface IOppgaveFilter { + id: string; + emneknagger: string[]; +} + +export const mockOppgaveFilter: IOppgaveFilter[] = [ + { id: "soknad", emneknagger: ["eos", "avslag-minsteinntekt", "permitert"] }, + { id: "klage-og-anke", emneknagger: ["eos", "avslag-minsteinntekt", "permitert"] }, + { id: "korrigerte-meldekort", emneknagger: ["eos", "permitert"] }, +];