From 3600ba665d0f81aee12c9e5f0f087a8fb25b92c6 Mon Sep 17 00:00:00 2001 From: elpatron Date: Mon, 12 Jan 2026 23:06:12 +0100 Subject: [PATCH] feat: implement flexible feeding intervals and fix layout alignment --- .../[planId]/_components/plan-dashboard.tsx | 20 +++++- app/api/plan/route.ts | 5 +- components/create-plan-form.tsx | 67 ++++++++++++++++++- dictionaries/de.json | 8 +++ dictionaries/en.json | 8 +++ .../migration.sql | 21 ++++++ .../migration.sql | 22 ++++++ prisma/schema.prisma | 3 + 8 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 prisma/migrations/20260112215900_add_intervals_to_plan/migration.sql create mode 100644 prisma/migrations/20260112220121_add_feeding_interval/migration.sql diff --git a/app/[lang]/dashboard/[planId]/_components/plan-dashboard.tsx b/app/[lang]/dashboard/[planId]/_components/plan-dashboard.tsx index 43fa8fe..fd14cbe 100644 --- a/app/[lang]/dashboard/[planId]/_components/plan-dashboard.tsx +++ b/app/[lang]/dashboard/[planId]/_components/plan-dashboard.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from "react" import { format, eachDayOfInterval, isSameDay } from "date-fns" import { de, enUS } from "date-fns/locale" -import { CalendarIcon, User, Home, X, Info } from "lucide-react" +import { CalendarIcon, User, Home, X, Info, Utensils, Trash2 } from "lucide-react" import { toast } from "sonner" import { Button } from "@/components/ui/button" @@ -37,6 +37,9 @@ type Plan = { webhookUrl: string | null notifyAll: boolean bookings: Booking[] + feedingPerDay: number + feedingInterval: number + litterInterval: number } interface PlanDashboardProps { @@ -255,6 +258,21 @@ export function PlanDashboard({ plan, dict, settingsDict, lang }: PlanDashboardP )} + +
+ {Math.floor((day.getTime() - new Date(plan.startDate).setHours(0, 0, 0, 0)) / (1000 * 60 * 60 * 24)) % plan.feedingInterval === 0 && ( +
+ {Array.from({ length: plan.feedingPerDay }).map((_, i) => ( + + ))} +
+ )} + {Math.floor((day.getTime() - new Date(plan.startDate).setHours(0, 0, 0, 0)) / (1000 * 60 * 60 * 24)) % plan.litterInterval === 0 && ( +
+ +
+ )} +
) })} diff --git a/app/api/plan/route.ts b/app/api/plan/route.ts index 1fd314d..6ddcf6f 100644 --- a/app/api/plan/route.ts +++ b/app/api/plan/route.ts @@ -4,7 +4,7 @@ import prisma from '@/lib/prisma'; export async function POST(req: Request) { try { const body = await req.json(); - const { title, startDate, endDate, password, instructions } = body; + const { title, startDate, endDate, password, instructions, feedingPerDay, feedingInterval, litterInterval } = body; if (!title || !startDate || !endDate || !password) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); @@ -17,6 +17,9 @@ export async function POST(req: Request) { endDate: new Date(endDate), password, instructions, + feedingPerDay: feedingPerDay ? parseInt(feedingPerDay) : undefined, + feedingInterval: feedingInterval ? parseInt(feedingInterval) : undefined, + litterInterval: litterInterval ? parseInt(litterInterval) : undefined, }, }); diff --git a/components/create-plan-form.tsx b/components/create-plan-form.tsx index 5644ee7..d878e3d 100644 --- a/components/create-plan-form.tsx +++ b/components/create-plan-form.tsx @@ -43,21 +43,33 @@ export function CreatePlanForm({ dict, lang }: CreatePlanFormProps) { password: z.string().min(4, { message: dict.passwordError, }), + feedingPerDay: z.coerce.number().min(1).max(10), + feedingInterval: z.coerce.number().min(1).max(30), + litterInterval: z.coerce.number().min(1).max(30), instructions: z.string().optional(), }) - const form = useForm>({ - resolver: zodResolver(formSchema), + type FormValues = z.infer + + const form = useForm({ + resolver: zodResolver(formSchema) as any, defaultValues: { title: "", password: "", instructions: "", + feedingPerDay: 2, + feedingInterval: 1, + litterInterval: 2, + dateRange: { + from: undefined, + to: undefined, + } }, }) const dateLocale = lang === "de" ? de : enUS - async function onSubmit(values: z.infer) { + async function onSubmit(values: FormValues) { try { const response = await fetch("/api/plan", { method: "POST", @@ -68,6 +80,9 @@ export function CreatePlanForm({ dict, lang }: CreatePlanFormProps) { endDate: values.dateRange.to, password: values.password, instructions: values.instructions, + feedingPerDay: values.feedingPerDay, + feedingInterval: values.feedingInterval, + litterInterval: values.litterInterval, }), }) @@ -153,6 +168,52 @@ export function CreatePlanForm({ dict, lang }: CreatePlanFormProps) { )} /> + +
+ ( + + {dict.feedingInterval} + + + + {dict.feedingIntervalDesc} + + + )} + /> + ( + + {dict.feedingPerDay} + + + + {dict.feedingDesc} + + + )} + /> + ( + + {dict.litterInterval} + + + + {dict.litterDesc} + + + )} + /> +
+