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/shy-hotels-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@quassel/frontend": minor
"@quassel/backend": minor
"@quassel/ui": minor
---

Allow clearing all entries from a questionnaire
11 changes: 9 additions & 2 deletions apps/backend/src/research/entries/entries.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 { ApiTags, ApiOperation, ApiUnprocessableEntityResponse } from "@nestjs/swagger";
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiTags, ApiOperation, ApiUnprocessableEntityResponse, ApiQuery } from "@nestjs/swagger";
import { ErrorResponseDto } from "../../common/dto/error.dto";
import { EntriesService } from "./entries.service";
import { EntryCreationDto, EntryResponseDto, EntryMutationDto } from "./entry.dto";
Expand Down Expand Up @@ -44,4 +44,11 @@
delete(@Param("id") id: string) {
return this.entriesService.remove(+id);
}

@Delete()
@ApiQuery({ name: "questionnaireId", required: true, type: Number })
@ApiOperation({ summary: "Delete all entries of a questionnaire" })
deleteAll(@Query("questionnaireId") questionnaireId: number) {
return this.entriesService.removeAllFromQuestionnaire(questionnaireId);

Check warning on line 52 in apps/backend/src/research/entries/entries.controller.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L52 was not covered by tests
}
}
4 changes: 4 additions & 0 deletions apps/backend/src/research/entries/entries.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,8 @@
remove(id: number) {
return this.em.remove(this.entryRepository.getReference(id)).flush();
}

removeAllFromQuestionnaire(questionnaireId: number) {
return this.entryRepository.nativeDelete({ questionnaire: questionnaireId });

Check warning on line 84 in apps/backend/src/research/entries/entries.service.ts

View check run for this annotation

Codecov / codecov/patch

apps/backend/src/research/entries/entries.service.ts#L83-L84

Added lines #L83 - L84 were not covered by tests
}
}
24 changes: 23 additions & 1 deletion apps/frontend/src/api.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ export interface paths {
put?: never;
/** Create a entry */
post: operations["EntriesController_create"];
delete?: never;
/** Delete all entries of a questionnaire */
delete: operations["EntriesController_deleteAll"];
options?: never;
head?: never;
patch?: never;
Expand Down Expand Up @@ -1842,6 +1843,27 @@ export interface operations {
};
};
};
EntriesController_deleteAll: {
parameters: {
query: {
questionnaireId: number;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": number;
};
};
};
};
EntriesController_get: {
parameters: {
query?: never;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Group, Modal, Stack, Title, useDisclosure, useForm } from "@quassel/ui";
import { Button, Group, IconClearAll, modals, Stack, Title, useForm, Text } from "@quassel/ui";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { i18n } from "../../../../../stores/i18n";
import { useStore } from "@nanostores/react";
Expand All @@ -14,9 +14,14 @@
addEntityLabel: "Add",
notificationSuccessCreateLanguage: "Successfully add a new language.",
notificationSuccessCreateCarer: "Successfully add a new carer.",
gapsDialogTitle: "Gaps detected in the calendar",
gapsDialogTitle: "Continue with gaps?",
gapsDialogDescription: "There were gaps detected in the calendar. Do you want to continue anyway or highlight the gaps?",

Check warning on line 18 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L17-L18

Added lines #L17 - L18 were not covered by tests
gapsDialogContinueAnyway: "Continue anyway",
gapsDialogHighlightGaps: "Highlight gaps",
confirmClearDialogTitle: "Clear all entries from this questionnaire?",
confirmClearDialogDescription: "When confirming, all entries from this questionnaires will be removed. This action can't be undone.",
confirmClearDialogCancel: "Cancel",
confirmClearDialogConfirm: "Clear all",

Check warning on line 24 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L21-L24

Added lines #L21 - L24 were not covered by tests
});

export function Entries() {
Expand All @@ -25,11 +30,12 @@

const t = useStore(messages);

const { data: questionnaire } = $api.useSuspenseQuery("get", "/questionnaires/{id}", { params: { path: p } });
const { data: questionnaire, refetch } = $api.useSuspenseQuery("get", "/questionnaires/{id}", { params: { path: p } });

Check warning on line 33 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L33

Added line #L33 was not covered by tests

const removeAllEntriesMutation = $api.useMutation("delete", "/entries", { onSuccess: () => refetch() });

Check warning on line 35 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L35

Added line #L35 was not covered by tests

const [gaps, setGaps] = useState<GapsPerDay>();
const [highlightGaps, setHighlightGaps] = useState(false);
const [gapsDialogOpened, { open, close }] = useDisclosure();

const f = useForm<{ entries: components["schemas"]["EntryResponseDto"][] }>({
initialValues: {
Expand All @@ -50,43 +56,52 @@
n({ to: "/questionnaire/$id/remarks", params: p });
};

const handleGapValidation = () => {
modals.openConfirmModal({
title: t.gapsDialogTitle,
children: <Text size="sm">{t.gapsDialogDescription}</Text>,
labels: { cancel: t.gapsDialogHighlightGaps, confirm: t.gapsDialogContinueAnyway },
confirmProps: { variant: "light" },
cancelProps: { variant: "filled" },
onConfirm: handleSubmit,
onCancel: () => setHighlightGaps(true),
});
};

Check warning on line 69 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L59-L69

Added lines #L59 - L69 were not covered by tests

const handleClearEntries = () => {
modals.openConfirmModal({
title: t.confirmClearDialogTitle,
children: <Text size="sm">{t.confirmClearDialogDescription}</Text>,
labels: { cancel: t.confirmClearDialogCancel, confirm: t.confirmClearDialogConfirm },
onConfirm: () => removeAllEntriesMutation.mutate({ params: { query: { questionnaireId: questionnaire.id } } }),
});
};

Check warning on line 78 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L71-L78

Added lines #L71 - L78 were not covered by tests

useEffect(() => {
f.setValues({ entries: questionnaire.entries });

if (highlightGaps) f.validate();
}, [questionnaire]);

return (
<>
<Modal opened={gapsDialogOpened} onClose={close} centered title={t.gapsDialogTitle}>
<Group justify="flex-end">
<Button onClick={handleSubmit} variant="light" type="submit">
{t.gapsDialogContinueAnyway}
</Button>
<Button
onClick={() => {
setHighlightGaps(true);
close();
}}
>
{t.gapsDialogHighlightGaps}
<form onSubmit={f.onSubmit(handleSubmit, handleGapValidation)}>
<Stack>
<Group justify="space-between">
<Title order={3}>{questionnaire.title}</Title>
<Button variant="default" onClick={handleClearEntries} rightSection={<IconClearAll />}>

Check warning on line 91 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L87-L91

Added lines #L87 - L91 were not covered by tests
Clear all
</Button>
</Group>
</Modal>
<form onSubmit={f.onSubmit(handleSubmit, open)}>
<Stack>
<Title order={3}>{questionnaire.title}</Title>
<QuestionnaireEntries gaps={highlightGaps ? gaps : undefined} questionnaire={questionnaire} />

<Group>
<Link to="/questionnaire/$id/period" params={p}>
<Button variant="light">{t.backAction}</Button>
</Link>
<Button type="submit">{t.formAction}</Button>
</Group>
</Stack>
</form>
</>
<QuestionnaireEntries gaps={highlightGaps ? gaps : undefined} questionnaire={questionnaire} />

Check warning on line 95 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L95

Added line #L95 was not covered by tests

<Group>
<Link to="/questionnaire/$id/period" params={p}>
<Button variant="light">{t.backAction}</Button>
</Link>
<Button type="submit">{t.formAction}</Button>
</Group>
</Stack>
</form>

Check warning on line 104 in apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx

View check run for this annotation

Codecov / codecov/patch

apps/frontend/src/routes/_auth/questionnaire/_questionnaire/$id/entries.tsx#L97-L104

Added lines #L97 - L104 were not covered by tests
);
}

Expand Down
1 change: 1 addition & 0 deletions libs/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@mantine/dates": "7.17.2",
"@mantine/form": "7.17.2",
"@mantine/hooks": "7.17.2",
"@mantine/modals": "^7.17.2",
"@mantine/notifications": "7.17.2",
"@tabler/icons-react": "^3.31.0",
"dayjs": "^1.11.13",
Expand Down
3 changes: 3 additions & 0 deletions libs/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export { useDisclosure, useFullscreen } from "@mantine/hooks";

export { notifications, type NotificationData } from "@mantine/notifications";

export { modals } from "@mantine/modals";

export { useForm, isInRange, isNotEmpty } from "@mantine/form";

export {
Expand All @@ -123,6 +125,7 @@ export {
IconReportAnalytics,
IconX,
IconInfoCircle,
IconClearAll,
} from "@tabler/icons-react";

export { uzhColors } from "./theme/uzh";
Expand Down
7 changes: 5 additions & 2 deletions libs/ui/src/theme/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import utc from "dayjs/plugin/utc";
import { DefaultMantineColor, MantineColorsTuple } from "@mantine/core";
import { Notifications } from "@mantine/notifications";
import { convertUZHColorsToMantine, UZHColor, uzhColors } from "./uzh";
import { ModalsProvider } from "@mantine/modals";

dayjs.extend(utc);

Expand Down Expand Up @@ -37,8 +38,10 @@ export function ThemeProvider({ children, ...args }: ThemeProviderProps) {
return (
<DatesProvider settings={{ timezone: "UTC" }}>
<MantineProvider {...args} theme={theme}>
<Notifications />
{children}
<ModalsProvider>
<Notifications />
{children}
</ModalsProvider>
</MantineProvider>
</DatesProvider>
);
Expand Down
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.