146 lines
4.1 KiB
TypeScript
146 lines
4.1 KiB
TypeScript
import { NextResponse } from "next/server"
|
|
import { getGoogleSheetsClient } from "@/lib/google-sheets"
|
|
|
|
const BOOKING_SHEET_ID = process.env.BOOKING_SHEET_ID
|
|
const BOOKING_SHEET_NAME = process.env.BOOKING_SHEET_NAME || "Occupancy"
|
|
|
|
// Accommodation criteria (mirrors booking-sheet.ts)
|
|
const ACCOMMODATION_CRITERIA: Record<
|
|
string,
|
|
{ venue: string; bedTypes: string[]; roomFilter?: (room: string) => boolean }
|
|
> = {
|
|
"ch-multi": {
|
|
venue: "Commons Hub",
|
|
bedTypes: ["bunk up", "bunk down", "single"],
|
|
roomFilter: (room) => {
|
|
const num = parseInt(room, 10)
|
|
return num >= 5 && num <= 9
|
|
},
|
|
},
|
|
"ch-double": {
|
|
venue: "Commons Hub",
|
|
bedTypes: ["double", "double (shared)"],
|
|
roomFilter: (room) => room === "2",
|
|
},
|
|
}
|
|
|
|
// In-memory cache (2-min TTL)
|
|
let cache: { data: Record<string, boolean> | null; timestamp: number } = { data: null, timestamp: 0 }
|
|
const CACHE_TTL_MS = 2 * 60 * 1000
|
|
|
|
interface BedRow {
|
|
venue: string
|
|
room: string
|
|
bedType: string
|
|
occupied: boolean
|
|
}
|
|
|
|
function parseBedsFromSheet(data: string[][]): BedRow[] {
|
|
const beds: BedRow[] = []
|
|
let currentVenue = ""
|
|
let roomCol = -1
|
|
let bedTypeCol = -1
|
|
let dateColumnIndices: number[] = []
|
|
let lastRoom = ""
|
|
let inDataSection = false
|
|
|
|
for (const row of data) {
|
|
if (!row || row.length === 0) {
|
|
inDataSection = false
|
|
continue
|
|
}
|
|
|
|
const firstCell = (row[0] || "").trim()
|
|
if (firstCell.toLowerCase().includes("commons hub")) {
|
|
currentVenue = "Commons Hub"
|
|
inDataSection = false
|
|
lastRoom = ""
|
|
continue
|
|
}
|
|
|
|
if (!currentVenue) continue
|
|
|
|
const lowerRow = row.map((c) => (c || "").trim().toLowerCase())
|
|
const roomIdx = lowerRow.findIndex((c) => c === "room" || c === "room #" || c === "room number")
|
|
const bedIdx = lowerRow.findIndex(
|
|
(c) => c === "bed type" || c === "bed" || c === "type" || c === "bed/type"
|
|
)
|
|
|
|
if (roomIdx !== -1 && bedIdx !== -1) {
|
|
roomCol = roomIdx
|
|
bedTypeCol = bedIdx
|
|
dateColumnIndices = []
|
|
for (let j = bedIdx + 1; j < row.length; j++) {
|
|
if ((row[j] || "").trim()) dateColumnIndices.push(j)
|
|
}
|
|
inDataSection = true
|
|
lastRoom = ""
|
|
continue
|
|
}
|
|
|
|
if (inDataSection && roomCol !== -1 && bedTypeCol !== -1) {
|
|
let bedType = (row[bedTypeCol] || "").trim().toLowerCase()
|
|
if (!bedType) continue
|
|
if (bedType.includes("(") && !bedType.includes(")")) bedType += ")"
|
|
|
|
const roomValue = (row[roomCol] || "").trim()
|
|
if (roomValue) lastRoom = roomValue
|
|
if (!lastRoom) continue
|
|
|
|
const occupied = dateColumnIndices.some((colIdx) => (row[colIdx] || "").trim().length > 0)
|
|
|
|
beds.push({ venue: currentVenue, room: lastRoom, bedType, occupied })
|
|
}
|
|
}
|
|
|
|
return beds
|
|
}
|
|
|
|
async function checkAvailability(): Promise<Record<string, boolean> | null> {
|
|
if (!BOOKING_SHEET_ID) return null
|
|
|
|
const now = Date.now()
|
|
if (cache.data && now - cache.timestamp < CACHE_TTL_MS) {
|
|
return cache.data
|
|
}
|
|
|
|
try {
|
|
const sheets = getGoogleSheetsClient()
|
|
const response = await sheets.spreadsheets.values.get({
|
|
spreadsheetId: BOOKING_SHEET_ID,
|
|
range: BOOKING_SHEET_NAME,
|
|
})
|
|
|
|
const sheetData = response.data.values || []
|
|
if (sheetData.length === 0) return null
|
|
|
|
const beds = parseBedsFromSheet(sheetData)
|
|
|
|
const availability: Record<string, boolean> = {}
|
|
for (const [type, criteria] of Object.entries(ACCOMMODATION_CRITERIA)) {
|
|
availability[type] = beds.some(
|
|
(bed) =>
|
|
bed.venue === criteria.venue &&
|
|
criteria.bedTypes.includes(bed.bedType) &&
|
|
!bed.occupied &&
|
|
(!criteria.roomFilter || criteria.roomFilter(bed.room))
|
|
)
|
|
}
|
|
|
|
cache.data = availability
|
|
cache.timestamp = now
|
|
return availability
|
|
} catch (error) {
|
|
console.error("[Availability] Error checking availability:", error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
export async function GET() {
|
|
const availability = await checkAvailability()
|
|
if (!availability) {
|
|
return NextResponse.json({ error: "Booking sheet not configured" }, { status: 503 })
|
|
}
|
|
return NextResponse.json(availability)
|
|
}
|