feat(dashboard): redesign with active bookings, calendar, and compact stats
Major Dashboard improvements focusing on active reservations and calendar view: Frontend changes: - Add ActiveBookings component showing in-progress bookings with progress bars - Add DashboardCalendar component with read-only calendar view of all user bookings - Refactor Dashboard layout: active bookings → stats grid → calendar → activity - Remove redundant Quick Actions and Available Spaces sections - Make Quick Stats compact (36px icons, 20px font) and clickable (router-link) - Add datetime utility functions (isBookingActive, getBookingProgress, formatRemainingTime) - Fix MyBookings to read status query parameter from URL - Auto-refresh active bookings every 60s with proper cleanup Backend changes: - Add GET /api/bookings/my/calendar endpoint with date range filtering - Fix Google Calendar sync in reschedule_booking and admin_update_booking - Add Google OAuth environment variables to .env.example Design: - Dark mode compatible with CSS variables throughout - Mobile responsive (768px breakpoint, 2-column stats grid) - CollapsibleSection pattern for all dashboard sections - Progress bars with accent colors for active bookings Performance: - Optimized API calls (calendar uses date range filtering) - Remove duplicate calendar data loading on mount - Computed property caching for stats and filtered bookings - Memory leak prevention (setInterval cleanup on unmount) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,3 +17,8 @@ SMTP_USER=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_FROM_ADDRESS=noreply@space-booking.local
|
||||
SMTP_ENABLED=false
|
||||
|
||||
# Google Calendar Integration
|
||||
GOOGLE_CLIENT_ID=your_google_client_id_here
|
||||
GOOGLE_CLIENT_SECRET=your_google_client_secret_here
|
||||
GOOGLE_REDIRECT_URI=https://your-domain.com/api/integrations/google/callback
|
||||
|
||||
@@ -12,7 +12,11 @@ from app.models.space import Space
|
||||
from app.models.user import User
|
||||
from app.services.audit_service import log_action
|
||||
from app.services.email_service import send_booking_notification
|
||||
from app.services.google_calendar_service import create_calendar_event, delete_calendar_event
|
||||
from app.services.google_calendar_service import (
|
||||
create_calendar_event,
|
||||
delete_calendar_event,
|
||||
update_calendar_event,
|
||||
)
|
||||
from app.services.notification_service import create_notification
|
||||
from app.schemas.booking import (
|
||||
AdminCancelRequest,
|
||||
@@ -202,6 +206,37 @@ def get_my_bookings(
|
||||
return [BookingWithSpace.model_validate(b) for b in bookings]
|
||||
|
||||
|
||||
@bookings_router.get("/my/calendar", response_model=list[BookingWithSpace])
|
||||
def get_my_bookings_calendar(
|
||||
start: Annotated[datetime, Query(description="Start datetime (ISO format)")],
|
||||
end: Annotated[datetime, Query(description="End datetime (ISO format)")],
|
||||
db: Annotated[Session, Depends(get_db)],
|
||||
current_user: Annotated[User, Depends(get_current_user)],
|
||||
) -> list[BookingWithSpace]:
|
||||
"""
|
||||
Get user's bookings for calendar view within date range.
|
||||
|
||||
Query parameters:
|
||||
- **start**: Start datetime in ISO format (e.g., 2024-01-01T00:00:00)
|
||||
- **end**: End datetime in ISO format (e.g., 2024-01-31T23:59:59)
|
||||
|
||||
Returns bookings with status approved or pending, sorted by start time.
|
||||
"""
|
||||
bookings = (
|
||||
db.query(Booking)
|
||||
.join(Space, Booking.space_id == Space.id)
|
||||
.filter(
|
||||
Booking.user_id == current_user.id,
|
||||
Booking.start_datetime < end,
|
||||
Booking.end_datetime > start,
|
||||
Booking.status.in_(["approved", "pending"]),
|
||||
)
|
||||
.order_by(Booking.start_datetime)
|
||||
.all()
|
||||
)
|
||||
return [BookingWithSpace.model_validate(b) for b in bookings]
|
||||
|
||||
|
||||
@bookings_router.post("", response_model=BookingResponse, status_code=status.HTTP_201_CREATED)
|
||||
def create_booking(
|
||||
booking_data: BookingCreate,
|
||||
@@ -873,6 +908,15 @@ def admin_update_booking(
|
||||
detail=errors[0],
|
||||
)
|
||||
|
||||
# Sync with Google Calendar if event exists
|
||||
if booking.google_calendar_event_id:
|
||||
update_calendar_event(
|
||||
db=db,
|
||||
booking=booking,
|
||||
user_id=int(booking.user_id), # type: ignore[arg-type]
|
||||
event_id=booking.google_calendar_event_id,
|
||||
)
|
||||
|
||||
# Log audit
|
||||
log_action(
|
||||
db=db,
|
||||
@@ -1025,6 +1069,15 @@ def reschedule_booking(
|
||||
booking.start_datetime = data.start_datetime # type: ignore[assignment]
|
||||
booking.end_datetime = data.end_datetime # type: ignore[assignment]
|
||||
|
||||
# Sync with Google Calendar if event exists
|
||||
if booking.google_calendar_event_id:
|
||||
update_calendar_event(
|
||||
db=db,
|
||||
booking=booking,
|
||||
user_id=int(booking.user_id), # type: ignore[arg-type]
|
||||
event_id=booking.google_calendar_event_id,
|
||||
)
|
||||
|
||||
# Log audit
|
||||
log_action(
|
||||
db=db,
|
||||
|
||||
Reference in New Issue
Block a user