Skip to content

Commit

Permalink
Handle sample form (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrivals committed Mar 13, 2024
1 parent ae7bbf2 commit ed0eb37
Show file tree
Hide file tree
Showing 25 changed files with 765 additions and 143 deletions.
3 changes: 2 additions & 1 deletion database/migrations/003-samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ exports.up = async (knex: Knex) => {
table.string('matrix');
table.string('matrix_kind');
table.string('matrix_part');
table.string('stage');
table.double('quantity');
table.string('quantity_unit');
table.string('culture_kind');
table.boolean('compliance_2002_63');
table.boolean('compliance200263');
table.string('storage_condition');
table.boolean('pooling');
table.boolean('release_control');
Expand Down
18 changes: 11 additions & 7 deletions database/seeds/test/001-users.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import bcrypt from 'bcryptjs';
import { Knex } from 'knex';
import { UserApi } from '../../../server/models/UserApi';
import userRepository, {
usersTable,
} from '../../../server/repositories/userRepository';
import { usersTable } from '../../../server/repositories/userRepository';
import { genUserApi } from '../../../server/test/testFixtures';

export const User1: UserApi = genUserApi();
export const User2: UserApi = genUserApi();

exports.seed = async function (knex: Knex) {
const hash = await bcrypt.hash(User1.password, 10);
const hash1 = await bcrypt.hash(User1.password, 10);
const hash2 = await bcrypt.hash(User2.password, 10);
await knex.table(usersTable).insert([
userRepository.formatUserApi({
{
...User1,
password: hash,
}),
password: hash1,
},
{
...User2,
password: hash2,
},
]);
};
12 changes: 12 additions & 0 deletions database/seeds/test/002-samples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import sampleRepository from '../../../server/repositories/sampleRepository';
import { genSample } from '../../../server/test/testFixtures';
import { Sample } from '../../../shared/schema/Sample';
import { User1, User2 } from './001-users';

export const Sample1: Sample = genSample(User1.id);
export const Sample2: Sample = genSample(User2.id);

exports.seed = async function () {
await sampleRepository.insert(Sample1);
await sampleRepository.insert(Sample2);
};
2 changes: 1 addition & 1 deletion frontend/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const Header = () => {
to: '/prelevements',
target: '_self',
},
text: 'Prélèvements',
text: 'Mes prélèvements',
isActive: location.pathname.startsWith('/prelevements'),
},
]
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/hooks/useAuthentication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export const useAuthentication = () => {
key: 'sample_route',
component: SampleView,
},
{
path: '/prelevements/:sampleId',
label: 'Prélèvement',
key: 'sample_route',
component: SampleView,
},
]
: [
{
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ i18n.use(initReactI18next).init({
fallbackLng: 'fr',
resources: {
fr: {
translation: {},
translation: {
sample_zero: 'Aucun prélèvement',
sample_one: 'Un prélèvement',
sample_other: '{{count}} prélèvements',
},
},
},
});
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/services/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export const api = createApi({
baseUrl: `${config.apiEndpoint}/api`,
prepareHeaders: withAuthHeader,
}),
tagTypes: ['User'],
tagTypes: ['Sample', 'User'],
endpoints: () => ({}),
});
43 changes: 40 additions & 3 deletions frontend/src/services/sample.service.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
import { SampleToCreate } from 'shared/schema/Sample';
import fp from 'lodash';
import {
PartialSample,
PartialSampleUpdate,
SampleToCreate,
} from 'shared/schema/Sample';
import { api } from 'src/services/api.service';

export const sampleApi = api.injectEndpoints({
endpoints: (builder) => ({
createSample: builder.mutation<void, SampleToCreate>({
createSample: builder.mutation<string, SampleToCreate>({
query: (draft) => ({
url: 'samples',
method: 'POST',
body: { ...draft },
}),
transformResponse: (response: any) => response.id,
}),
getSample: builder.query<PartialSample, string>({
query: (sampleId) => `samples/${sampleId}`,
transformResponse: (response: any) =>
PartialSample.parse(fp.omitBy(response, fp.isNil)),
providesTags: (result, error, sampleId) =>
result ? [{ type: 'Sample', id: sampleId }] : [],
}),
findSamples: builder.query<PartialSample[], void>({
query: () => 'samples',
transformResponse: (response: any[]) =>
response.map((_) => PartialSample.parse(fp.omitBy(_, fp.isNil))),
providesTags: (result) =>
result ? result.map(({ id }) => ({ type: 'Sample', id })) : [],
}),
updateSample: builder.mutation<
void,
{ sampleId: string; sampleUpdate: PartialSampleUpdate }
>({
query: ({ sampleId, sampleUpdate }) => ({
url: `samples/${sampleId}`,
method: 'PUT',
body: sampleUpdate,
}),
invalidatesTags: (result, error, { sampleId }) =>
result ? [{ type: 'Sample', id: sampleId }] : [],
}),
}),
});

export const { useCreateSampleMutation } = sampleApi;
export const {
useCreateSampleMutation,
useFindSamplesQuery,
useGetSampleQuery,
useUpdateSampleMutation,
} = sampleApi;
25 changes: 23 additions & 2 deletions frontend/src/views/SampleListView/SampleListView.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import Button from '@codegouvfr/react-dsfr/Button';
import { cx } from '@codegouvfr/react-dsfr/fr/cx';
import Table from '@codegouvfr/react-dsfr/Table';
import { format } from 'date-fns';
import { t } from 'i18next';
import { Link } from 'react-router-dom';
import { useDocumentTitle } from 'src/hooks/useDocumentTitle';
import { useFindSamplesQuery } from 'src/services/sample.service';

const SampleListView = () => {
useDocumentTitle('Liste des prélèvements');
useDocumentTitle('Liste de mes prélèvements');

const { data: samples } = useFindSamplesQuery();

return (
<section className={cx('fr-py-6w')}>
<h1>Liste des prélèvements</h1>
<h1>Mes prélèvements</h1>
<div className={cx('fr-mb-4w')}>
{t('sample', { count: samples?.length || 0 })}
</div>
{samples && samples.length > 0 && (
<Table
noCaption
headers={['Identifiant', 'Date de création']}
data={samples.map((sample) => [
<Link to={`/prelevements/${sample.id}`}>{sample.reference}</Link>,
format(sample.createdAt, 'dd/MM/yyyy'),
])}
/>
)}
<Button
linkProps={{
to: '/prelevements/nouveau',
target: '_self',
}}
className={cx('fr-mt-4w')}
>
Créer un prélèvement
</Button>
Expand Down
27 changes: 19 additions & 8 deletions frontend/src/views/SampleView/SampleFormStep1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Button from '@codegouvfr/react-dsfr/Button';
import { cx } from '@codegouvfr/react-dsfr/fr/cx';
import Select from '@codegouvfr/react-dsfr/Select';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Department,
DepartmentLabels,
Expand All @@ -18,17 +19,20 @@ import AppSelect from 'src/components/_app/AppSelect/AppSelect';
import { selectOptionsFromList } from 'src/components/_app/AppSelect/AppSelectOption';
import AppTextInput from 'src/components/_app/AppTextInput/AppTextInput';
import { useForm } from 'src/hooks/useForm';
import { useCreateSampleMutation } from 'src/services/sample.service';

interface Props {
onValid: (draftSample: SampleToCreate) => void;
}
interface Props {}

const SampleFormStep1 = ({}: Props) => {
const navigate = useNavigate();

const SampleFormStep1 = ({ onValid }: Props) => {
const [resytalId, setResytalId] = useState('');
const [context, setContext] = useState<SampleContext>();
const [userLocation, setUserLocation] = useState<UserLocation>();
const [department, setDepartment] = useState<Department>();

const [createSample] = useCreateSampleMutation();

const Form = SampleToCreate;

const form = useForm(Form, {
Expand All @@ -52,13 +56,20 @@ const SampleFormStep1 = ({ onValid }: Props) => {

const submit = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
await form.validate(() => {
onValid({
await form.validate(async () => {
await createSample({
userLocation: userLocation as UserLocation,
resytalId,
context: context as SampleContext,
department: department as Department,
});
})
.unwrap()
.then((result) => {
navigate(`/prelevements/${result}`, { replace: true });
})
.catch(() => {
//TODO handle error
});
});
};

Expand Down Expand Up @@ -127,7 +138,7 @@ const SampleFormStep1 = ({ onValid }: Props) => {
nativeSelectProps={{ defaultValue: '' }}
disabled={true}
>
<option value="" disabled selected>
<option value="" disabled defaultValue="">
TODO ?
</option>
</Select>
Expand Down
Loading

0 comments on commit ed0eb37

Please sign in to comment.