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 21, 2024
1 parent b544a6a commit 819522d
Show file tree
Hide file tree
Showing 12 changed files with 424 additions and 20 deletions.
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
134 changes: 134 additions & 0 deletions src/components/sokperson/SokPerson.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React, { useState } from 'react';
import {
BodyShort,
Box,
Button,
Heading,
HStack,
Modal,
TextField,
VStack,
} from '@navikt/ds-react';
import { useSokPerson } from '@/data/personoversiktHooks';
import { PersonOversiktStatusDTO } from '@/api/types/personoversiktTypes';
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',
},
};

export default function SokPerson() {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [nameInitials, setNameInitials] = useState<string>('');
const [birthdate, setBirthdate] = useState<string>('');
const sokPerson = useSokPerson();
const [searchResult, setSearchResult] = useState<
PersonOversiktStatusDTO[] | undefined
>(undefined);
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 !== 0;
};

const handleSubmit = () => {
const parsedBirthdate = parseBirthdate(birthdate);
if (validInitials(nameInitials) && parsedBirthdate) {
const requestDTO: SokDTO = {
initials: nameInitials.toLowerCase(),
birthdate: parsedBirthdate,
};
sokPerson.mutate(requestDTO, {
onSuccess: (data) => {
setSearchResult(data);
},
});
} 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="ddmmyy"
htmlSize={14}
type="text"
onChange={(e) => setBirthdate(e.target.value)}
error={
isFormError && parseBirthdate(birthdate) === null
? texts.validation.birthdate
: undefined
}
/>
<Button
onClick={handleSubmit}
loading={sokPerson.isLoading}
icon={<MagnifyingGlassIcon />}
type="submit"
>
Søk
</Button>
</HStack>
{searchResult && (
<SokPersonResultat sokeresultater={searchResult} />
)}
</VStack>
</Modal.Body>
</Modal>
</Box>
);
}
102 changes: 102 additions & 0 deletions src/components/sokperson/SokPersonResultat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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 { PersonRadVirksomhetColumn } from '@/components/PersonRadVirksomhetColumn';
import { VeilederColumn } from '@/components/VeilederColumn';
import { FristDataCell } from '@/components/FristDataCell';
import {
getHendelser,
getVarighetOppfolgingstilfelle,
} from '@/utils/hendelseColumnUtils';
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 } = useSorting();

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.DataCell textSize="small">
<PersonRadVirksomhetColumn personData={persondata} />
</Table.DataCell>
<Table.DataCell textSize="small">
<VeilederColumn personData={persondata} />
</Table.DataCell>
<Table.DataCell textSize="small">
{getVarighetOppfolgingstilfelle(
persondata.latestOppfolgingstilfelle
)}
</Table.DataCell>
<FristDataCell personData={persondata} />
<Table.DataCell
textSize="small"
className="[&>*:not(:last-child)]:mb-1.5"
>
{getHendelser(persondata).map((hendelse, index) => (
<p key={index} className="m-0">
{hendelse}
</p>
))}
</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
15 changes: 13 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,13 @@ export const usePersonoversiktQuery = () => {
),
};
};

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

return useMutation({
mutationFn: postSok,
});
};
6 changes: 5 additions & 1 deletion src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { mockUnleash } from '@/mocks/mockUnleash';
import { mockSyfoveileder } from '@/mocks/syfoveileder/mockSyfoveileder';
import { mockSyfoperson } from '@/mocks/syfoperson/mockSyfoperson';
import { mockPersontildeling } from '@/mocks/persontildeling/mockPersontildeling';
import { mockPersonoversikt } from '@/mocks/personoversikt/mockPersonoversikt';
import {
mockPersonoversikt,
mockSokPerson,
} from '@/mocks/personoversikt/mockPersonoversikt';
import { mockModiacontextholder } from '@/mocks/modiacontextholder/mockModiacontextholder';
import { mockFlexjar } from '@/mocks/flexjar/mockFlexjar';
import { mockEreg } from '@/mocks/ereg/mockEreg';
Expand All @@ -22,6 +25,7 @@ const handlers: HttpHandler[] = [
mockSyfoperson(generatedPersons),
mockPersontildeling,
mockPersonoversikt(generatedPersons),
mockSokPerson(),
...mockModiacontextholder,
];

Expand Down
14 changes: 14 additions & 0 deletions src/mocks/personoversikt/mockPersonoversikt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MockPerson,
} from '../mockUtils';
import { http, HttpResponse } from 'msw';
import { SokDTO } from '@/api/types/sokDTO';

const personoversiktEnhet = (generatedPersons: MockPerson[]) => [
...personoversiktEnhetMock,
Expand All @@ -15,3 +16,16 @@ export const mockPersonoversikt = (generatedPersons: MockPerson[]) =>
http.get(`${PERSONOVERSIKT_ROOT}/enhet/:id`, () =>
HttpResponse.json(personoversiktEnhet(generatedPersons))
);

export function mockSokPerson() {
return http.post(`${PERSONOVERSIKT_ROOT}/search`, async ({ request }) => {
const requestBody = (await request.json()) as SokDTO;
console.log('requestBody', requestBody);
const results = personoversiktEnhetMock.filter(
(person) =>
person.navn.toLowerCase().substring(0, 1) ===
requestBody.initials.toLowerCase().substring(0, 1)
);
return HttpResponse.json(results);
});
}
Loading

0 comments on commit 819522d

Please sign in to comment.