Skip to content

Commit

Permalink
IS-2795: Add endpoint and modal for search
Browse files Browse the repository at this point in the history
  • Loading branch information
eirikdahlen committed Nov 26, 2024
1 parent 24899aa commit 8427dc4
Show file tree
Hide file tree
Showing 14 changed files with 489 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/api/types/personoversiktTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface PersonOversiktStatusDTO
extends PersonOversiktUbehandletStatusDTO {
fnr: string;
navn: string;
fodselsdato: Date;
enhet: string;
veilederIdent: string | null;
motestatus: MoteStatusType | undefined;
Expand Down
4 changes: 4 additions & 0 deletions src/api/types/sokDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface SokDTO {
initials: string;
birthdate: Date;
}
14 changes: 4 additions & 10 deletions src/components/NewOversiktTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,19 @@ import { Checkbox, Table } from '@navikt/ds-react';
import { VeilederColumn } from '@/components/VeilederColumn';
import { PersonData } from '@/api/types/personregisterTypes';
import { PersonRadVirksomhetColumn } from '@/components/PersonRadVirksomhetColumn';
import { OppfolgingstilfelleDTO } from '@/api/types/personoversiktTypes';
import { FristDataCell } from '@/components/FristDataCell';
import { Sorting, SortingKey, useSorting } from '@/hooks/useSorting';
import { LinkSyfomodiaperson } from '@/components/LinkSyfomodiaperson';
import { toLastnameFirstnameFormat } from '@/utils/stringUtil';
import { getHendelser } from '@/utils/hendelseColumnUtils';
import {
getHendelser,
getVarighetOppfolgingstilfelle,
} from '@/utils/hendelseColumnUtils';
import { useTabType } from '@/context/tab/TabTypeContext';
import { OverviewTabType } from '@/konstanter';
import * as Amplitude from '@/utils/amplitude';
import { EventType } from '@/utils/amplitude';

function getVarighetOppfolgingstilfelle(
oppfolgingstilfelle: OppfolgingstilfelleDTO | undefined
): string {
return oppfolgingstilfelle
? `${oppfolgingstilfelle.varighetUker} uker`
: 'Ukjent';
}

interface Props {
personListe: [string, PersonData][];
selectedRows: string[];
Expand Down
139 changes: 139 additions & 0 deletions src/components/sokperson/SokPerson.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, { useState } from 'react';
import {
Alert,
BodyShort,
Box,
Button,
Heading,
HStack,
Modal,
TextField,
VStack,
} from '@navikt/ds-react';
import { useSokPerson } from '@/data/personoversiktHooks';
import { SokDTO } from '@/api/types/sokDTO';
import SokPersonResultat from '@/components/sokperson/SokPersonResultat';
import { MagnifyingGlassIcon } from '@navikt/aksel-icons';
import { isNumeric, removePunctuation } from '@/utils/stringUtil';
import { parseDateString } from '@/utils/dateUtils';

const texts = {
buttonText: 'Søk etter sykmeldt',
header: 'Søk etter sykmeldt',
info:
'Her kan du søke opp sykmeldte personer basert på initialer og fødselsdato.',
validation: {
initials: 'Vennligst angi gyldige initialer',
birthdate: 'Vennligst angi en gyldig fødselsdato',
},
error: 'Noe gikk galt under søket. Vennligst prøv igjen.',
};

export default function SokPerson() {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [nameInitials, setNameInitials] = useState<string>('');
const [birthdate, setBirthdate] = useState<string>('');
const {
mutate,
data: searchResults,
isLoading,
isError,
isSuccess,
} = useSokPerson();
const [isFormError, setIsFormError] = useState<boolean>(false);

const parseBirthdate = (birthdate: string): Date | null => {
const cleanedDateStr = removePunctuation(birthdate);

if (cleanedDateStr.length < 6 || !isNumeric(cleanedDateStr)) {
return null;
} else {
return parseDateString(cleanedDateStr);
}
};

const validInitials = (initials: string): boolean => {
return initials.length <= 3 && initials.length > 1;
};

const handleSubmit = () => {
const parsedBirthdate = parseBirthdate(birthdate);
if (validInitials(nameInitials) && !!parsedBirthdate) {
const requestDTO: SokDTO = {
initials: nameInitials.toLowerCase(),
birthdate: parsedBirthdate,
};
mutate(requestDTO);
} else {
setIsFormError(true);
}
};

return (
<Box>
<Button onClick={() => setIsModalOpen(true)} size="small">
{texts.buttonText}
</Button>
<Modal
closeOnBackdropClick
className="w-[90%] max-w-[90%]"
open={isModalOpen}
aria-label={texts.buttonText}
onClose={() => setIsModalOpen(false)}
>
<Modal.Header>
<Heading level="2" size="medium">
{texts.header}
</Heading>
</Modal.Header>
<Modal.Body>
<VStack gap="4">
<BodyShort>{texts.info}</BodyShort>
<HStack gap="8" align="end">
<TextField
label="Initialer"
description="AB"
htmlSize={10}
type="text"
onChange={(e) => setNameInitials(e.target.value)}
error={
isFormError && !validInitials(nameInitials)
? texts.validation.initials
: undefined
}
/>
<TextField
label="Fødselsdato"
description="ddmmåå"
htmlSize={14}
type="text"
onChange={(e) => setBirthdate(e.target.value)}
error={
isFormError && parseBirthdate(birthdate) === null
? texts.validation.birthdate
: undefined
}
/>
<Button
onClick={handleSubmit}
loading={isLoading}
icon={<MagnifyingGlassIcon />}
type="submit"
>
Søk
</Button>
</HStack>
{searchResults && isSuccess && (
<SokPersonResultat sokeresultater={searchResults} />
)}
{isError && (
<Alert variant="error" size="small">
{texts.error}
</Alert>
)}
</VStack>
</Modal.Body>
</Modal>
</Box>
);
}
76 changes: 76 additions & 0 deletions src/components/sokperson/SokPersonResultat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { ReactElement } from 'react';
import { useSorting } from '@/hooks/useSorting';
import { PersonOversiktStatusDTO } from '@/api/types/personoversiktTypes';
import { BodyShort, Box, Table } from '@navikt/ds-react';
import { LinkSyfomodiaperson } from '@/components/LinkSyfomodiaperson';
import { toLastnameFirstnameFormat } from '@/utils/stringUtil';
import { toPersonData } from '@/utils/toPersondata';

const texts = {
noResults: {
first: 'Fant ingen sykmeldte personer for søkeparameterne.',
second:
'Det kan hende personen ikke er sykmeldt eller at du ikke har tilgang å se personen.',
},
};

interface Props {
sokeresultater: PersonOversiktStatusDTO[];
}

export default function SokPersonResultat({
sokeresultater,
}: Props): ReactElement {
const { columns: allColumns } = useSorting();
const columns = allColumns.filter(
(column) => column.sortKey === 'NAME' || column.sortKey === 'FNR'
);

const personer = Object.entries(toPersonData(sokeresultater, []));

return personer.length === 0 ? (
<Box>
<BodyShort>{texts.noResults.first}</BodyShort>
<BodyShort>{texts.noResults.second}</BodyShort>
</Box>
) : (
<Table size="small" zebraStripes className="bg-white mt-2">
<Table.Header>
<Table.Row>
{columns.map((col, index) => (
<Table.ColumnHeader key={index} sortKey={col.sortKey}>
{col.sortingText}
</Table.ColumnHeader>
))}
<Table.DataCell />
</Table.Row>
</Table.Header>
<Table.Body>
{personer.map(([fnr, persondata], index) => (
<Table.Row key={index}>
<Table.HeaderCell scope="row" textSize="small">
{persondata.navn.length > 0 && (
<LinkSyfomodiaperson
personData={persondata}
personident={fnr}
linkText={toLastnameFirstnameFormat(persondata.navn)}
/>
)}
</Table.HeaderCell>
<Table.DataCell textSize="small">
{persondata.navn.length > 0 ? (
fnr
) : (
<LinkSyfomodiaperson
personData={persondata}
personident={fnr}
linkText={fnr}
/>
)}
</Table.DataCell>
</Table.Row>
))}
</Table.Body>
</Table>
);
}
18 changes: 11 additions & 7 deletions src/components/toolbar/ToolbarWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { ReactElement, useState } from 'react';
import Toolbar from './Toolbar';
import { Label } from '@navikt/ds-react';
import { PAGINATED_NUMBER_OF_ITEMS } from '@/components/toolbar/PaginationContainer';
import SokPerson from '@/components/sokperson/SokPerson';

export interface ToolbarWrapperProps {
alleMarkert: boolean;
Expand Down Expand Up @@ -38,15 +39,18 @@ const ToolbarWrapper = (props: ToolbarWrapperProps): ReactElement => {

return (
<>
<div className="px-1 py-2 flex flex-row gap-1">
<Label size="small">
{textPaginatedUsers(pageInfo, props.numberOfItemsTotal)}
</Label>
{props.markertePersoner.length > 0 && (
<div className="px-1 py-2 flex justify-between items-center">
<div className="flex gap-1">
<Label size="small">
{textMarkedUsers(props.markertePersoner.length)}
{textPaginatedUsers(pageInfo, props.numberOfItemsTotal)}
</Label>
)}
{props.markertePersoner.length > 0 && (
<Label size="small">
{textMarkedUsers(props.markertePersoner.length)}
</Label>
)}
</div>
<SokPerson />
</div>
<Toolbar {...props} setPageInfo={setPageInfo} />
</>
Expand Down
20 changes: 18 additions & 2 deletions src/data/personoversiktHooks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useQuery } from '@tanstack/react-query';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
PersonOversiktStatusDTO,
PersonOversiktUbehandletStatusDTO,
} from '@/api/types/personoversiktTypes';
import { get } from '@/api/axios';
import { get, post } from '@/api/axios';
import { useAktivEnhet } from '@/context/aktivEnhet/AktivEnhetContext';
import { useNotifications } from '@/context/notification/NotificationContext';
import { FetchPersonoversiktFailed } from '@/context/notification/Notifications';
Expand All @@ -12,6 +12,7 @@ import { useAsyncError } from '@/data/useAsyncError';
import { minutesToMillis } from '@/utils/timeUtils';
import { useMemo } from 'react';
import { PERSONOVERSIKT_ROOT } from '@/apiConstants';
import { SokDTO } from '@/api/types/sokDTO';

const isUbehandlet = (ubehandletStatus: PersonOversiktUbehandletStatusDTO) => {
return Object.values(ubehandletStatus).some((value) => value);
Expand Down Expand Up @@ -88,3 +89,18 @@ export const usePersonoversiktQuery = () => {
),
};
};

export const useSokPerson = () => {
const path = `${PERSONOVERSIKT_ROOT}/search`;
const postSok = (sokDTO: SokDTO) =>
post<PersonOversiktStatusDTO[]>(path, sokDTO);

const mutation = useMutation({
mutationFn: postSok,
});

return {
...mutation,
data: mutation.data || [],
};
};
Loading

0 comments on commit 8427dc4

Please sign in to comment.