diff --git a/backend/app/routers/projects.py b/backend/app/routers/projects.py index 06ebde6..cd2b1f2 100644 --- a/backend/app/routers/projects.py +++ b/backend/app/routers/projects.py @@ -4,7 +4,7 @@ from sqlalchemy import select from sqlalchemy.orm import selectinload from typing import List, Optional from datetime import date, timedelta -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from app.database import get_db from app.models.project import Project @@ -20,6 +20,7 @@ router = APIRouter() class ReorderItem(BaseModel): + model_config = ConfigDict(extra="forbid") id: int sort_order: int diff --git a/frontend/src/hooks/useCalendars.ts b/frontend/src/hooks/useCalendars.ts index b5e74ed..ee2fa4d 100644 --- a/frontend/src/hooks/useCalendars.ts +++ b/frontend/src/hooks/useCalendars.ts @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react'; +import { useState, useMemo } from 'react'; import { useQuery } from '@tanstack/react-query'; import api from '@/lib/api'; import type { Calendar, SharedCalendarMembership } from '@/types'; @@ -18,10 +18,11 @@ export function useCalendars({ pollingEnabled = false }: UseCalendarsOptions = { // AS-4: Gate shared-calendar polling on whether user participates in sharing. // Saves ~12 API calls/min for personal-only users. - // Use a ref to latch "has sharing" once discovered, so polling doesn't flicker. - const hasSharingRef = useRef(false); + // Uses useState (not useRef) so changes trigger a re-render and the + // refetchInterval picks up the new value immediately. + const [hasSharing, setHasSharing] = useState(false); const ownsShared = (ownedQuery.data ?? []).some((c) => c.is_shared); - if (ownsShared) hasSharingRef.current = true; + if (ownsShared && !hasSharing) setHasSharing(true); const sharedQuery = useQuery({ queryKey: ['calendars', 'shared'], @@ -29,12 +30,12 @@ export function useCalendars({ pollingEnabled = false }: UseCalendarsOptions = { const { data } = await api.get('/shared-calendars'); return data; }, - refetchInterval: pollingEnabled && hasSharingRef.current ? 5_000 : false, + refetchInterval: pollingEnabled && hasSharing ? 5_000 : false, staleTime: 3_000, }); // Also latch if user is a member of others' shared calendars - if ((sharedQuery.data ?? []).length > 0) hasSharingRef.current = true; + if ((sharedQuery.data ?? []).length > 0 && !hasSharing) setHasSharing(true); const allCalendarIds = useMemo(() => { const owned = (ownedQuery.data ?? []).map((c) => c.id);