Initial commit: Cat Sitting Planner with PWA, SQLite, and Webhook Notifications
This commit is contained in:
50
app/api/plan/[planId]/ics/route.ts
Normal file
50
app/api/plan/[planId]/ics/route.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import prisma from "@/lib/prisma"
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ planId: string }> }
|
||||
) {
|
||||
const { planId } = await params
|
||||
const plan = await prisma.plan.findUnique({
|
||||
where: { id: planId },
|
||||
include: { bookings: true }
|
||||
})
|
||||
|
||||
if (!plan) {
|
||||
return new NextResponse("Plan not found", { status: 404 })
|
||||
}
|
||||
|
||||
let icsContent = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Cat Sitting Planner//EN\n"
|
||||
|
||||
for (const booking of plan.bookings) {
|
||||
if (booking.type === "OWNER_HOME") continue; // Don't put owner home days in calendar? Or maybe do?
|
||||
|
||||
const date = new Date(booking.date)
|
||||
const dateStr = date.toISOString().replace(/-|:|\.\d\d\d/g, "").slice(0, 8)
|
||||
|
||||
icsContent += "BEGIN:VEVENT\n"
|
||||
icsContent += `DTSTART;VALUE=DATE:${dateStr}\n`
|
||||
icsContent += `DTEND;VALUE=DATE:${dateStr}\n` // Single day event usually ends next day or same day? Calendar usually expects Start and End. For all day: DTSTART:YYYYMMDD, DTEND:YYYYMMDD+1
|
||||
|
||||
// Calculate next day for DTEND
|
||||
const nextDate = new Date(date)
|
||||
nextDate.setDate(date.getDate() + 1)
|
||||
const nextDateStr = nextDate.toISOString().replace(/-|:|\.\d\d\d/g, "").slice(0, 8)
|
||||
|
||||
icsContent = icsContent.replace(`DTEND;VALUE=DATE:${dateStr}\n`, `DTEND;VALUE=DATE:${nextDateStr}\n`)
|
||||
|
||||
icsContent += `SUMMARY:Cat Sitting: ${booking.sitterName}\n`
|
||||
icsContent += `DESCRIPTION:Cat sitting for plan ${plan.id}\n`
|
||||
icsContent += "END:VEVENT\n"
|
||||
}
|
||||
|
||||
icsContent += "END:VCALENDAR"
|
||||
|
||||
return new NextResponse(icsContent, {
|
||||
headers: {
|
||||
"Content-Type": "text/calendar",
|
||||
"Content-Disposition": `attachment; filename="cat-sitting-${plan.startDate.toISOString().slice(0, 10)}.ics"`
|
||||
}
|
||||
})
|
||||
}
|
||||
27
app/api/plan/route.ts
Normal file
27
app/api/plan/route.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import prisma from '@/lib/prisma';
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { startDate, endDate, password, instructions } = body;
|
||||
|
||||
if (!startDate || !endDate || !password) {
|
||||
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
||||
}
|
||||
|
||||
const plan = await prisma.plan.create({
|
||||
data: {
|
||||
startDate: new Date(startDate),
|
||||
endDate: new Date(endDate),
|
||||
password,
|
||||
instructions,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({ planId: plan.id });
|
||||
} catch (error) {
|
||||
console.error('Error creating plan:', error);
|
||||
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user