Fix QA findings: combine todo queries, remove dead prop, add aria-labels
- Merge total_todos and total_incomplete_todos into single DB query (W-04) - Remove unused `days` prop from UpcomingWidget interface (W-03) - Add aria-label to focus/show-past toggle buttons (S-08) - Add zero-duration event guard in CalendarWidget progress calc (S-07) - Combine duplicate date-utils imports (S-01) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b41b0b6635
commit
ac3f746ba3
@ -1,6 +1,6 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func, or_
|
||||
from sqlalchemy import select, func, or_, case
|
||||
from datetime import datetime, date, timedelta
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
@ -84,20 +84,16 @@ async def get_dashboard(
|
||||
projects_by_status_result = await db.execute(projects_by_status_query)
|
||||
projects_by_status = {row[0]: row[1] for row in projects_by_status_result}
|
||||
|
||||
# Total incomplete todos count (scoped to user)
|
||||
total_incomplete_result = await db.execute(
|
||||
select(func.count(Todo.id)).where(
|
||||
Todo.user_id == current_user.id,
|
||||
Todo.completed == False,
|
||||
)
|
||||
# Todo counts: total and incomplete in a single query
|
||||
todo_counts_result = await db.execute(
|
||||
select(
|
||||
func.count(Todo.id).label("total"),
|
||||
func.count(case((Todo.completed == False, Todo.id))).label("incomplete"),
|
||||
).where(Todo.user_id == current_user.id)
|
||||
)
|
||||
total_incomplete_todos = total_incomplete_result.scalar()
|
||||
|
||||
# Total todos count (for progress ring ratio)
|
||||
total_todos_result = await db.execute(
|
||||
select(func.count(Todo.id)).where(Todo.user_id == current_user.id)
|
||||
)
|
||||
total_todos = total_todos_result.scalar()
|
||||
todo_row = todo_counts_result.one()
|
||||
total_todos = todo_row.total
|
||||
total_incomplete_todos = todo_row.incomplete
|
||||
|
||||
# Starred events (upcoming, ordered by date, scoped to user's calendars)
|
||||
starred_query = select(CalendarEvent).where(
|
||||
|
||||
@ -33,6 +33,7 @@ function getProgressPercent(event: DashboardEvent, now: Date): number {
|
||||
if (event.all_day) return 0;
|
||||
const start = new Date(event.start_datetime).getTime();
|
||||
const end = new Date(event.end_datetime).getTime();
|
||||
if (end <= start) return 0;
|
||||
const current = now.getTime();
|
||||
if (current >= end) return 100;
|
||||
if (current <= start) return 0;
|
||||
|
||||
@ -272,7 +272,7 @@ export default function DashboardPage() {
|
||||
<div className="grid gap-3 sm:gap-5 lg:grid-cols-5 animate-slide-up" style={{ animationDelay: '100ms', animationFillMode: 'backwards' }}>
|
||||
{/* Left: Upcoming feed (wider) */}
|
||||
<div className="lg:col-span-3 flex flex-col">
|
||||
<UpcomingWidget items={upcomingData?.items ?? []} days={upcomingData?.days} />
|
||||
<UpcomingWidget items={upcomingData?.items ?? []} />
|
||||
</div>
|
||||
|
||||
{/* Right: Countdown + Today's events + todos stacked */}
|
||||
|
||||
@ -17,13 +17,11 @@ import type { UpcomingItem } from '@/types';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getRelativeTime } from '@/lib/date-utils';
|
||||
import { toLocalDatetime } from '@/lib/date-utils';
|
||||
import { getRelativeTime, toLocalDatetime } from '@/lib/date-utils';
|
||||
import api from '@/lib/api';
|
||||
|
||||
interface UpcomingWidgetProps {
|
||||
items: UpcomingItem[];
|
||||
days?: number;
|
||||
}
|
||||
|
||||
const typeConfig: Record<string, { hoverGlow: string; pillBg: string; pillText: string; label: string }> = {
|
||||
@ -216,6 +214,7 @@ export default function UpcomingWidget({ items }: UpcomingWidgetProps) {
|
||||
focusMode ? 'bg-accent/15 text-accent' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'
|
||||
)}
|
||||
title={focusMode ? 'Show all days' : 'Focus: Today + Tomorrow'}
|
||||
aria-label={focusMode ? 'Show all days' : 'Focus: Today + Tomorrow'}
|
||||
>
|
||||
<Target className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
@ -226,6 +225,7 @@ export default function UpcomingWidget({ items }: UpcomingWidgetProps) {
|
||||
showPast ? 'bg-accent/15 text-accent' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'
|
||||
)}
|
||||
title={showPast ? 'Hide past events' : 'Show past events'}
|
||||
aria-label={showPast ? 'Hide past events' : 'Show past events'}
|
||||
>
|
||||
{showPast ? <Eye className="h-3.5 w-3.5" /> : <EyeOff className="h-3.5 w-3.5" />}
|
||||
</button>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user