Add tickets & bookings to account page
Add TicketDTO, user_tickets/user_bookings to CalendarService protocol and SqlCalendarService implementation, plus nav links and panel templates for the auth account sub-pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,8 @@ from sqlalchemy import select, update, func
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from shared.models.calendars import Calendar, CalendarEntry, CalendarEntryPost
|
||||
from shared.contracts.dtos import CalendarDTO, CalendarEntryDTO
|
||||
from shared.models.calendars import Calendar, CalendarEntry, CalendarEntryPost, Ticket
|
||||
from shared.contracts.dtos import CalendarDTO, CalendarEntryDTO, TicketDTO
|
||||
|
||||
|
||||
def _cal_to_dto(cal: Calendar) -> CalendarDTO:
|
||||
@@ -47,6 +47,24 @@ def _entry_to_dto(entry: CalendarEntry) -> CalendarEntryDTO:
|
||||
)
|
||||
|
||||
|
||||
def _ticket_to_dto(ticket: Ticket) -> TicketDTO:
|
||||
entry = getattr(ticket, "entry", None)
|
||||
tt = getattr(ticket, "ticket_type", None)
|
||||
cal = getattr(entry, "calendar", None) if entry else None
|
||||
return TicketDTO(
|
||||
id=ticket.id,
|
||||
code=ticket.code,
|
||||
state=ticket.state,
|
||||
entry_name=entry.name if entry else "",
|
||||
entry_start_at=entry.start_at if entry else ticket.created_at,
|
||||
entry_end_at=entry.end_at if entry else None,
|
||||
ticket_type_name=tt.name if tt else None,
|
||||
calendar_name=cal.name if cal else None,
|
||||
created_at=ticket.created_at,
|
||||
checked_in_at=ticket.checked_in_at,
|
||||
)
|
||||
|
||||
|
||||
class SqlCalendarService:
|
||||
|
||||
# -- reads ----------------------------------------------------------------
|
||||
@@ -210,6 +228,38 @@ class SqlCalendarService:
|
||||
)
|
||||
return [_entry_to_dto(e) for e in result.scalars().all()]
|
||||
|
||||
async def user_tickets(
|
||||
self, session: AsyncSession, *, user_id: int,
|
||||
) -> list[TicketDTO]:
|
||||
result = await session.execute(
|
||||
select(Ticket)
|
||||
.where(
|
||||
Ticket.user_id == user_id,
|
||||
Ticket.state != "cancelled",
|
||||
)
|
||||
.order_by(Ticket.created_at.desc())
|
||||
.options(
|
||||
selectinload(Ticket.entry).selectinload(CalendarEntry.calendar),
|
||||
selectinload(Ticket.ticket_type),
|
||||
)
|
||||
)
|
||||
return [_ticket_to_dto(t) for t in result.scalars().all()]
|
||||
|
||||
async def user_bookings(
|
||||
self, session: AsyncSession, *, user_id: int,
|
||||
) -> list[CalendarEntryDTO]:
|
||||
result = await session.execute(
|
||||
select(CalendarEntry)
|
||||
.where(
|
||||
CalendarEntry.user_id == user_id,
|
||||
CalendarEntry.deleted_at.is_(None),
|
||||
CalendarEntry.state.in_(("ordered", "provisional", "confirmed")),
|
||||
)
|
||||
.order_by(CalendarEntry.start_at.desc())
|
||||
.options(selectinload(CalendarEntry.calendar))
|
||||
)
|
||||
return [_entry_to_dto(e) for e in result.scalars().all()]
|
||||
|
||||
# -- batch reads (not in protocol — convenience for blog service) ---------
|
||||
|
||||
async def confirmed_entries_for_posts(
|
||||
|
||||
Reference in New Issue
Block a user