revert: drop By Date sidebar node and supporting code

Tried it, didn't add value beyond what the date-grouped timeline
already gives. The grouped, sticky-headered timeline (which kicks
in by default whenever sortBy is taken_at) is the better
affordance for date navigation — duplicating that as a sidebar
drilldown was just clutter.

Removes the full stack:
- LeftSidebar: by-date tree node, byDateChildren computation,
  date-year-/date-month- handlers in applyLibraryNode, isItemActive
  branches that matched a date-range filter, the now-unused
  setDateFrom/setDateTo/filterDateFrom/filterDateTo selectors, and
  the Calendar icon import.
- frontend/src/hooks/useDateBucketsQuery.ts deleted entirely.
- api.ts: library.dateBuckets helper and DateBucketYear/Month types.
- backend/app/routers/library.py: GET /library/date_buckets endpoint
  and its strftime aggregation query.

The dateFrom/dateTo filter state stays in filterStore — the
FilterBar still uses it for the "Date" range inputs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 00:19:59 +02:00
parent 7d33e1688a
commit a0c41e38d3
4 changed files with 1 additions and 135 deletions

View File

@@ -47,50 +47,6 @@ async def trigger_scan():
return {"status": "success", "message": "Library scan started"}
@router.get("/date_buckets")
async def get_date_buckets(db: AsyncSession = Depends(get_db)):
"""Aggregate non-discarded photos by year and month based on taken_at.
Used by the LeftSidebar 'By Date' subtree to render a year-then-month
drilldown without fetching every photo. Returns:
[{ year: 2026, count: 5, months: [{ month: 4, count: 5 }] }]
"""
year_expr = func.strftime('%Y', Photo.taken_at)
month_expr = func.strftime('%m', Photo.taken_at)
result = await db.execute(
select(
year_expr.label('year'),
month_expr.label('month'),
func.count(Photo.id).label('count'),
)
.where(
Photo.is_discarded == False, # noqa: E712
Photo.taken_at.is_not(None),
)
.group_by(year_expr, month_expr)
.order_by(year_expr.desc(), month_expr.desc())
)
rows = result.all()
years: dict[int, dict] = {}
for year_str, month_str, count in rows:
if not year_str or not month_str:
continue
try:
year = int(year_str)
month = int(month_str)
except ValueError:
continue
bucket = years.setdefault(
year, {'year': year, 'count': 0, 'months': []}
)
bucket['count'] += int(count)
bucket['months'].append({'month': month, 'count': int(count)})
# Already ordered by the SQL ORDER BY (newest first), preserve insertion.
return list(years.values())
@router.get("/scan/status")
async def get_scan_status(db: AsyncSession = Depends(get_db)):
"""Get current scan status"""