Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mote varighet #1191

Merged
merged 13 commits into from
Sep 14, 2023
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.78",
"@testing-library/react": "^12.1.5",
"@testing-library/user-event": "^14.4.3",
"@types/moment-duration-format": "^2.2.3",
"@types/node": "^18.11.9",
"@types/react": "^17.0.45",
Expand Down
53 changes: 40 additions & 13 deletions src/moduler/aktivitet/aktivitet-forms/mote/MoteAktivitetForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AktivitetStatus, Kanal } from '../../../../datatypes/aktivitetTypes';
import { MoteAktivitet, VeilarbAktivitetType } from '../../../../datatypes/internAktivitetTypes';
import { coerceToUndefined } from '../../../../felles-komponenter/skjema/datovelger/common';
import ControlledDatePicker from '../../../../felles-komponenter/skjema/datovelger/ControlledDatePicker';
import { beregnFraTil, beregnKlokkeslettVarighet, formatterVarighet } from '../../aktivitet-util';
import { beregnFraTil, beregnKlokkeslettVarighet } from '../../aktivitet-util';
import AktivitetFormHeader from '../AktivitetFormHeader';
import CustomErrorSummary from '../CustomErrorSummary';
import { dateOrUndefined } from '../ijobb/AktivitetIjobbForm';
Expand All @@ -23,8 +23,7 @@ const schema = z.object({
invalid_type_error: 'Ikke en gyldig dato',
}),
klokkeslett: z.string().min(1, 'Du må fylle ut klokkeslett'),

varighet: z.string().min(1, 'Du må fylle ut varighet'),
varighet: z.number({ invalid_type_error: 'Du må velge varighet' }), // Blir NaN på default value
kanal: z.nativeEnum(Kanal, {
errorMap: (issue) => {
switch (issue.code) {
Expand Down Expand Up @@ -63,7 +62,7 @@ const MoteAktivitetForm = (props: Props) => {
tittel: aktivitet?.tittel,
klokkeslett: moteTid?.klokkeslett,
// Keep field as string since input natively returns string
varighet: formatterVarighet(moteTid?.varighet),
varighet: moteTid?.varighet,
kanal: aktivitet?.kanal,
adresse: aktivitet?.adresse,
beskrivelse: aktivitet?.beskrivelse,
Expand Down Expand Up @@ -91,6 +90,26 @@ const MoteAktivitetForm = (props: Props) => {
const beskrivelseValue = watch('beskrivelse'); // for <Textarea /> character-count to work
const forberedelserValue = watch('forberedelser'); // for <Textarea /> character-count to work

const varighet = [
{ minutter: 15, tekst: '15 minutter' },
{ minutter: 30, tekst: '30 minutter' },
{ minutter: 45, tekst: '45 minutter' },
{ minutter: 60, tekst: '1 time' },
{ minutter: 90, tekst: '1 time, 30 minutter' },
{ minutter: 120, tekst: '2 timer' },
{ minutter: 150, tekst: '2 timer, 30 minutter' },
{ minutter: 180, tekst: '3 timer' },
{ minutter: 210, tekst: '3 timer, 30 minutter' },
{ minutter: 240, tekst: '4 timer' },
{ minutter: 270, tekst: '4 timer, 30 minutter' },
{ minutter: 300, tekst: '5 timer' },
{ minutter: 330, tekst: '5 timer, 30 minutter' },
{ minutter: 360, tekst: '6 timer' },
{ minutter: 390, tekst: '6 timer, 30 minutter' },
{ minutter: 420, tekst: '7 timer' },
{ minutter: 450, tekst: '7 timer, 30 minutter' },
];

return (
<form
autoComplete="off"
Expand All @@ -102,7 +121,7 @@ const MoteAktivitetForm = (props: Props) => {
status: AktivitetStatus.PLANLAGT,
avtalt: false,
// dato: selectedDay!!.toString(),
})
}),
)}
>
<FormProvider {...formHandlers}>
Expand All @@ -124,18 +143,26 @@ const MoteAktivitetForm = (props: Props) => {
<TextField
label="Klokkeslett (obligatorisk)"
{...register('klokkeslett')}
type={'time' as any}
step="300"
type="time"
error={errors.klokkeslett && errors.klokkeslett.message}
/>
<TextField
<Select
label="Varighet (obligatorisk)"
{...register('varighet')}
type={'time' as any}
step="900"
{...register('varighet', { valueAsNumber: true })}
error={errors.varighet && errors.varighet.message}
/>
<Select label="Møteform (obligatorisk)" {...register('kanal')} error={errors.kanal && errors.kanal.message}>
>
<option value="">Velg varighet</option>
{varighet.map((item) => (
<option value={item.minutter} key={item.minutter}>
{item.tekst}
</option>
))}
</Select>
<Select
label="Møteform (obligatorisk)"
{...register('kanal')}
error={errors.kanal && errors.kanal.message}
>
<option value="">Velg møteform</option>
<option value={Kanal.OPPMOTE}>Oppmøte</option>
<option value={Kanal.TELEFON}>Telefonmøte</option>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { configureStore } from '@reduxjs/toolkit';
import { RenderResult, fireEvent, getByRole, render, screen } from '@testing-library/react';
import { RenderResult, fireEvent, render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { addDays, addMinutes, subYears } from 'date-fns';
import React from 'react';
import { Provider as ReduxProvider } from 'react-redux';

import { MOTE_TYPE } from '../../../../constant';
import reducer from '../../../../reducer';
import MoteAktivitetForm from './MoteAktivitetForm';
import { expect } from 'vitest';
import { Kanal } from '../../../../datatypes/aktivitetTypes';

const initialState: any = {
data: {
Expand All @@ -22,10 +25,32 @@ function mountWithIntl(node: any): RenderResult {
return render(<ReduxProvider store={store}>{node}</ReduxProvider>);
}

const fillForm = () => {
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Tema for møtet (obligatorisk)'), {
target: { value: 'Møte med NAV' },
});
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Dato (obligatorisk)'), {
target: { value: '21.09.2023' },
});
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Klokkeslett (obligatorisk)'), {
target: { value: '08:00' },
});
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Møteform (obligatorisk)'), {
target: { value: Kanal.TELEFON },
});
fireEvent.change(
screen.getByLabelText<HTMLInputElement>('Møtested eller annen praktisk informasjon (obligatorisk)'),
{ target: { value: 'Kontor' } },
);
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Hensikt med møtet (obligatorisk)'), {
target: { value: 'Møte med NAV' },
});
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Forberedelser til møtet (valgfri)'));
};
describe('MoteAktivitetForm', () => {
it.skip('Skal vise error summary når man submitter uten å oppgi påkrevde verdier', async () => {
const { queryByText, getByText, getByRole } = mountWithIntl(
<MoteAktivitetForm onSubmit={() => Promise.resolve()} isDirtyRef={dirtyRef} />
<MoteAktivitetForm onSubmit={() => Promise.resolve()} isDirtyRef={dirtyRef} />,
);
expect(queryByText('For å gå videre må du rette opp følgende')).toBeNull();

Expand Down Expand Up @@ -56,7 +81,7 @@ describe('MoteAktivitetForm', () => {
isDirtyRef={dirtyRef}
aktivitet={aktivitet as any}
/>
</ReduxProvider>
</ReduxProvider>,
);

fireEvent.click(screen.getByText('Lagre'));
Expand All @@ -76,7 +101,7 @@ describe('MoteAktivitetForm', () => {
onSubmit={() => new Promise(() => null)}
isDirtyRef={dirtyRef}
aktivitet={aktivitet as any}
/>
/>,
);

fireEvent.click(screen.getByText('Lagre'));
Expand Down Expand Up @@ -108,6 +133,61 @@ describe('MoteAktivitetForm', () => {
screen.getByDisplayValue('Dette er en beskrivelse');
});

it.skip('Skal validere form', async () => {
const mock = vi.fn();
mountWithIntl(<MoteAktivitetForm onSubmit={mock} isDirtyRef={dirtyRef} />);

fillForm();
fireEvent.change(screen.getByLabelText<HTMLInputElement>('Varighet (obligatorisk)'), {
target: { value: '30' },
});
await act(async () => {
fireEvent.click(screen.getByText('Lagre'));
});

expect(mock).toHaveBeenCalledWith({
adresse: 'Kontor',
avtalt: false,
beskrivelse: 'Møte med NAV',
dato: new Date('2023-09-20T22:00:00.000Z'),
forberedelser: '',
fraDato: '2023-09-21T06:00:00.000Z',
kanal: 'TELEFON',
klokkeslett: '08:00',
status: 'PLANLAGT',
tilDato: '2023-09-21T06:30:00.000Z',
tittel: 'Møte med NAV',
varighet: 30,
});
});

it.skip('Skal selekte riktig varighet', async () => {
const mock = vi.fn();
mountWithIntl(<MoteAktivitetForm onSubmit={mock} isDirtyRef={dirtyRef} />);

fillForm();
await userEvent.selectOptions(screen.getByLabelText('Varighet (obligatorisk)'), '2 timer, 30 minutter');

await act(async () => {
fireEvent.click(screen.getByText('Lagre'));
});

expect(mock).toHaveBeenCalledWith({
adresse: 'Kontor',
avtalt: false,
beskrivelse: 'Møte med NAV',
dato: new Date('2023-09-20T22:00:00.000Z'),
forberedelser: '',
fraDato: '2023-09-21T06:00:00.000Z',
kanal: 'TELEFON',
klokkeslett: '08:00',
status: 'PLANLAGT',
tilDato: '2023-09-21T08:30:00.000Z',
tittel: 'Møte med NAV',
varighet: 150,
});
});

it('Skal være disablede felter ved endring av aktivitet', () => {
const aktivitet = {
tittel: 'Dette er en test',
Expand All @@ -126,7 +206,8 @@ describe('MoteAktivitetForm', () => {
expect(screen.getByLabelText<HTMLInputElement>('Varighet (obligatorisk)').disabled).not.toBeTruthy();
expect(screen.getByLabelText<HTMLInputElement>('Møteform (obligatorisk)').disabled).not.toBeTruthy();
expect(
screen.getByLabelText<HTMLInputElement>('Møtested eller annen praktisk informasjon (obligatorisk)').disabled
screen.getByLabelText<HTMLInputElement>('Møtested eller annen praktisk informasjon (obligatorisk)')
.disabled,
).not.toBeTruthy();
expect(screen.getByLabelText<HTMLInputElement>('Hensikt med møtet (obligatorisk)').disabled).toBeTruthy();
expect(screen.getByLabelText<HTMLInputElement>('Forberedelser til møtet (valgfri)').disabled).toBeTruthy();
Expand Down
13 changes: 10 additions & 3 deletions src/moduler/aktivitet/aktivitet-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ export function beregnFraTil(data: MoteTid): FraTil {
if (dato && klokkeslett && validKlokkeslett(klokkeslett) && varighet !== undefined && varighet !== null) {
const { hour, minute } = toHourAndMinutes(klokkeslett);
const fraDato = setMinutes(setHours(startOfDay(toDate(dato)), hour), minute);
const { hour: varighetHours, minute: varighetMinutes } = toHourAndMinutes(varighet);
const tilDato = addMinutes(fraDato, varighetHours * 60 + varighetMinutes);
const tilDato = addMinutes(fraDato, varighet);
return {
fraDato: fraDato.toISOString(),
tilDato: tilDato.toISOString(),
Expand All @@ -176,7 +175,15 @@ export function formatterVarighet(varighet?: string | number): string | undefine
if (!varighet) return undefined;
if (typeof varighet === 'number' || !isNaN(parseInt(varighet))) {
const { hour, minute } = toHourAndMinutes(varighet);
return `${prefixMed0(hour.toString())}:${prefixMed0(minute.toString())}`;
if (hour > 0) {
if (minute > 0) {
return `${hour.toString()} ${hour === 1 ? 'time' : 'timer'}, ${prefixMed0(minute.toString())} minutter`;
} else {
return `${hour.toString()} ${hour === 1 ? 'time' : 'timer'}`;
}
} else {
return `${prefixMed0(minute.toString())} minutter`;
}
} else {
// Assuming this is correctly formatted "HH:ss"
return varighet;
Expand Down