aboutsummaryrefslogtreecommitdiff
path: root/src/lib/server/services/routine-actions.ts
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-09-08 08:57:08 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-09-08 08:57:08 +0200
commit4b2b964283075712de98743720ff3e31370ddc8e (patch)
tree7200e83869dbd42d5f20318a07a6ca328e5ac59f /src/lib/server/services/routine-actions.ts
parent992e40835af1600a0961ec2454fb78ff43aec3c6 (diff)
Deduplicate routine actions and badges across project routes
- Share edit/complete/archive form actions in routine-actions.ts; route servers become thin wrappers (strictest guard semantics in one place) - Move parseFrequencyDays into validation.ts - Extract ScoreBadge/StatusBadge components; use on project pages and the home page instead of duplicated scoreClass/statusClass helpers - Fix archiveProject calling the routine archive with the project id
Diffstat (limited to 'src/lib/server/services/routine-actions.ts')
-rw-r--r--src/lib/server/services/routine-actions.ts86
1 files changed, 86 insertions, 0 deletions
diff --git a/src/lib/server/services/routine-actions.ts b/src/lib/server/services/routine-actions.ts
new file mode 100644
index 0000000..b546ec7
--- /dev/null
+++ b/src/lib/server/services/routine-actions.ts
@@ -0,0 +1,86 @@
+import { fail, type ActionFailure } from '@sveltejs/kit';
+import { archive, complete, edit, loadCurrent } from './routines';
+import { parseFormString, parseFrequencyDays } from './validation';
+
+/**
+ * Shared implementations of the routine edit/complete form actions, used by
+ * both the project dashboard (/projects/[id]) and the routine page
+ * (/projects/[projectId]/routine/[routineId]). Guarding and failure messages
+ * are the strictest common denominator: a missing or archived routine is
+ * rejected before any write.
+ */
+
+export type EditRoutineResult =
+ ActionFailure<{ error: string }> | { success: boolean; type?: 'edit' };
+
+export async function editRoutineAction(
+ data: FormData,
+ routineId: number,
+ extra?: { type: 'edit' },
+): Promise<EditRoutineResult> {
+ const current = await loadCurrent(routineId);
+ if (!current) {
+ return fail(400, { error: 'Routine introuvable' });
+ }
+ if (current.archived) {
+ return fail(400, { error: 'Routine archivée' });
+ }
+
+ const name = parseFormString(data.get('name'));
+ if (!name) {
+ return fail(400, { error: 'Le nom est requis' });
+ }
+
+ let frequencyDays: number;
+ try {
+ frequencyDays = parseFrequencyDays(data.get('frequencyDays'));
+ } catch {
+ return fail(400, { error: 'Fréquence invalide' });
+ }
+
+ // The dashboard row form has no description field; only touch it when the
+ // form actually sends one, so list edits never wipe a description.
+ const fields: { name: string; frequencyDays: number; description?: string } =
+ { name, frequencyDays };
+ const description = data.get('description');
+ if (description !== null) {
+ fields.description = description.toString();
+ }
+
+ try {
+ await edit(routineId, fields);
+ } catch {
+ return fail(400, { error: 'Routine introuvable' });
+ }
+ return { success: true, type: extra?.type };
+}
+
+export async function completeRoutineAction(data: FormData, routineId: number) {
+ const current = await loadCurrent(routineId);
+ if (!current) {
+ return fail(400, { error: 'Routine introuvable' });
+ }
+ if (current.archived) {
+ return fail(400, { error: 'Routine archivée' });
+ }
+
+ try {
+ await complete(
+ routineId,
+ parseFormString(data.get('note')),
+ new Date().toISOString(),
+ );
+ } catch {
+ return fail(400, { error: 'Routine introuvable' });
+ }
+ return { success: true };
+}
+
+export async function archiveRoutineAction(routineId: number) {
+ try {
+ await archive(routineId);
+ } catch {
+ return fail(400, { error: 'Routine introuvable' });
+ }
+ return { success: true };
+}