180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
from datetime import date
|
|
from typing import Annotated
|
|
from fastapi import APIRouter, Depends, Query
|
|
import asyncpg
|
|
|
|
from app.core.pagination import pagination_params
|
|
from app.core.responses import ok, paginated
|
|
from app.dependencies import get_db, require_admin
|
|
from app.models.bookings import (
|
|
UpdateBookingStatus, SlotCreate, WeeklyScheduleCreate,
|
|
GenerateSlotsRequest, BlockedDateCreate, UpdateSlotRequest,
|
|
)
|
|
from app.services import booking_service, slot_service
|
|
|
|
router = APIRouter(tags=["Admin — Bookings"])
|
|
|
|
|
|
# ── Bookings ──────────────────────────────────────────────────────────────────
|
|
|
|
@router.get("/bookings")
|
|
async def list_bookings(
|
|
pagination: Annotated[tuple, Depends(pagination_params)],
|
|
status: str | None = Query(None),
|
|
from_date: date | None = Query(None),
|
|
to_date: date | None = Query(None),
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
page, per_page, offset = pagination
|
|
bookings, total = await booking_service.list_bookings(
|
|
db, page, per_page, offset,
|
|
status=status, from_date=from_date, to_date=to_date,
|
|
)
|
|
return paginated(bookings, total, page, per_page)
|
|
|
|
|
|
@router.get("/bookings/{booking_id}")
|
|
async def get_booking(
|
|
booking_id: str,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
booking = await booking_service.get_booking(db, booking_id)
|
|
return ok(booking)
|
|
|
|
|
|
@router.patch("/bookings/{booking_id}")
|
|
async def update_booking(
|
|
booking_id: str,
|
|
body: UpdateBookingStatus,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
admin: dict = Depends(require_admin),
|
|
):
|
|
booking = await booking_service.admin_update_booking(
|
|
db, booking_id, body.status, body.admin_notes, str(admin["id"])
|
|
)
|
|
return ok(booking)
|
|
|
|
|
|
@router.delete("/bookings/{booking_id}", status_code=204)
|
|
async def delete_booking(
|
|
booking_id: str,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
await booking_service.admin_delete_booking(db, booking_id)
|
|
|
|
|
|
# ── Slots ─────────────────────────────────────────────────────────────────────
|
|
|
|
@router.get("/slots")
|
|
async def list_slots(
|
|
from_date: date = Query(...),
|
|
to_date: date = Query(...),
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
slots = await slot_service.list_slots(db, from_date, to_date, available_only=False)
|
|
return ok(slots)
|
|
|
|
|
|
@router.post("/slots", status_code=201)
|
|
async def create_slot(
|
|
body: SlotCreate,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
slot = await slot_service.create_slot(db, body)
|
|
return ok(slot)
|
|
|
|
|
|
@router.patch("/slots/{slot_id}")
|
|
async def update_slot(
|
|
slot_id: str,
|
|
body: UpdateSlotRequest,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
slot = await slot_service.update_slot(db, slot_id, body.is_blocked, body.block_reason)
|
|
return ok(slot)
|
|
|
|
|
|
@router.delete("/slots/{slot_id}", status_code=204)
|
|
async def delete_slot(
|
|
slot_id: str,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
await slot_service.delete_slot(db, slot_id)
|
|
|
|
|
|
@router.post("/slots/generate")
|
|
async def generate_slots(
|
|
body: GenerateSlotsRequest,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
created = await slot_service.generate_slots(db, body.from_date, body.to_date)
|
|
return ok({"created": created})
|
|
|
|
|
|
# ── Weekly schedule ───────────────────────────────────────────────────────────
|
|
|
|
@router.get("/schedule")
|
|
async def get_schedule(
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
schedule = await slot_service.get_schedule(db)
|
|
return ok(schedule)
|
|
|
|
|
|
@router.post("/schedule", status_code=201)
|
|
async def create_schedule(
|
|
body: WeeklyScheduleCreate,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
entry = await slot_service.create_schedule_entry(db, body)
|
|
return ok(entry)
|
|
|
|
|
|
@router.delete("/schedule/{schedule_id}", status_code=204)
|
|
async def delete_schedule(
|
|
schedule_id: str,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
await slot_service.delete_schedule_entry(db, schedule_id)
|
|
|
|
|
|
# ── Blocked dates ─────────────────────────────────────────────────────────────
|
|
|
|
@router.get("/blocked-dates")
|
|
async def get_blocked_dates(
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
dates = await slot_service.get_blocked_dates(db)
|
|
return ok(dates)
|
|
|
|
|
|
@router.post("/blocked-dates", status_code=201)
|
|
async def add_blocked_date(
|
|
body: BlockedDateCreate,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
entry = await slot_service.add_blocked_date(db, body.date, body.reason)
|
|
return ok(entry)
|
|
|
|
|
|
@router.delete("/blocked-dates/{blocked_date_id}", status_code=204)
|
|
async def remove_blocked_date(
|
|
blocked_date_id: str,
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
await slot_service.remove_blocked_date(db, blocked_date_id)
|