feat: allow sitters to mark jobs as completed with notification

This commit is contained in:
2026-01-12 23:16:23 +01:00
parent 3600ba665d
commit 22183a8d59
6 changed files with 93 additions and 19 deletions

View File

@@ -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, Utensils, Trash2 } from "lucide-react"
import { CalendarIcon, User, Home, X, Info, Utensils, Trash2, Check } from "lucide-react"
import { toast } from "sonner"
import { Button } from "@/components/ui/button"
@@ -19,7 +19,7 @@ import {
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { createBooking, deleteBooking } from "@/app/actions/booking"
import { createBooking, deleteBooking, completeBooking } from "@/app/actions/booking"
import { PlanSettings } from "@/components/plan-settings"
type Booking = {
@@ -27,6 +27,7 @@ type Booking = {
date: Date
sitterName: string | null
type: string
completedAt?: Date | string | null
}
type Plan = {
@@ -59,6 +60,15 @@ export function PlanDashboard({ plan, dict, settingsDict, lang }: PlanDashboardP
const [cancelReason, setCancelReason] = useState("")
const [isSubmitting, setIsSubmitting] = useState(false)
const handleComplete = async (bookingId: number) => {
try {
await completeBooking(bookingId, plan.id, lang)
toast.success(dict.bookedSuccess) // reuse for now or add new toast
} catch (error) {
toast.error(dict.bookError)
}
}
const dateLocale = lang === "de" ? de : enUS
// Load saved name from localStorage
@@ -194,17 +204,40 @@ export function PlanDashboard({ plan, dict, settingsDict, lang }: PlanDashboardP
</div>
{booking ? (
<div className="flex items-center gap-2">
{isOwnerHome ? (
<>
<Home className="w-5 h-5 text-blue-500" />
<span className="font-medium text-blue-700 dark:text-blue-300">{dict.ownerHome}</span>
</>
) : (
<>
<User className="w-5 h-5 text-green-600" />
<span className="font-medium text-green-700 dark:text-green-300">{booking.sitterName}</span>
</>
<div className="space-y-3">
<div className="flex items-center gap-2">
{isOwnerHome ? (
<>
<Home className="w-5 h-5 text-blue-500" />
<span className="font-medium text-blue-700 dark:text-blue-300">{dict.ownerHome}</span>
</>
) : (
<>
<User className="w-5 h-5 text-green-600" />
<span className="font-medium text-green-700 dark:text-green-300">{booking.sitterName}</span>
</>
)}
</div>
{booking.type === "SITTER" && (
<div className="pt-1">
{booking.completedAt ? (
<div className="flex items-center gap-2 text-green-600 font-bold bg-green-100/50 dark:bg-green-900/30 px-3 py-1.5 rounded-full border border-green-200 dark:border-green-800 w-fit text-sm">
<Check className="w-4 h-4 stroke-[3px]" />
<span>{dict.jobDone}</span>
</div>
) : (
<Button
variant="outline"
size="sm"
className="w-full flex gap-2 items-center border-green-200 hover:bg-green-100/50 text-green-700 dark:border-green-800 dark:hover:bg-green-900/40 font-semibold"
onClick={() => handleComplete(booking.id)}
>
<Check className="w-4 h-4" />
{dict.markDone}
</Button>
)}
</div>
)}
</div>
) : (

View File

@@ -82,3 +82,35 @@ export async function deleteBooking(bookingId: number, planId: string, lang: str
revalidatePath(`/${lang}/dashboard/${planId}`)
}
export async function completeBooking(bookingId: number, planId: string, lang: string = "en") {
const dict = await getDictionary(lang as any)
const booking = await prisma.booking.findUnique({
where: { id: bookingId },
include: { plan: true }
})
if (!booking) return
await prisma.booking.update({
where: { id: bookingId },
data: { completedAt: new Date() }
})
if (booking.plan.webhookUrl) {
const host = (await headers()).get("host")
const protocol = host?.includes("localhost") ? "http" : "https"
const planUrl = `${protocol}://${host}/${lang}/dashboard/${planId}`
const dateStr = booking.date.toLocaleDateString(lang)
const message = dict.notifications.completed
.replace("{name}", booking.sitterName || "Someone")
.replace("{date}", dateStr)
.replace("{url}", planUrl)
await sendNotification(booking.plan.webhookUrl, message)
}
revalidatePath(`/${lang}/dashboard/${planId}`)
}