-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
IS-2795: Add endpoint and modal for search
- Loading branch information
1 parent
b544a6a
commit e78a027
Showing
12 changed files
with
425 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface SokDTO { | ||
initials: string; | ||
birthdate: Date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.