Skip to content

Commit

Permalink
Legg til filtrering for oppgaver
Browse files Browse the repository at this point in the history
  • Loading branch information
ssaegrov committed Feb 20, 2024
1 parent c0ba423 commit 5c1be7d
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 59 deletions.
9 changes: 9 additions & 0 deletions app/components/oppgave-filter/OppgaveFilter.module.css
Original file line number Diff line number Diff line change
@@ -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;
}
120 changes: 120 additions & 0 deletions app/components/oppgave-filter/OppgaveFilter.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof loader>();
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 (
<>
<CheckboxGroup
size="small"
legend="Filter"
onChange={(verdi) => toggleOppgaveFilter(verdi)}
defaultValue={hentAktiveOppgaveFilter()}
>
{oppgaveFilter.map((filter) => (
<div key={filter.id}>
<Checkbox value={filter.id}>{oppgaveFilterText[filter.id]}</Checkbox>

{visEmneknaggerForOppgaveFilter(filter) && (
<CheckboxGroup
legend=""
size="small"
className={styles.emneknaggCheckbox}
defaultValue={hentAktiveEmneknaggerForOppgaveFilter(filter.id)}
onChange={(verdi) => toggleEmneknagger(verdi, filter.id)}
>
{filter.emneknagger.map((knagg) => (
<Checkbox key={knagg} value={knagg}>
{oppgaveFilterText[knagg]}
</Checkbox>
))}
</CheckboxGroup>
)}
</div>
))}
</CheckboxGroup>
</>
);
}

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;
}
8 changes: 8 additions & 0 deletions app/components/oppgave-filter/OppgaveFilterText.ts
Original file line number Diff line number Diff line change
@@ -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",
};
19 changes: 19 additions & 0 deletions app/components/oppgave-lagret-filter/OppgaveLagretFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import { Button } from "@navikt/ds-react";

export function OppgaveLagretFilter() {
return (
<div>
<Button variant="tertiary-neutral" size="xsmall">
Mine saker
</Button>
<Button variant="tertiary-neutral" size="xsmall">
Alle saker
</Button>

<Button variant="tertiary-neutral" size="xsmall">
Minste inntekt
</Button>
</div>
);
}
55 changes: 55 additions & 0 deletions app/components/oppgave-liste/OppgaveListe.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Table zebraStripes={true}>
<Table.Header>
<Table.Row>
<Table.HeaderCell scope="col">Opprettet</Table.HeaderCell>
<Table.HeaderCell scope="col">Oppgave</Table.HeaderCell>
<Table.HeaderCell scope="col">Status</Table.HeaderCell>
<Table.HeaderCell scope="col">Personnummer</Table.HeaderCell>
<Table.HeaderCell scope="col"></Table.HeaderCell>
</Table.Row>
</Table.Header>

<Table.Body>
{aapneSaker?.map((oppgave) => {
const { oppgaveId, personIdent, datoOpprettet, tilstand, emneknagger, steg } = oppgave;
return (
<Table.Row key={oppgave.oppgaveId}>
<Table.DataCell>{hentFormattertDato(datoOpprettet)}</Table.DataCell>
<Table.DataCell>
{emneknagger.map((emneknagg) => (
<Tag key={emneknagg} size={"xsmall"} variant="alt2-filled">
{emneknagg}
</Tag>
))}
</Table.DataCell>
<Table.DataCell>{tilstand}</Table.DataCell>
<Table.DataCell>
<RemixLink to={`person/${oppgaveId}/oversikt`}>{personIdent}</RemixLink>
</Table.DataCell>
<Table.DataCell>
<RemixLink
to={`oppgave/${oppgaveId}/steg/${steg[0].uuid}`}
asButtonVariant="primary"
>
Behandle
</RemixLink>
</Table.DataCell>
</Table.Row>
);
})}
</Table.Body>
</Table>
);
}
8 changes: 6 additions & 2 deletions app/models/oppgave.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<IOppgave[]> {
export async function hentOppgaver(
session: SessionWithOboProvider,
urlParams?: string,
): Promise<IOppgave[]> {
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: {
Expand Down
33 changes: 27 additions & 6 deletions app/route-styles/saksbehandling-index.module.css
Original file line number Diff line number Diff line change
@@ -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;
}
80 changes: 29 additions & 51 deletions app/routes/saksbehandling._index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<main>
<Table zebraStripes={true}>
<Table.Header>
<Table.Row>
<Table.HeaderCell scope="col">Opprettet</Table.HeaderCell>
<Table.HeaderCell scope="col">Oppgave</Table.HeaderCell>
<Table.HeaderCell scope="col">Status</Table.HeaderCell>
<Table.HeaderCell scope="col">Personnummer</Table.HeaderCell>
<Table.HeaderCell scope="col"></Table.HeaderCell>
</Table.Row>
</Table.Header>
<main className={styles.container}>
<div className={styles.filterMeny}>
<OppgaveFilter />
</div>

<div className={styles.lagretFilter}>
<OppgaveLagretFilter />
</div>

<Table.Body>
{aapneSaker?.map((oppgave) => {
const { oppgaveId, personIdent, datoOpprettet, tilstand, emneknagger, steg } = oppgave;
return (
<Table.Row key={oppgave.oppgaveId}>
<Table.DataCell>{hentFormattertDato(datoOpprettet)}</Table.DataCell>
<Table.DataCell>
{emneknagger.map((emneknagg) => (
<Tag key={emneknagg} size={"xsmall"} variant="alt2-filled">
{emneknagg}
</Tag>
))}
</Table.DataCell>
<Table.DataCell>{tilstand}</Table.DataCell>
<Table.DataCell>
<RemixLink to={`person/${oppgaveId}/oversikt`}>{personIdent}</RemixLink>
</Table.DataCell>
<Table.DataCell>
{steg[0] && (
<RemixLink
to={`oppgave/${oppgaveId}/steg/${steg[0].uuid}`}
asButtonVariant="primary"
>
Behandle
</RemixLink>
)}
</Table.DataCell>
</Table.Row>
);
})}
</Table.Body>
</Table>
<div className={styles.oppgaveListe}>
<OppgaveListe />
</div>
</main>
);
}
10 changes: 10 additions & 0 deletions mocks/data/mock-filter.ts
Original file line number Diff line number Diff line change
@@ -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"] },
];

0 comments on commit 5c1be7d

Please sign in to comment.