Skip to content

Commit 00e6eb1

Browse files
authored
Merge pull request #201 from openscript-ch/198-improve-calendar-entries-display
198 improve calendar entries display
2 parents fd32ba2 + 26fa90f commit 00e6eb1

File tree

17 files changed

+199
-89
lines changed

17 files changed

+199
-89
lines changed

.changeset/fuzzy-avocados-provide.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@quassel/frontend": patch
3+
"@quassel/backend": patch
4+
"@quassel/ui": patch
5+
---
6+
7+
Introduce color field per carer for calendar entries

.changeset/thirty-beers-matter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@quassel/frontend": patch
3+
---
4+
5+
Enhance content displayed on calendar entries

apps/backend/db/migrations/.snapshot-postgres.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@
156156
"length": 255,
157157
"mappedType": "string"
158158
},
159+
"color": {
160+
"name": "color",
161+
"type": "varchar(255)",
162+
"unsigned": false,
163+
"autoincrement": false,
164+
"primary": false,
165+
"nullable": true,
166+
"length": 255,
167+
"mappedType": "string"
168+
},
159169
"participant_id": {
160170
"name": "participant_id",
161171
"type": "bigint",
@@ -190,7 +200,14 @@
190200
"unique": true
191201
}
192202
],
193-
"checks": [],
203+
"checks": [
204+
{
205+
"name": "carer_color_check",
206+
"expression": "color ~* '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$'",
207+
"definition": "check ((color ~* '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$'))",
208+
"columnName": "color"
209+
}
210+
],
194211
"foreignKeys": {
195212
"carer_participant_id_foreign": {
196213
"constraintName": "carer_participant_id_foreign",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Migration } from "@mikro-orm/migrations";
2+
3+
export class Migration20241217101643 extends Migration {
4+
override async up(): Promise<void> {
5+
this.addSql(`alter table "carer" add column "color" varchar(255) null;`);
6+
this.addSql(`alter table "carer" add constraint carer_color_check check(color ~* '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\$');`);
7+
}
8+
9+
override async down(): Promise<void> {
10+
this.addSql(`alter table "carer" drop constraint carer_color_check;`);
11+
this.addSql(`alter table "carer" drop column "color";`);
12+
}
13+
}

apps/backend/src/defaults/carers/carer.dto.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ApiProperty, OmitType, PartialType } from "@nestjs/swagger";
2-
import { IsNotEmpty, MinLength } from "class-validator";
2+
import { IsNotEmpty, Matches, MinLength } from "class-validator";
33
import { Type } from "class-transformer";
44
import { ParticipantDto } from "../../research/participants/participant.dto";
55

@@ -12,6 +12,10 @@ export class CarerDto {
1212
@IsNotEmpty()
1313
name: string;
1414

15+
@Matches("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")
16+
@ApiProperty({ example: "#ffffff", description: "The color used to display entries in the calendar" })
17+
color?: string;
18+
1519
@Type(() => ParticipantDto)
1620
participant?: ParticipantDto;
1721

apps/backend/src/defaults/carers/carer.entity.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Collection, Entity, ManyToOne, OneToMany, Property } from "@mikro-orm/core";
1+
import { Check, Collection, Entity, ManyToOne, OneToMany, Property } from "@mikro-orm/core";
22
import { BaseEntity } from "../../common/entities/base.entity";
33
import { Participant } from "../../research/participants/participant.entity";
44
import { Entry } from "../../research/entries/entry.entity";
@@ -8,6 +8,10 @@ export class Carer extends BaseEntity {
88
@Property({ unique: true })
99
name!: string;
1010

11+
@Check<Carer>({ expression: (columns) => `${columns.color} ~* '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$'` })
12+
@Property()
13+
color?: string;
14+
1115
@ManyToOne()
1216
participant?: Participant;
1317

apps/frontend/src/api.gen.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,11 @@ export interface components {
494494
* @example Grandmother
495495
*/
496496
name: string;
497+
/**
498+
* @description The color used to display entries in the calendar
499+
* @example #ffffff
500+
*/
501+
color?: string;
497502
participant?: number;
498503
};
499504
StudyDto: {
@@ -574,6 +579,11 @@ export interface components {
574579
* @example Grandmother
575580
*/
576581
name: string;
582+
/**
583+
* @description The color used to display entries in the calendar
584+
* @example #ffffff
585+
*/
586+
color?: string;
577587
participant?: components["schemas"]["ParticipantDto"];
578588
entries: number[];
579589
};
@@ -583,6 +593,11 @@ export interface components {
583593
* @example Grandmother
584594
*/
585595
name?: string;
596+
/**
597+
* @description The color used to display entries in the calendar
598+
* @example #ffffff
599+
*/
600+
color?: string;
586601
participant?: number;
587602
};
588603
LanguageCreationDto: {
@@ -758,6 +773,11 @@ export interface components {
758773
* @example Grandmother
759774
*/
760775
name: string;
776+
/**
777+
* @description The color used to display entries in the calendar
778+
* @example #ffffff
779+
*/
780+
color?: string;
761781
participant?: components["schemas"]["ParticipantDto"];
762782
entries: number[];
763783
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Button, ColorInput, TextInput, useForm, uzhColors } from "@quassel/ui";
2+
import { useEffect } from "react";
3+
4+
type CarerFormProps = {
5+
carer?: FormValues;
6+
onSave: (carer: FormValues) => void;
7+
loading: boolean;
8+
};
9+
10+
type FormValues = {
11+
name: string;
12+
color?: string;
13+
};
14+
15+
export function CarerForm({ carer, onSave, loading }: CarerFormProps) {
16+
const f = useForm<FormValues>({
17+
initialValues: {
18+
name: "",
19+
},
20+
});
21+
22+
useEffect(() => {
23+
if (carer) f.initialize({ ...carer, color: carer.color ?? "" });
24+
}, [carer]);
25+
26+
return (
27+
<form autoComplete="off" onSubmit={f.onSubmit(onSave)}>
28+
<TextInput label="Name" type="name" {...f.getInputProps("name")} />
29+
30+
<ColorInput label="Color" {...f.getInputProps("color")} swatchesPerRow={6} swatches={Object.values(uzhColors).flat()} />
31+
32+
<Button type="submit" fullWidth mt="xl" loading={loading}>
33+
Change
34+
</Button>
35+
</form>
36+
);
37+
}
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
import { EventImpl } from "@fullcalendar/core/internal";
22
import { ExtendedEvent } from "../../../routes/_auth/questionnaire/_questionnaire/$id/entries";
3-
import { Flex } from "@quassel/ui";
3+
import { IconRepeat, Stack, Text } from "@quassel/ui";
4+
import { i18n } from "../../../stores/i18n";
5+
import { params } from "@nanostores/i18n";
6+
import { useStore } from "@nanostores/react";
47

58
type QuestionnaireEntryProps = {
69
event: EventImpl;
710
};
811

12+
const messages = i18n("questionnaireEntries", {
13+
labelRecurringWeekly: params("{weeks} weeks"),
14+
});
15+
916
export function QuestionnaireEntry({ event }: QuestionnaireEntryProps) {
17+
const { entryLanguages, weeklyRecurring } = event.extendedProps as ExtendedEvent["extendedProps"];
18+
19+
const t = useStore(messages);
20+
1021
return (
11-
<Flex direction={"column"}>
12-
<span>{event.title}</span>
13-
<span>{(event.extendedProps as ExtendedEvent["extendedProps"]).entryLanguages.map(({ language }) => language.name).join(", ")}</span>
14-
</Flex>
22+
<Stack gap={0}>
23+
<Text size="sm" fw="bold" truncate>
24+
{event.title}
25+
</Text>
26+
{entryLanguages.map(({ language }) => language.ietfBcp47).join(", ")}
27+
{weeklyRecurring && weeklyRecurring > 1 && (
28+
<Text mt="sm" size="sm">
29+
<IconRepeat size={13} /> {t.labelRecurringWeekly({ weeks: weeklyRecurring })}
30+
</Text>
31+
)}
32+
</Stack>
1533
);
1634
}

apps/frontend/src/routes/_auth/administration/carers/edit.$id.tsx

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { createFileRoute, useNavigate } from "@tanstack/react-router";
2-
import { components } from "../../../../api.gen";
32
import { $api } from "../../../../stores/api";
43
import { useQueryClient } from "@tanstack/react-query";
5-
import { Button, TextInput, useForm } from "@quassel/ui";
6-
import { useEffect } from "react";
7-
8-
type FormValues = components["schemas"]["CarerMutationDto"];
4+
import { CarerForm } from "../../../../components/CarerForm";
5+
import { components } from "../../../../api.gen";
96

107
function AdministrationCarersEdit() {
118
const p = Route.useParams();
129
const q = useQueryClient();
13-
const { data, isSuccess } = $api.useSuspenseQuery("get", "/carers/{id}", { params: { path: { id: p.id } } });
10+
const { data } = $api.useSuspenseQuery("get", "/carers/{id}", { params: { path: { id: p.id } } });
1411
const n = useNavigate();
1512
const editCarerMutation = $api.useMutation("patch", "/carers/{id}", {
1613
onSuccess: () => {
@@ -22,34 +19,15 @@ function AdministrationCarersEdit() {
2219
n({ to: "/administration/carers" });
2320
},
2421
});
25-
const f = useForm<FormValues>({
26-
initialValues: {
27-
name: "",
28-
},
29-
});
30-
const handleSubmit = (values: FormValues) => {
22+
23+
const handleSubmit = (values: components["schemas"]["CarerMutationDto"]) => {
3124
editCarerMutation.mutate({
3225
body: { ...values },
3326
params: { path: { id: p.id } },
3427
});
3528
};
3629

37-
useEffect(() => {
38-
f.setValues({ ...data, participant: data.participant?.id });
39-
f.resetDirty();
40-
}, [isSuccess, data]);
41-
42-
return (
43-
<>
44-
<form autoComplete="off" onSubmit={f.onSubmit(handleSubmit)}>
45-
<TextInput label="Name" type="name" {...f.getInputProps("name")} />
46-
47-
<Button type="submit" fullWidth mt="xl" loading={editCarerMutation.isPending}>
48-
Change
49-
</Button>
50-
</form>
51-
</>
52-
);
30+
return <CarerForm carer={data} onSave={handleSubmit} loading={editCarerMutation.isPending} />;
5331
}
5432

5533
export const Route = createFileRoute("/_auth/administration/carers/edit/$id")({

0 commit comments

Comments
 (0)