Skip to content

Commit c04d3c7

Browse files
committed
feat: implement questionnaire creation
1 parent e61a80e commit c04d3c7

File tree

8 files changed

+106
-36
lines changed

8 files changed

+106
-36
lines changed

apps/backend/src/research/questionnaires/questionnaire.dto.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApiProperty, OmitType, PartialType } from "@nestjs/swagger";
22
import { Type } from "class-transformer";
3-
import { IsDate, IsNotEmpty } from "class-validator";
3+
import { IsDateString, IsNotEmpty } from "class-validator";
44
import { ParticipantDto } from "../participants/participant.dto";
55
import { StudyDto } from "../studies/study.dto";
66

@@ -9,11 +9,11 @@ export class QuestionnaireDto {
99
id: number;
1010

1111
@ApiProperty({ example: "2024-11-01T07:00:00.000Z", description: "The starting date of the questionnaire" })
12-
@IsDate()
12+
@IsDateString()
1313
startedAt?: Date;
1414

1515
@ApiProperty({ example: "2024-11-01T08:00:00.00Z", description: "The ending date of the questionnaire" })
16-
@IsDate()
16+
@IsDateString()
1717
endedAt?: Date;
1818

1919
@ApiProperty({ example: "First few months", description: "The title of the questionnaire" })
@@ -33,5 +33,8 @@ export class QuestionnaireDto {
3333
entries?: number[];
3434
}
3535
export class QuestionnaireResponseDto extends QuestionnaireDto {}
36-
export class QuestionnaireCreationDto extends OmitType(QuestionnaireDto, ["id"]) {}
36+
export class QuestionnaireCreationDto extends OmitType(QuestionnaireDto, ["id", "study", "participant"]) {
37+
study: number;
38+
participant: number;
39+
}
3740
export class QuestionnaireMutationDto extends PartialType(QuestionnaireCreationDto) {}

apps/backend/src/research/questionnaires/questionnaires.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class QuestionnairesService {
1414

1515
async create(questionnaireCreationDto: QuestionnaireCreationDto) {
1616
const questionnaire = new Questionnaire();
17-
questionnaire.assign(questionnaireCreationDto);
17+
questionnaire.assign(questionnaireCreationDto, { em: this.em });
1818

1919
try {
2020
await this.em.persist(questionnaire).flush();

apps/frontend/src/api.gen.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,8 @@ export interface components {
750750
* @example We went on holidays for 2 weeks and only spoke Esperanto
751751
*/
752752
remark?: string;
753-
study?: components["schemas"]["StudyDto"];
754-
participant?: components["schemas"]["ParticipantDto"];
753+
study: number;
754+
participant: number;
755755
entries?: number[];
756756
};
757757
QuestionnaireResponseDto: {
@@ -809,9 +809,9 @@ export interface components {
809809
* @example We went on holidays for 2 weeks and only spoke Esperanto
810810
*/
811811
remark?: string;
812-
study?: components["schemas"]["StudyDto"];
813-
participant?: components["schemas"]["ParticipantDto"];
814812
entries?: number[];
813+
study?: number;
814+
participant?: number;
815815
};
816816
LanguageDto: {
817817
/**
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useForm } from "@mantine/form";
2+
import { Button, Flex, MonthPicker, Stack, TextInput } from "@quassel/ui";
3+
import { i18n } from "../../stores/i18n";
4+
import { useStore } from "@nanostores/react";
5+
import { components } from "../../api.gen";
6+
7+
type FormValues = {
8+
title: string;
9+
range: [Date, Date];
10+
};
11+
12+
type ResultType = Pick<components["schemas"]["QuestionnaireMutationDto"], "startedAt" | "endedAt" | "title">;
13+
14+
type PeriodFormProps = {
15+
onSave: (questionnaire: ResultType) => void;
16+
actionLabel: string;
17+
};
18+
19+
const mapValues = ({ range: [startedAt, endedAt], ...rest }: FormValues): ResultType => ({
20+
...rest,
21+
startedAt: startedAt.toISOString(),
22+
endedAt: endedAt.toISOString(),
23+
});
24+
25+
export const messages = i18n("periodForm", {
26+
labelTitle: "Title",
27+
});
28+
29+
export function PeriodForm({ onSave, actionLabel }: PeriodFormProps) {
30+
const f = useForm<FormValues>({});
31+
const t = useStore(messages);
32+
33+
return (
34+
<form onSubmit={f.onSubmit((values) => onSave(mapValues(values)))}>
35+
<Stack>
36+
<Flex justify="center">
37+
<MonthPicker {...f.getInputProps("range")} size="md" type="range" numberOfColumns={2} />
38+
</Flex>
39+
<TextInput {...f.getInputProps("title")} label={t.labelTitle} />
40+
<Button type="submit">{actionLabel}</Button>
41+
</Stack>
42+
</form>
43+
);
44+
}

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/new.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { Button } from "@quassel/ui";
21
import { createFileRoute, useNavigate } from "@tanstack/react-router";
32
import { i18n } from "../../../../stores/i18n";
43
import { useStore } from "@nanostores/react";
4+
import { PeriodForm } from "../../../../components/questionnaire/PeriodForm";
5+
import { $api } from "../../../../stores/api";
6+
import { $questionnaire } from "../../../../stores/questionnaire";
57

68
export const messages = i18n("questionnaireNew", {
79
title: "Create new period of life",
@@ -12,19 +14,25 @@ function QuestionnaireNew() {
1214
const n = useNavigate();
1315
const t = useStore(messages);
1416

15-
const handleSubmit = () => {
16-
// TODO create new questionnaire and receive ID
17+
const questionnaire = useStore($questionnaire);
1718

18-
n({ to: "/questionnaire/$id/entries", params: { id: "123" } });
19-
};
19+
const createQuestionnaireMutation = $api.useMutation("post", "/questionnaires", {
20+
onSuccess: (questionnaire) => {
21+
n({ to: "/questionnaire/$id/entries", params: { id: questionnaire.id.toString() } });
22+
},
23+
});
2024

2125
return (
2226
<>
2327
<h3>{t.title}</h3>
24-
<form onSubmit={handleSubmit}>
25-
{/* TODO period form */}
26-
<Button type="submit">{t.formAction}</Button>
27-
</form>
28+
<PeriodForm
29+
onSave={(period) =>
30+
createQuestionnaireMutation.mutate({
31+
body: { ...period, study: questionnaire!.study.id, participant: questionnaire!.participant.id },
32+
})
33+
}
34+
actionLabel={t.formAction}
35+
/>
2836
</>
2937
);
3038
}

libs/ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
},
2828
"dependencies": {
2929
"@mantine/core": "^7.13.5",
30+
"@mantine/dates": "^7.14.1",
3031
"@mantine/hooks": "^7.13.5",
3132
"@tabler/icons-react": "3.17.0",
3233
"react": "^18.3.1",

libs/ui/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import "@mantine/core/styles/Title.css";
2727
import "@mantine/core/styles/ActionIcon.css";
2828
import "@mantine/core/styles/Combobox.css";
2929
import "@mantine/core/styles/Stack.css";
30+
import "@mantine/dates/styles.css";
3031

3132
export { ThemeProvider } from "./theme/ThemeProvider";
3233

@@ -41,6 +42,7 @@ export {
4142
Checkbox,
4243
Container,
4344
Divider,
45+
Flex,
4446
Group,
4547
NavLink,
4648
Paper,
@@ -55,6 +57,8 @@ export {
5557
useMantineTheme,
5658
} from "@mantine/core";
5759

60+
export { MonthPicker } from "@mantine/dates";
61+
5862
export {
5963
IconLogout,
6064
IconUsers,

pnpm-lock.yaml

Lines changed: 28 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)