Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/sixty-wolves-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@quassel/frontend": patch
"@quassel/backend": patch
"@quassel/ui": patch
---

Allow custom carers and languages per participant
6 changes: 6 additions & 0 deletions .changeset/wet-forks-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@quassel/frontend": patch
"@quassel/ui": patch
---

Introduce setting weeklyRecurring for entries
4 changes: 3 additions & 1 deletion apps/backend/src/defaults/carers/carer.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ export class CarerDto {
entries: number[];
}
export class CarerResponseDto extends CarerDto {}
export class CarerCreationDto extends OmitType(CarerDto, ["id", "entries"]) {}
export class CarerCreationDto extends OmitType(CarerDto, ["id", "entries", "participant"]) {
participant?: number;
}
export class CarerMutationDto extends PartialType(CarerCreationDto) {}
9 changes: 5 additions & 4 deletions apps/backend/src/defaults/carers/carers.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Body, Controller, Delete, Get, Param, Patch, Post } from "@nestjs/common";
import { ApiOperation, ApiTags, ApiUnprocessableEntityResponse } from "@nestjs/swagger";
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiOperation, ApiQuery, ApiTags, ApiUnprocessableEntityResponse } from "@nestjs/swagger";
import { CarersService } from "./carers.service";
import { CarerCreationDto, CarerMutationDto, CarerResponseDto } from "./carer.dto";
import { ErrorResponseDto } from "../../common/dto/error.dto";
Expand All @@ -19,9 +19,10 @@
}

@Get()
@ApiQuery({ name: "participantId", required: false, type: Number })
@ApiOperation({ summary: "Get all carers" })
index(): Promise<CarerResponseDto[]> {
return this.carersService.findAll();
index(@Query("participantId") participantId?: number): Promise<CarerResponseDto[]> {
return this.carersService.findAll(participantId);

Check warning on line 25 in apps/backend/src/defaults/carers/carers.controller.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/carers/carers.controller.ts#L25

Added line #L25 was not covered by tests
}

@Get(":id")
Expand Down
10 changes: 7 additions & 3 deletions apps/backend/src/defaults/carers/carers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

async create(carerCreationDto: CarerCreationDto) {
const carer = new Carer();
carer.assign(carerCreationDto);
carer.assign(carerCreationDto, { em: this.em });

Check warning on line 17 in apps/backend/src/defaults/carers/carers.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/carers/carers.service.ts#L17

Added line #L17 was not covered by tests

try {
await this.em.persist(carer).flush();
Expand All @@ -28,8 +28,12 @@
return carer.toObject();
}

async findAll() {
return (await this.carerRepository.findAll()).map((carer) => carer.toObject());
async findAll(participantId?: number) {
return (

Check warning on line 32 in apps/backend/src/defaults/carers/carers.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/carers/carers.service.ts#L31-L32

Added lines #L31 - L32 were not covered by tests
await this.carerRepository.findAll({
where: participantId ? { $or: [{ participant: null }, { participant: participantId }] } : { participant: null },
})
).map((carer) => carer.toObject());

Check warning on line 36 in apps/backend/src/defaults/carers/carers.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/carers/carers.service.ts#L36

Added line #L36 was not covered by tests
}

async findOne(id: number) {
Expand Down
4 changes: 3 additions & 1 deletion apps/backend/src/defaults/languages/language.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ export class LanguageDto {
entryLanguages: number[];
}
export class LanguageResponseDto extends LanguageDto {}
export class LanguageCreationDto extends OmitType(LanguageDto, ["id", "entryLanguages"]) {}
export class LanguageCreationDto extends OmitType(LanguageDto, ["id", "entryLanguages", "participant"]) {
participant?: number;
}
export class LanguageMutationDto extends PartialType(LanguageCreationDto) {}
9 changes: 5 additions & 4 deletions apps/backend/src/defaults/languages/languages.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Body, Controller, Delete, Get, Param, Patch, Post } from "@nestjs/common";
import { ApiOperation, ApiTags, ApiUnprocessableEntityResponse } from "@nestjs/swagger";
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiOperation, ApiQuery, ApiTags, ApiUnprocessableEntityResponse } from "@nestjs/swagger";
import { LanguagesService } from "./languages.service";
import { ErrorResponseDto } from "../../common/dto/error.dto";
import { Roles } from "../../system/users/roles.decorator";
Expand All @@ -19,9 +19,10 @@
}

@Get()
@ApiQuery({ name: "participantId", required: false, type: Number })
@ApiOperation({ summary: "Get all languages" })
index(): Promise<LanguageResponseDto[]> {
return this.languagesService.findAll();
index(@Query("participantId") participantId?: number): Promise<LanguageResponseDto[]> {
return this.languagesService.findAll(participantId);

Check warning on line 25 in apps/backend/src/defaults/languages/languages.controller.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/languages/languages.controller.ts#L25

Added line #L25 was not covered by tests
}

@Get(":id")
Expand Down
10 changes: 7 additions & 3 deletions apps/backend/src/defaults/languages/languages.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

async create(languageCreationDto: LanguageCreationDto) {
const language = new Language();
language.assign(languageCreationDto);
language.assign(languageCreationDto, { em: this.em });

Check warning on line 17 in apps/backend/src/defaults/languages/languages.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/languages/languages.service.ts#L17

Added line #L17 was not covered by tests

try {
await this.em.persist(language).flush();
Expand All @@ -28,8 +28,12 @@
return language.toObject();
}

async findAll() {
return (await this.languageRepository.findAll()).map((language) => language.toObject());
async findAll(participantId?: number) {
return (

Check warning on line 32 in apps/backend/src/defaults/languages/languages.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/languages/languages.service.ts#L31-L32

Added lines #L31 - L32 were not covered by tests
await this.languageRepository.findAll({
where: participantId ? { $or: [{ participant: null }, { participant: participantId }] } : { participant: null },
})
).map((language) => language.toObject());

Check warning on line 36 in apps/backend/src/defaults/languages/languages.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/defaults/languages/languages.service.ts#L36

Added line #L36 was not covered by tests
}

async findOne(id: number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
throw e;
}

return (await questionnaire.populate(["entries", "entries.carer", "entries.entryLanguages.language"])).toObject();
return (await questionnaire.populate(["entries", "entries.carer", "entries.entryLanguages.language", "participant"])).toObject();

Check warning on line 50 in apps/backend/src/research/questionnaires/questionnaires.service.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L50 was not covered by tests
}

async findAll() {
Expand All @@ -56,7 +56,9 @@

async findOne(id: number) {
return (
await this.questionnaireRepository.findOneOrFail(id, { populate: ["entries", "entries.carer", "entries.entryLanguages.language"] })
await this.questionnaireRepository.findOneOrFail(id, {
populate: ["entries", "entries.carer", "entries.entryLanguages.language", "participant"],
})
).toObject();
}

Expand All @@ -70,7 +72,7 @@

async update(id: number, questionnaireMutationDto: QuestionnaireMutationDto) {
const questionnaire = await this.questionnaireRepository.findOneOrFail(id, {
populate: ["entries", "entries.carer", "entries.entryLanguages.language"],
populate: ["entries", "entries.carer", "entries.entryLanguages.language", "participant"],
});
questionnaire.assign(questionnaireMutationDto);

Expand Down
30 changes: 17 additions & 13 deletions apps/frontend/src/api.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,14 @@ export interface components {
*/
role?: "ASSISTANT" | "ADMIN";
};
CarerCreationDto: {
/**
* @description The name of the carer
* @example Grandmother
*/
name: string;
participant?: number;
};
StudyDto: {
/**
* @description The id of the study (child id)
Expand Down Expand Up @@ -532,14 +540,6 @@ export interface components {
carers: number[];
languages: number[];
};
CarerCreationDto: {
/**
* @description The name of the carer
* @example Grandmother
*/
name: string;
participant?: components["schemas"]["ParticipantDto"];
};
CarerResponseDto: {
/**
* @description The id of the carer
Expand All @@ -560,7 +560,7 @@ export interface components {
* @example Grandmother
*/
name?: string;
participant?: components["schemas"]["ParticipantDto"];
participant?: number;
};
LanguageCreationDto: {
/**
Expand All @@ -573,7 +573,7 @@ export interface components {
* @example de-DE
*/
ietfBcp47?: string;
participant?: components["schemas"]["ParticipantDto"];
participant?: number;
};
LanguageResponseDto: {
/**
Expand Down Expand Up @@ -605,7 +605,7 @@ export interface components {
* @example de-DE
*/
ietfBcp47?: string;
participant?: components["schemas"]["ParticipantDto"];
participant?: number;
};
ParticipantCreationDto: {
/**
Expand Down Expand Up @@ -1345,7 +1345,9 @@ export interface operations {
};
CarersController_index: {
parameters: {
query?: never;
query?: {
participantId?: number;
};
header?: never;
path?: never;
cookie?: never;
Expand Down Expand Up @@ -1461,7 +1463,9 @@ export interface operations {
};
LanguagesController_index: {
parameters: {
query?: never;
query?: {
participantId?: number;
};
header?: never;
path?: never;
cookie?: never;
Expand Down
12 changes: 6 additions & 6 deletions apps/frontend/src/components/CarerSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { $api } from "../stores/api";
import { components } from "../api.gen";
import { EntitySelect, EntitySelectProps } from "./EntitySelect";

type CarerSelectProps = EntitySelectProps;
type CarerSelectProps = EntitySelectProps & {
data: components["schemas"]["CarerDto"][];
};

export function CarerSelect({ value, onChange, ...rest }: CarerSelectProps) {
const { data } = $api.useQuery("get", "/carers");

return <EntitySelect value={value} onChange={onChange} {...rest} data={data} buildLabel={(carer) => carer.name} />;
export function CarerSelect({ value, onChange, onAddNew, data, ...rest }: CarerSelectProps) {
return <EntitySelect value={value} onChange={onChange} onAddNew={onAddNew} {...rest} data={data} labelKey="name" />;

Check warning on line 9 in apps/frontend/src/components/CarerSelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/CarerSelect.tsx#L8-L9

Added lines #L8 - L9 were not covered by tests
}
99 changes: 88 additions & 11 deletions apps/frontend/src/components/EntitySelect.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,99 @@
import { Select, SelectProps } from "@quassel/ui";
import { Combobox, TextInput, TextInputProps, useCombobox } from "@quassel/ui";
import { useEffect, useState } from "react";
import { i18n } from "../stores/i18n";
import { useStore } from "@nanostores/react";
import { params } from "@nanostores/i18n";

export type EntitySelectProps = Omit<SelectProps, "value" | "onChange"> & {
export type EntitySelectProps = Omit<TextInputProps, "value" | "onChange"> & {
value?: number;
onChange?: (id?: number) => void;
onChange?: (value?: number) => void;
onAddNew?: (value: string) => Promise<number>;
};

type StringKeys<T> = { [K in keyof T]-?: T[K] extends string ? K : never }[keyof T];

type Props<T extends { id: number }> = Omit<EntitySelectProps, "data"> & {
data?: T[];
buildLabel: (value: T) => string;
onAddNew?: (value: string) => void;
labelKey: StringKeys<T>;
};

export function EntitySelect<T extends { id: number }>({ value, onChange, data, buildLabel, ...rest }: Props<T>) {
const customValueKey = "CUSTOM_VALUE";

Check warning on line 21 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L21

Added line #L21 was not covered by tests

const messages = i18n("entitySelect", {
actionCreateNew: params('Create new "{value}"'),
});

Check warning on line 25 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L23-L25

Added lines #L23 - L25 were not covered by tests

export function EntitySelect<T extends { id: number }>({ value, onChange, data, labelKey, onAddNew, ...rest }: Props<T>) {
const t = useStore(messages);

Check warning on line 28 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L27-L28

Added lines #L27 - L28 were not covered by tests

const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
});

Check warning on line 32 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L30-L32

Added lines #L30 - L32 were not covered by tests

const [searchValue, setSearchValue] = useState("");

Check warning on line 34 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L34

Added line #L34 was not covered by tests

const shouldFilterOptions = !data?.some((item) => item[labelKey] === searchValue);
const filteredOptions =
(shouldFilterOptions
? data?.filter((item) => (item[labelKey] as string)?.toLowerCase().includes(searchValue.toLowerCase().trim()))
: data) ?? [];

Check warning on line 40 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L36-L40

Added lines #L36 - L40 were not covered by tests

const options = filteredOptions?.map((item) => (
<Combobox.Option key={item.id} value={item.id.toString()}>
{item[labelKey] as string}
</Combobox.Option>
));

Check warning on line 46 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L42-L46

Added lines #L42 - L46 were not covered by tests

useEffect(() => {
if (value && data) {
const index = data.findIndex((item) => item.id === value);
if (index !== -1) {
setSearchValue(data[index][labelKey] as string);
combobox.selectOption(index);
}
}
}, [value, data]);

Check warning on line 56 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L48-L56

Added lines #L48 - L56 were not covered by tests

useEffect(() => {
if (shouldFilterOptions) combobox.selectFirstOption();
}, [searchValue]);

Check warning on line 60 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L58-L60

Added lines #L58 - L60 were not covered by tests

return (
<Select
value={value?.toString()}
onChange={(value) => onChange?.(value ? parseInt(value) : undefined)}
data={data?.map((entity) => ({ value: entity.id.toString(), label: buildLabel(entity) })) ?? []}
{...rest}
/>
<Combobox
store={combobox}
onOptionSubmit={async (value) => {
let id: number;
if (value === customValueKey) {
id = await onAddNew!(searchValue);
} else {
id = +value;
}
onChange?.(id);
combobox.closeDropdown();
}}

Check warning on line 74 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L63-L74

Added lines #L63 - L74 were not covered by tests
>
<Combobox.Target>
<TextInput
value={searchValue}
onChange={({ target: { value } }) => {
setSearchValue(value);
}}
onClick={() => combobox.openDropdown()}
onFocus={() => combobox.openDropdown()}
onBlur={() => combobox.closeDropdown()}
{...rest}
/>
</Combobox.Target>

Check warning on line 87 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L76-L87

Added lines #L76 - L87 were not covered by tests

<Combobox.Dropdown>
<Combobox.Options>
{onAddNew && !options?.length && (
<Combobox.Option value={customValueKey}>{t.actionCreateNew({ value: searchValue })}</Combobox.Option>

Check warning on line 92 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L89-L92

Added lines #L89 - L92 were not covered by tests
)}
{options}
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>

Check warning on line 97 in apps/frontend/src/components/EntitySelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/EntitySelect.tsx#L94-L97

Added lines #L94 - L97 were not covered by tests
);
}
12 changes: 6 additions & 6 deletions apps/frontend/src/components/LanguageSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { $api } from "../stores/api";
import { components } from "../api.gen";
import { EntitySelect, EntitySelectProps } from "./EntitySelect";

type LanguageSelectProps = EntitySelectProps;
type LanguageSelectProps = EntitySelectProps & {
data: components["schemas"]["LanguageDto"][];
};

export function LanguageSelect({ value, onChange, ...rest }: LanguageSelectProps) {
const { data } = $api.useQuery("get", "/languages");

return <EntitySelect value={value} onChange={onChange} searchable {...rest} data={data} buildLabel={(language) => language.name} />;
export function LanguageSelect({ value, onChange, data, onAddNew, ...rest }: LanguageSelectProps) {
return <EntitySelect value={value} onChange={onChange} onAddNew={onAddNew} {...rest} data={data} labelKey="name" />;

Check warning on line 9 in apps/frontend/src/components/LanguageSelect.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/components/LanguageSelect.tsx#L8-L9

Added lines #L8 - L9 were not covered by tests
}
Loading
Loading