Skip to content

Commit 6740baa

Browse files
authored
Merge pull request #383 from openscript-ch/322-select-multiple-days-with-the-same-pattern
322 select multiple days with the same pattern
2 parents fd9e2bd + 7866bfd commit 6740baa

File tree

10 files changed

+130
-122
lines changed

10 files changed

+130
-122
lines changed

.changeset/strong-bottles-tease.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@quassel/frontend": minor
3+
"@quassel/backend": minor
4+
---
5+
6+
Allow creating entries for multiple days

apps/backend/src/research/entries/entries.controller.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestj
22
import { ApiTags, ApiOperation, ApiUnprocessableEntityResponse, ApiQuery } from "@nestjs/swagger";
33
import { ErrorResponseDto } from "../../common/dto/error.dto";
44
import { EntriesService } from "./entries.service";
5-
import { EntryCreationDto, EntryResponseDto, EntryMutationDto } from "./entry.dto";
5+
import { EntryCreationDto, EntryResponseDto, EntryUpdateDto } from "./entry.dto";
66
import { Serialize } from "../../common/decorators/serialize";
77

88
@ApiTags("Entries")
@@ -13,8 +13,7 @@ export class EntriesController {
1313
@Post()
1414
@ApiOperation({ summary: "Create a entry" })
1515
@ApiUnprocessableEntityResponse({ description: "Unique name constraint violation", type: ErrorResponseDto })
16-
@Serialize(EntryResponseDto)
17-
create(@Body() entry: EntryCreationDto): Promise<EntryResponseDto> {
16+
create(@Body() entry: EntryCreationDto): Promise<number[]> {
1817
return this.entriesService.create(entry);
1918
}
2019

@@ -35,7 +34,7 @@ export class EntriesController {
3534
@Patch(":id")
3635
@ApiOperation({ summary: "Update a entry by ID" })
3736
@Serialize(EntryResponseDto)
38-
update(@Param("id") id: string, @Body() entry: EntryMutationDto): Promise<EntryResponseDto> {
37+
update(@Param("id") id: string, @Body() entry: EntryUpdateDto): Promise<EntryResponseDto> {
3938
return this.entriesService.update(+id, entry);
4039
}
4140

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

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { EntityRepository, UniqueConstraintViolationException, FilterQuery } from "@mikro-orm/core";
1+
import { EntityRepository, FilterQuery } from "@mikro-orm/core";
22
import { InjectRepository } from "@mikro-orm/nestjs";
3-
import { Injectable, UnprocessableEntityException } from "@nestjs/common";
4-
import { EntryCreationDto, EntryMutationDto } from "./entry.dto";
3+
import { Injectable } from "@nestjs/common";
4+
import { EntryCreationDto, EntryUpdateDto } from "./entry.dto";
55
import { Entry } from "./entry.entity";
66
import { EntityManager, raw } from "@mikro-orm/postgresql";
77

@@ -13,20 +13,15 @@ export class EntriesService {
1313
private readonly em: EntityManager
1414
) {}
1515

16-
async create(entryCreationDto: EntryCreationDto) {
17-
const entry = new Entry();
18-
entry.assign(entryCreationDto, { em: this.em });
16+
create({ weekday, ...rest }: EntryCreationDto) {
17+
return this.entryRepository.insertMany(
18+
weekday.map((w) => {
19+
const entry = new Entry();
20+
entry.assign({ weekday: w, ...rest }, { em: this.em });
1921

20-
try {
21-
await this.em.persist(entry).flush();
22-
} catch (e) {
23-
if (e instanceof UniqueConstraintViolationException) {
24-
throw new UnprocessableEntityException("Entry with this name already exists");
25-
}
26-
throw e;
27-
}
28-
29-
return (await entry.populate(["entryLanguages"])).toObject();
22+
return entry;
23+
})
24+
);
3025
}
3126

3227
async findAll() {
@@ -66,7 +61,7 @@ export class EntriesService {
6661
});
6762
}
6863

69-
async update(id: number, entryMutationDto: EntryMutationDto) {
64+
async update(id: number, entryMutationDto: EntryUpdateDto) {
7065
const entry = await this.entryRepository.findOneOrFail(id, { populate: ["entryLanguages"] });
7166

7267
entry.assign(entryMutationDto);

apps/backend/src/research/entries/entry.dto.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ class EntryBaseDto {
1515
@Expose()
1616
endedAt: string;
1717

18-
@ApiProperty({ example: 1, description: "The weekday of the entry (Sunday is 0 like in JS)" })
19-
@Min(0)
20-
@Max(6)
21-
@Expose()
22-
weekday: number;
23-
2418
@ApiProperty({ example: 1, description: "The weekly recurring of the entry" })
2519
@Min(1)
2620
@IsOptional()
@@ -32,6 +26,12 @@ export class EntryResponseDto extends EntryBaseDto {
3226
@Expose()
3327
id: number;
3428

29+
@ApiProperty({ example: 1, description: "The weekday of the entry (Sunday is 0 like in JS)" })
30+
@Min(0)
31+
@Max(6)
32+
@Expose()
33+
weekday: number;
34+
3535
@Type(() => CarerResponseDto)
3636
@Expose()
3737
carer: CarerResponseDto;
@@ -41,14 +41,17 @@ export class EntryResponseDto extends EntryBaseDto {
4141
entryLanguages: Array<EntryLanguageResponseDto>;
4242
}
4343

44-
export class EntryCreationDto extends EntryBaseDto {
44+
class EntryMutationDto extends EntryBaseDto {
4545
carer: number;
4646
questionnaire: number;
4747

4848
@Type(() => EntryLanguageCreationDto)
4949
entryLanguages: Array<EntryLanguageCreationDto>;
5050
}
51-
export class EntryMutationDto extends PartialType(EntryCreationDto) {}
51+
export class EntryCreationDto extends EntryMutationDto {
52+
weekday: number[];
53+
}
54+
export class EntryUpdateDto extends PartialType(EntryMutationDto) {}
5255

5356
export class EntryTemplateDto {
5457
@Type(() => CarerResponseDto)

apps/frontend/src/api.gen.ts

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -741,16 +741,12 @@ export interface components {
741741
* @example 2024-11-01T08:00:00.00Z
742742
*/
743743
endedAt: string;
744-
/**
745-
* @description The weekday of the entry (Sunday is 0 like in JS)
746-
* @example 1
747-
*/
748-
weekday: number;
749744
/**
750745
* @description The weekly recurring of the entry
751746
* @example 1
752747
*/
753748
weeklyRecurring?: number;
749+
weekday: number[];
754750
carer: number;
755751
questionnaire: number;
756752
entryLanguages: components["schemas"]["EntryLanguageCreationDto"][];
@@ -766,11 +762,6 @@ export interface components {
766762
* @example 2024-11-01T08:00:00.00Z
767763
*/
768764
endedAt: string;
769-
/**
770-
* @description The weekday of the entry (Sunday is 0 like in JS)
771-
* @example 1
772-
*/
773-
weekday: number;
774765
/**
775766
* @description The weekly recurring of the entry
776767
* @example 1
@@ -781,10 +772,15 @@ export interface components {
781772
* @example 1
782773
*/
783774
id: number;
775+
/**
776+
* @description The weekday of the entry (Sunday is 0 like in JS)
777+
* @example 1
778+
*/
779+
weekday: number;
784780
carer: components["schemas"]["CarerResponseDto"];
785781
entryLanguages: components["schemas"]["EntryLanguageResponseDto"][];
786782
};
787-
EntryMutationDto: {
783+
EntryUpdateDto: {
788784
/**
789785
* @description The starting date of the entry
790786
* @example 2024-11-01T07:00:00.000Z
@@ -795,11 +791,6 @@ export interface components {
795791
* @example 2024-11-01T08:00:00.00Z
796792
*/
797793
endedAt?: string;
798-
/**
799-
* @description The weekday of the entry (Sunday is 0 like in JS)
800-
* @example 1
801-
*/
802-
weekday?: number;
803794
/**
804795
* @description The weekly recurring of the entry
805796
* @example 1
@@ -1829,7 +1820,7 @@ export interface operations {
18291820
[name: string]: unknown;
18301821
};
18311822
content: {
1832-
"application/json": components["schemas"]["EntryResponseDto"];
1823+
"application/json": number[];
18331824
};
18341825
};
18351826
/** @description Unique name constraint violation */
@@ -1915,7 +1906,7 @@ export interface operations {
19151906
};
19161907
requestBody: {
19171908
content: {
1918-
"application/json": components["schemas"]["EntryMutationDto"];
1909+
"application/json": components["schemas"]["EntryUpdateDto"];
19191910
};
19201911
};
19211912
responses: {
Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,53 @@
1-
import { SegmentedControl } from "@quassel/ui";
1+
import { Chip, Group, Select } from "@quassel/ui";
22
import { i18n } from "../stores/i18n";
33
import { useStore } from "@nanostores/react";
44

5-
type WeekdayPickerProps = {
6-
value?: number;
7-
onChange?: (weekday: number) => void;
8-
};
5+
type WeekdayPickerProps =
6+
| { value?: number; onChange?: (weekday?: number) => void; multiple?: false }
7+
| { value?: number[]; onChange?: (weekday: number[]) => void; multiple: true };
98

109
const messages = i18n("WeekdayPicker", {
11-
mondayLabel: "Mo",
12-
tusedayLabel: "Tu",
13-
wednesdayLabel: "We",
14-
thrusdayLabel: "Th",
15-
fridayLabel: "Fr",
16-
saturdayLabel: "Sa",
17-
sundayLabel: "Su",
10+
mondayShortLabel: "Mo",
11+
mondayLabel: "Monday",
12+
tusedayShortLabel: "Tu",
13+
tusedayLabel: "Tuesday",
14+
wednesdayShortLabel: "We",
15+
wednesdayLabel: "Wednesday",
16+
thursdayShortLabel: "Th",
17+
thursdayLabel: "Thursday",
18+
fridayShortLabel: "Fr",
19+
fridayLabel: "Friday",
20+
saturdayShortLabel: "Sa",
21+
saturdayLabel: "Saturday",
22+
sundayShortLabel: "Su",
23+
sundayLabel: "Sunday",
1824
});
1925

20-
export function WeekdayPicker({ onChange, value }: WeekdayPickerProps) {
26+
export function WeekdayPicker({ onChange, value, multiple }: WeekdayPickerProps) {
2127
const t = useStore(messages);
2228

23-
return (
24-
<SegmentedControl
25-
value={value?.toString()}
26-
onChange={(value) => onChange?.(parseInt(value))}
27-
data={[
28-
{ value: "1", label: t.mondayLabel },
29-
{ value: "2", label: t.tusedayLabel },
30-
{ value: "3", label: t.wednesdayLabel },
31-
{ value: "4", label: t.thrusdayLabel },
32-
{ value: "5", label: t.fridayLabel },
33-
{ value: "6", label: t.saturdayLabel },
34-
{ value: "0", label: t.sundayLabel },
35-
]}
36-
/>
37-
);
29+
const weekdayOptions = [
30+
{ value: "1", label: t.mondayLabel, short: t.mondayShortLabel },
31+
{ value: "2", label: t.tusedayLabel, short: t.tusedayShortLabel },
32+
{ value: "3", label: t.wednesdayLabel, short: t.wednesdayShortLabel },
33+
{ value: "4", label: t.thursdayLabel, short: t.thursdayShortLabel },
34+
{ value: "5", label: t.fridayLabel, short: t.fridayShortLabel },
35+
{ value: "6", label: t.saturdayLabel, short: t.saturdayShortLabel },
36+
{ value: "0", label: t.sundayLabel, short: t.sundayShortLabel },
37+
];
38+
39+
if (multiple)
40+
return (
41+
<Chip.Group multiple value={value?.map(String)} onChange={(values) => onChange?.(values.map(Number))}>
42+
<Group gap={"xs"}>
43+
{weekdayOptions.map(({ value, short }) => (
44+
<Chip key={value} value={value}>
45+
{short}
46+
</Chip>
47+
))}
48+
</Group>
49+
</Chip.Group>
50+
);
51+
52+
return <Select data={weekdayOptions} value={value?.toString()} onChange={(v) => onChange?.(v ? parseInt(v) : undefined)} />;
3853
}

apps/frontend/src/components/questionnaire/QuestionnaireEntries.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function QuestionnaireEntries({ questionnaire, gaps }: QuestionnaireEntri
6262
const handleCreate = (entry: EntryFormValues) => {
6363
const entryRequest = { ...entry, questionnaire: questionnaire.id };
6464

65-
return createMutation.mutateAsync({ body: entryRequest }, { onSuccess: reloadEntries });
65+
return createMutation.mutateAsync({ body: entryRequest as components["schemas"]["EntryCreationDto"] }, { onSuccess: reloadEntries });
6666
};
6767

6868
const handleUpdate = (id: number, entry: Partial<EntryFormValues>) => {

0 commit comments

Comments
 (0)