Initial commit: Cat Sitting Planner with PWA, SQLite, and Webhook Notifications
This commit is contained in:
25
app/actions/auth.ts
Normal file
25
app/actions/auth.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
"use server"
|
||||
|
||||
import { cookies } from "next/headers"
|
||||
import prisma from "@/lib/prisma"
|
||||
|
||||
export async function verifyPlanPassword(planId: string, password: string) {
|
||||
const plan = await prisma.plan.findUnique({
|
||||
where: { id: planId },
|
||||
})
|
||||
|
||||
if (!plan) return false
|
||||
|
||||
if (plan.password === password) {
|
||||
// Set a simple cookie to authorize this plan
|
||||
(await cookies()).set(`plan_auth_${planId}`, "true", {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
maxAge: 60 * 60 * 24 * 30, // 30 days
|
||||
path: "/",
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
62
app/actions/booking.ts
Normal file
62
app/actions/booking.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
"use server"
|
||||
|
||||
import prisma from "@/lib/prisma"
|
||||
import { revalidatePath } from "next/cache"
|
||||
import { sendNotification } from "@/lib/notifications"
|
||||
|
||||
export async function createBooking(planId: string, date: Date, name: string, type: "SITTER" | "OWNER_HOME" = "SITTER") {
|
||||
// Simple check to ensure no double booking on server side
|
||||
const existing = await prisma.booking.findFirst({
|
||||
where: {
|
||||
planId,
|
||||
date: date,
|
||||
}
|
||||
})
|
||||
|
||||
if (existing) {
|
||||
throw new Error("Day is already booked")
|
||||
}
|
||||
|
||||
const plan = await prisma.plan.findUnique({
|
||||
where: { id: planId }
|
||||
})
|
||||
|
||||
await prisma.booking.create({
|
||||
data: {
|
||||
planId,
|
||||
date,
|
||||
sitterName: name,
|
||||
type
|
||||
}
|
||||
})
|
||||
|
||||
if (plan?.webhookUrl && plan.notifyAll) {
|
||||
const dateStr = date.toLocaleDateString()
|
||||
const message = type === "OWNER_HOME"
|
||||
? `🏠 OWNER HOME: Marked for ${dateStr}.`
|
||||
: `✅ NEW BOOKING: ${name} is sitting on ${dateStr}.`
|
||||
await sendNotification(plan.webhookUrl, message)
|
||||
}
|
||||
|
||||
revalidatePath(`/dashboard/${planId}`)
|
||||
}
|
||||
|
||||
export async function deleteBooking(bookingId: number, planId: string) {
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: { id: bookingId },
|
||||
include: { plan: true }
|
||||
})
|
||||
|
||||
if (!booking) return
|
||||
|
||||
await prisma.booking.delete({
|
||||
where: { id: bookingId }
|
||||
})
|
||||
|
||||
if (booking.plan.webhookUrl) {
|
||||
const dateStr = booking.date.toLocaleDateString()
|
||||
await sendNotification(booking.plan.webhookUrl, `🚨 CANCELLATION: ${booking.sitterName} removed their booking for ${dateStr}.`)
|
||||
}
|
||||
|
||||
revalidatePath(`/dashboard/${planId}`)
|
||||
}
|
||||
22
app/actions/plan.ts
Normal file
22
app/actions/plan.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
"use server"
|
||||
|
||||
import prisma from "@/lib/prisma"
|
||||
import { revalidatePath } from "next/cache"
|
||||
import { sendNotification } from "@/lib/notifications"
|
||||
|
||||
export async function updatePlan(planId: string, data: { instructions?: string; webhookUrl?: string; notifyAll?: boolean }) {
|
||||
const plan = await prisma.plan.update({
|
||||
where: { id: planId },
|
||||
data: {
|
||||
instructions: data.instructions,
|
||||
webhookUrl: data.webhookUrl,
|
||||
notifyAll: data.notifyAll,
|
||||
}
|
||||
})
|
||||
|
||||
if (data.instructions && plan.webhookUrl && plan.notifyAll) {
|
||||
await sendNotification(plan.webhookUrl, `📝 UPDATED: Cat instructions have been modified.`)
|
||||
}
|
||||
|
||||
revalidatePath(`/dashboard/${planId}`)
|
||||
}
|
||||
Reference in New Issue
Block a user