Skip to content

Commit

Permalink
OK-683 & OK-713 (#74)
Browse files Browse the repository at this point in the history
* OK-683 nollataan lähetysten sivutus jos hakukriteeri muuttuu
* Mui v6 TreeSelectin migraatio
* OK-713 korjattu organisaatiovalinta ja päivitetty käyttämään uusia mui-komponentteja
* OK-713 passataan kieli parametrina eikä hookin kautta organsaatiopuulle, parametrin uudelleennimentä ja sivutuksen nollaus organisaation vaihtuessa
* OK-683 nollataan sivutus myös vastaanottajalistalla jos hakukriteeri muuttuu
  • Loading branch information
marjakari authored Nov 19, 2024
1 parent 99a3e99 commit 80179c9
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,6 @@ class LahetysResource {
try
// suodatetaan pois swagger-esimerkkirivin palvelu
val palvelut = kantaOperaatiot.getLahettavatPalvelut().filterNot(p => p.equals("Esimerkkipalvelu"))
LOG.info(s"Löytyi ${palvelut.size} palvelua")
ResponseEntity.status(HttpStatus.OK).body(write[List[String]](palvelut))
catch
case e: Exception =>
Expand Down
115 changes: 80 additions & 35 deletions viestinvalitys-raportointi/src/app/Haku.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
SelectChangeEvent,
} from '@mui/material';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
import { LocalizationProvider, PickersActionBarProps } from '@mui/x-date-pickers';
import {
LocalizationProvider,
PickersActionBarProps,
} from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import 'dayjs/locale/fi';
import 'dayjs/locale/sv';
Expand All @@ -19,20 +22,29 @@ import { Search } from '@mui/icons-material';
import { NUQS_DEFAULT_OPTIONS } from './lib/constants';
import { LahettavaPalveluInput } from './components/LahettavaPalveluInput';
import { OphFormControl } from './components/OphFormControl';
import { OphButton, ophColors, OphSelect } from '@opetushallitus/oph-design-system';
import {
OphButton,
ophColors,
OphSelect,
} from '@opetushallitus/oph-design-system';
import dayjs from 'dayjs';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useHasChanged } from './hooks/useHasChanged';

function CustomActionBar(props: PickersActionBarProps) {
const { onAccept, onClear, className } = props;
const t = useTranslations();
return (
<DialogActions className={className} sx={{ mb: 2, mr: 2}}>
<OphButton variant='contained' onClick={onAccept}>{t('yleinen.ok')}</OphButton>
<OphButton variant='outlined' onClick={onClear}>{t('yleinen.tyhjenna')}</OphButton>
</DialogActions>
<DialogActions className={className} sx={{ mb: 2, mr: 2 }}>
<OphButton variant="contained" onClick={onAccept}>
{t('yleinen.ok')}
</OphButton>
<OphButton variant="outlined" onClick={onClear}>
{t('yleinen.tyhjenna')}
</OphButton>
</DialogActions>
);
}
}

const HakukenttaSelect = ({
labelId,
Expand Down Expand Up @@ -96,23 +108,56 @@ export default function Haku({
'hakusana',
NUQS_DEFAULT_OPTIONS,
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [seuraavatAlkaen, setSeuraavatAlkaen] = useQueryState(
'seuraavatAlkaen',
NUQS_DEFAULT_OPTIONS,
);
const [palvelu, setPalvelu] = useQueryState('palvelu', NUQS_DEFAULT_OPTIONS);
const [hakuAlkaen, setHakuAlkaen] = useQueryState('hakuAlkaen', NUQS_DEFAULT_OPTIONS);
const [hakuPaattyen, setHakuPaattyen] = useQueryState('hakuPaattyen', NUQS_DEFAULT_OPTIONS);
const [calendarErrors, setCalendarErrors] = useState<Array<string>>([])
const [hakuAlkaen, setHakuAlkaen] = useQueryState(
'hakuAlkaen',
NUQS_DEFAULT_OPTIONS,
);
const [hakuPaattyen, setHakuPaattyen] = useQueryState(
'hakuPaattyen',
NUQS_DEFAULT_OPTIONS,
);
const [calendarErrors, setCalendarErrors] = useState<Array<string>>([]);

const handleAlkuDateTimeChange = (value: dayjs.Dayjs | null) => {
setHakuAlkaen(value?.toISOString() ?? null)
};
const hakusanaChanged = useHasChanged(hakusana);
const palveluChanged = useHasChanged(palvelu);
const hakuAlkaenChanged = useHasChanged(hakuAlkaen);
const hakuPaattyenChanged = useHasChanged(hakuPaattyen);

const handleLoppuDateTimeChange = (value: dayjs.Dayjs | null) => {
if(hakuAlkaen && value && !dayjs(hakuAlkaen).isBefore(value)) {
setCalendarErrors([t('error.virheellinen-aikavali')]);
} else {
setCalendarErrors([]);
setHakuPaattyen(value?.toISOString() ?? null);
}
};
useEffect(() => {
if (
hakusanaChanged ||
palveluChanged ||
hakuAlkaenChanged ||
hakuPaattyenChanged
) {
setSeuraavatAlkaen(null);
}
}, [
hakusanaChanged,
palveluChanged,
hakuAlkaenChanged,
hakuPaattyenChanged,
setSeuraavatAlkaen,
]);

const handleAlkuDateTimeChange = (value: dayjs.Dayjs | null) => {
setHakuAlkaen(value?.toISOString() ?? null);
};

const handleLoppuDateTimeChange = (value: dayjs.Dayjs | null) => {
if (hakuAlkaen && value && !dayjs(hakuAlkaen).isBefore(value)) {
setCalendarErrors([t('error.virheellinen-aikavali')]);
} else {
setCalendarErrors([]);
setHakuPaattyen(value?.toISOString() ?? null);
}
};

// päivitetään 3s viiveellä hakuparametrit
const handleTypedSearch = useDebouncedCallback((term) => {
Expand Down Expand Up @@ -151,21 +196,21 @@ const handleLoppuDateTimeChange = (value: dayjs.Dayjs | null) => {
rightArrowIcon: {
sx: { border: '1px solid', borderRadius: '50%' },
},
}
};

return (
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={locale}>
<Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'stretch',
gap: 2,
marginBottom: 2,
flexWrap: 'wrap',
alignItems: 'flex-end'
}}
display: 'flex',
flexDirection: 'row',
justifyContent: 'stretch',
gap: 2,
marginBottom: 2,
flexWrap: 'wrap',
alignItems: 'flex-end',
}}
>
<HakukenttaInput
value={selectedHakukentta ?? ''}
Expand Down Expand Up @@ -223,10 +268,10 @@ const handleLoppuDateTimeChange = (value: dayjs.Dayjs | null) => {
value={dayjs(hakuAlkaen)}
onChange={(newValue) => handleAlkuDateTimeChange(newValue)}
aria-labelledby={labelId}
timeSteps = {{ minutes: 1}}
timeSteps={{ minutes: 1 }}
slots={{
actionBar: CustomActionBar,
}}
}}
slotProps={calendarSlotProps}
/>
);
Expand All @@ -243,10 +288,10 @@ const handleLoppuDateTimeChange = (value: dayjs.Dayjs | null) => {
value={dayjs(hakuPaattyen)}
onChange={(newValue) => handleLoppuDateTimeChange(newValue)}
aria-labelledby={labelId}
timeSteps = {{ minutes: 1}}
timeSteps={{ minutes: 1 }}
slots={{
actionBar: CustomActionBar,
}}
}}
slotProps={calendarSlotProps}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useTranslations } from 'next-intl';

export default function OrganisaatioFilter() {
const [organisaatioHaku, setOrganisaatioHaku] = useQueryState(
'orgSearchStr',
'organisaatioHaku',
NUQS_DEFAULT_OPTIONS,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,83 +1,42 @@
'use client';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import { TreeItem, TreeView } from '@mui/x-tree-view';
import { TreeItem2, TreeItem2Props, TreeItem2SlotProps } from '@mui/x-tree-view';
import { LanguageCode, Organisaatio } from '../lib/types';
import { FormControl, FormControlLabel, Radio } from '@mui/material';
import { ChangeEvent, SyntheticEvent } from 'react';
import { translateOrgName } from '../lib/util';
import { useLocale, useTranslations } from 'next-intl';
import React from 'react';
import { RadioButtonChecked, RadioButtonUnchecked } from '@mui/icons-material';

type Props = {
organisaatiot: Organisaatio[];
selectedOid: string | undefined;
expandedOids: string[];
handleSelect: (event: SyntheticEvent<Element, Event>, nodeId: string) => void;
handleChange: (event: ChangeEvent<HTMLInputElement>) => void;
handleToggle: (event: SyntheticEvent<Element, Event>, nodeIds: string[]) => void;
}

const OrganisaatioHierarkia = ({
organisaatiot,
selectedOid,
expandedOids,
handleSelect,
handleChange,
handleToggle,
}: Props) => {
const lng = useLocale() as LanguageCode;
const t = useTranslations();
const renderTree = (org: Organisaatio) => {
if (!org) {
return null;
}
return (
<TreeItem
key={org.oid}
// @ts-expect-error: Tässä kohtaa tyypitys menee hankalaksi
nodeId={org.oid}
label={
<FormControl>
<FormControlLabel
label={translateOrgName(org, lng)}
control={
<Radio
checked={selectedOid === org.oid}
name="organisaatio"
value={org.oid}
onChange={(e) => {
handleChange(e);
}}
/>
}
/>
</FormControl>
}
>
{Array.isArray(org.children)
? org.children.map((node) => renderTree(node))
: null}
</TreeItem>
);
};
const CustomTreeItem = React.forwardRef(function CustomTreeItem(
props: TreeItem2Props,
ref: React.Ref<HTMLLIElement>,
) {
return (
<TreeItem2
{...props}
ref={ref}
slotProps= {{
checkbox: {
icon: <RadioButtonUnchecked />,
checkedIcon: <RadioButtonChecked />,
},
} as TreeItem2SlotProps
}
/>
);
});

export const OrganisaatioTree = (org: Organisaatio, lng: LanguageCode) => {
if (!org) {
return null;
}
return (
<>
<TreeView
multiSelect={false}
aria-label={t('organisaatio.label')}
// @ts-expect-error: Tässä kohtaa tyypitys menee hankalaksi
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
onNodeSelect={handleSelect}
onNodeToggle={handleToggle}
selected={selectedOid}
expanded={expandedOids}
>
{organisaatiot.map((org) => renderTree(org))}
</TreeView>
</>
<CustomTreeItem
itemId={org.oid}
key={org.oid}
label={translateOrgName(org, lng)}
>
{Array.isArray(org.children)
? org.children.map((node) => OrganisaatioTree(node, lng))
: null}
</CustomTreeItem>
);
};

export default OrganisaatioHierarkia;
Loading

0 comments on commit 80179c9

Please sign in to comment.