Commit graph

59 commits

Author SHA1 Message Date
Vadym Samoilenko
433512fc78 feat: task time slots, calendar block drag-to-move
- TaskForm: add start_time/end_time fields; on save emits optional
  block payload so PlannerView creates a PlannedBlock automatically
- PlannerView: handleSave accepts block param, calls createBlock after
  task creation when time is provided
- CalendarBlock: planned blocks with task_id get draggable="true" +
  @dragstart emitting blockDragStart event
- CalendarGrid: forward blockDragStart to useCalendarDnD
- useCalendarDnD: onBlockDragStart stores block_id + duration in
  dataTransfer; onDrop handles both move-existing-block and
  create-new-block paths

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 21:19:49 +01:00
Vadym Samoilenko
732e692c8a fix: implement 5-phase contract fixes, devops page, AI summaries, expanded assistant
Phase 1 — Contract bugs:
- Add progress_pct/budget_hours to ProjectHours schema and compute from ProjectBudget
- Add hours/pct to ToolUsage schema, compute in project_detail endpoint
- Fix ProjectDetailView to use correct nested data shape (data.project.*, data.sessions, etc.)
- Fix GenerateReportIn: rename field date → period_date; update reports router
- Fix tasks list date filter: use Query(alias='date') instead of positional arg
- Fix AzureIntegration/AzureWorkItem types: org→organization, id type, ado_id, sync_enabled
- Fix devops API payload and SettingsView to use organization field
- Fix TaskForm ADO work item selector to use wi.ado_id for display, wi.id for value
- Add light theme CSS variables in :root, keep dark in .dark class
- Remove hardcoded class='dark' from HTML, add theme persistence script
- TopBar: persist dark/light to localStorage on toggle
- DashboardView: switch monthly() → timeline() endpoint for charts
- DOW endpoint: add from/to date range filtering

Phase 2 — Planner:
- Add projects API endpoint and Pinia store
- Add project picker to TaskForm
- Fix ADO work item display (#ado_id not #id)

Phase 3 — Calendar:
- getWeekDays() accepts weekLength 5|7 parameter
- Calendar store: add weekLength ref, setWeekLength(), update fetchCurrentView range
- CalendarToolbar: add 5d/7d toggle buttons; fix dateLabel to use days[days.length-1]
- CalendarView: clicking session block navigates to project-detail/:id/:date
- project-detail route: add optional :date? param; ProjectDetailView filters by date
- DnD resize: send start_at alongside end_at (PlannedBlockIn requires both fields)

Phase 4 — AI session summaries:
- Add ai_title/ai_result columns to Session model
- Alembic migration 0006 for new columns
- New ai_session_summary service using Claude Haiku
- Session summarize endpoint: POST /api/dashboard/sessions/{id}/summarize
- Scheduler job: summarize sessions without ai_title every 10 minutes
- SessionOut schema: add ai_title/ai_result fields
- ProjectDetailView: show ai_title as primary, ai_result as subtitle; sparkle button to generate

Phase 5 — Expanded AI assistant:
- Add 14 new tools: list/create/update/delete/complete tasks, prioritize_day,
  schedule_task, auto_schedule_day, list_projects, list/delete manual entries,
  generate_report, search_sessions, list_work_items
- Import PlannedBlock and AzureWorkItem in assistant service
- Update SYSTEM_PROMPT to describe full agent capabilities
- Agentic loop: 5 → 10 rounds max
- AssistantWidget: add tool labels for all new tools, update quick hints

New files: DevopsView.vue, projects store/API, ai_session_summary.py, migration 0006

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:13:28 +01:00
Vadym Samoilenko
2b4fd5dee8 feat: premium redesign — cyan/teal design system + bug fixes
Design:
- Replace purple SaaS theme with operational dark navy (#0b1020) + cyan (#57c7ff)
- Satoshi + JetBrains Mono fonts via CSS @import
- KpiCard: hero variant for Total Hours, tabular-nums for all values
- Sidebar: cyan active state instead of amber, dimmer inactive icons
- Dashboard: skeleton loading states for all charts, polished empty states
- TopBar: cyan user avatar consistent with sidebar

Fixes:
- Live Feed: SSE URL was /api/events/stream (wrong) → /api/events; pass JWT as ?token= query param
- Dashboard: default preset changed to 'today' instead of '30d'
- index.html: Cache-Control: no-cache to prevent stale asset issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 20:08:44 +01:00
Vadym Samoilenko
3e48a789fc fix: guard tool.pct against undefined before toFixed 2026-05-06 19:51:23 +01:00
Vadym Samoilenko
d791cda770 fix: auth store sends JSON body instead of form-urlencoded (backend expects LoginRequest JSON) 2026-05-06 19:50:03 +01:00
Vadym Samoilenko
8c1f338c73 fix: remove BASE prefix from healthz/static/SPA routes (root_path strips prefix) 2026-05-06 19:47:18 +01:00
Vadym Samoilenko
7e9727587e fix: healthcheck uses host curl, not exec inside container
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:35:26 +01:00
Vadym Samoilenko
162f4ba822 feat: AI assistant widget, design system upgrade via 21st-dev Magic
Backend:
- Add AI assistant service with gap detection, anomaly analysis, Anthropic tool_use streaming
- Add assistant router (chat SSE, history, flags CRUD, session categorization)
- Fix agentic loop: text+tool_use in single assistant message per Anthropic spec
- Migrate logging from stdlib to structlog in assistant modules
- Fix migration 0005: UUID type for ai_flags/assistant_messages FKs

Frontend:
- Fix vite base path → /cc-dashboard/static/ to match FastAPI StaticFiles mount
- Redesign Sidebar: gradient background, amber gradient active state, 44px touch targets, user avatar
- Redesign KpiCard: corner decorations, ring border, trend icon, baseline bar (21st.dev pattern)
- Redesign TopBar: backdrop-blur, sticky, gradient user avatar, sign-out button
- Improve AssistantWidget: fix setInterval leak, aria-labels, proper markdown block parser
- Fix AssistantWidget renderMarkdown: line-by-line parser for correct list/header nesting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:34:17 +01:00
Vadym Samoilenko
2118187c76 fix: remove add_logger_name from structlog — incompatible with PrintLoggerFactory
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:18:11 +01:00
Vadym Samoilenko
95fef1fe64 fix: migration 0005 — use postgresql.UUID for ai_flags/assistant_messages FK columns
String(36) cannot reference UUID column users.id in PostgreSQL.
Replace with postgresql.UUID(as_uuid=False) to match existing pattern in 0003.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:15:54 +01:00
Vadym Samoilenko
30764d8998 merge: planning hub — Vue 3 frontend, AI assistant, ADO sync, calendar, tasks
Merges feat/planning-hub into main:
- Complete Vue 3 + Vite + TypeScript + Tailwind frontend (replaces vanilla JS)
- Corporate planning: calendar, daily task planner, drag-to-calendar DnD
- Azure DevOps two-way sync (work items pull + CompletedWork push)
- AI assistant chat widget with streaming tool-use (gap detection, anomaly flags)
- Session categories, manual time entries, project budgets, tags, CSV/ICS export
- APScheduler: ADO sync every 15min, AI reports at 20:00, anomaly scan hourly
- Alembic migrations: 0003 (corporate tables), 0004 (indexes), 0005 (categories + ai_flags + assistant_messages)
- structlog, Fernet PAT encryption, audit log, admin /recompute-stats endpoint
- deploy.sh updated: removed dead static sync, added healthcheck loop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:11:16 +01:00
Vadym Samoilenko
9495aebef1 fix: TypeScript errors in TaskForm/CalendarView/PlannerView, rebuild frontend
- TaskForm: change project_id/azure_work_item_id form state from null to undefined
- CalendarView/PlannerView: accept TaskCreatePayload | TaskUpdatePayload from @save emit
- Rebuild: web assets with AssistantWidget included

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:01:58 +01:00
Vadym Samoilenko
2533f4b046 feat: AI assistant chat widget + session categories + anomaly detection
- Migration 0005: sessions.category, manual_entries.category, ai_flags table, assistant_messages table
- AiFlag + AssistantMessage ORM models added to models.py
- src/services/assistant.py: gap detection (>30min gaps, low active/wall ratio, uncategorized sessions, long days), Anthropic tool_use loop with 7 tools (get_daily_summary, get_sessions, get_project_stats, detect_anomalies, create_manual_entry, set_session_category, get_unresolved_flags)
- src/routers/assistant.py: POST /api/assistant/chat (SSE streaming), GET/DELETE /history, GET /flags, POST /flags/scan, PATCH /flags/:id/resolve, PATCH /sessions/:id/category
- APScheduler: hourly anomaly scan for all users, persists AiFlag records
- AssistantWidget.vue: floating bottom-right chat panel, streaming SSE rendering, quick-hint chips, tool activity indicators, red badge for unresolved flags, markdown rendering, clear history

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:59:51 +01:00
Vadym Samoilenko
ff52d502b8 feat: add complete Vue 3 frontend in web/ directory
Full Vue 3 + Vite + TypeScript + Tailwind SPA replacing the vanilla JS static frontend.
Includes router, Pinia stores (auth/tasks/calendar/devops), axios API client with all
endpoints, UI components (Button/Card/Dialog/Badge/Input/etc), calendar grid with
lane-packing algorithm and DnD support, SSE live feed, and all 11 views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:52:43 +01:00
Vadym Samoilenko
1071ac2f4d fix: idempotent report generation — return existing if already generated
Prevents duplicate reports and Anthropic API spam when the generate
endpoint is called multiple times for the same period.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:50:40 +01:00
Vadym Samoilenko
14cdd2f983 fix: CSV export uses wall-clock hours (not active_hours) for sessions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:49:41 +01:00
Vadym Samoilenko
d6aea758f0 feat: composite DB indexes + admin recompute-stats endpoint
- Migration 0004: composite indexes (user_id, date) on sessions and
  daily_stats; (user_id, start_at, end_at) on planned_blocks and
  manual_entries — speeds up calendar and monthly/dow queries
- admin POST /recompute-stats: recomputes all DailyStat records from raw
  session wall-clock intervals; use once after upgrading to fix historical
  data that was stored as active_hours

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:49:14 +01:00
Vadym Samoilenko
264ee297af fix: create src/static stub on startup if frontend not yet built
Prevents FastAPI from crashing with StaticFiles mount when the Vue
frontend hasn't been built yet (cd web && npm run build).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:48:07 +01:00
Vadym Samoilenko
ed6b86d690 fix: add pytest/pytest-asyncio/aiosqlite to requirements for test suite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:47:20 +01:00
Vadym Samoilenko
ac6ba28008 fix: calendar datetime filters, tasks N+1 query, AI report hours accuracy
- calendar.py: replace fragile string-concat datetime filters with proper
  datetime.combine(date, time.min/max) for PlannedBlock and ManualEntry overlap
- tasks.py list_tasks: batch-load projects + work items in 2 queries instead
  of N individual lookups per task (fixes N+1 performance issue)
- ai_reports.py: use wall-clock union hours + overhead for report totals,
  consistent with KPI summary endpoint; import _union_hours from aggregator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:46:47 +01:00
Vadym Samoilenko
908205da44 fix: consistent wall-clock hours across all dashboard endpoints
- aggregator.py: store union of (start_at, end_at) intervals, not sum of
  active_hours — active_hours is Claude's estimate, not wall-clock time
- dashboard/monthly: compute from session intervals + overhead per day
  instead of DailyStat.total_hours (which lacked overhead and used active_hours)
- dashboard/dow: same fix — query sessions directly, apply union + overhead
- Both monthly and dow now match the KPI summary card exactly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:45:04 +01:00
Vadym Samoilenko
9d9e8e82d4 feat: corporate planning hub backend — tasks, calendar, ADO, AI reports
- Alembic 0003_corporate: 10 new tables (tasks, planned_blocks, manual_entries,
  project_budgets, tags, task_tags, azure_integrations, azure_work_items,
  ai_reports, audit_log) + session.task_id FK
- New routers: calendar, tasks, manual_entries, budgets, tags, devops, exports, reports
- Services: crypto (Fernet PAT encryption), audit log, Mailgun email,
  APScheduler (ADO sync every 15 min, daily AI report at 20:00, weekly Sunday 21:00)
- Azure DevOps two-way sync: pull assigned work items, push CompletedWork on task complete
- AI reports: Anthropic API summaries or plain-stats fallback, sent via Mailgun
- structlog JSON/console logging, LoggingMiddleware, updated main.py lifespan
- pyproject.toml (ruff/mypy/pytest config), CI workflow, pre-commit hooks
- Schemas: CalendarBlock, Task*, ManualEntry*, Budget*, Tag*, AzureIntegration*,
  AiReport*, SyncReport

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:44:26 +01:00
Vadym Samoilenko
57655056e3 fix: restore daily_overhead_hours on top of union hours
overhead now applies to union-based totals (not inflated session sums),
so the setting in UI works correctly again

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 17:50:30 +01:00
Vadym Samoilenko
c178a00b74 fix: accurate time tracking — union intervals for totals, no double-counted overhead
- summary/timeline: use union of session start_at/end_at intervals per day
  instead of summing active_hours, so parallel sessions don't inflate totals
- projects: show raw session hours per project (correct for per-project billing)
- monthly: remove daily_overhead_hours addition
- models: change daily_overhead_hours default 2.0 → 0.0
- daily_overhead_hours setting in user profile still works but defaults to 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 17:39:55 +01:00
Vadym Samoilenko
2eee1a7c02 fix: projects page shows all-time data, not just last 30 days
When /api/dashboard/projects is called without date params (as the
Projects page does), remove the date filter so all 36 projects appear
regardless of last activity date. Explicit date params still filter
as before (dashboard summary tab).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:57:07 +01:00
Vadym Samoilenko
474e35c0ea feat: dashboard preset filters (Today/7d/30d), live feed loads today sessions on mount 2026-03-26 15:33:52 +00:00
Vadym Samoilenko
dd92268e80 fix: setup script quoting (HOOK_CMD paths), quoted heredoc, proper JSON connection test 2026-03-26 15:29:17 +00:00
Vadym Samoilenko
015c5efbbd fix: try underscore variant when dash-slug path not found on disk 2026-03-26 15:23:03 +00:00
Vadym Samoilenko
dd902ebb61 fix: repo path resolution via slug+ROOT_PATH, add trailing slash to repo URL 2026-03-26 15:21:43 +00:00
Vadym Samoilenko
4e950a2a67 feat: auto-detect repo URL from git remote, fix project slug (full path not last segment) 2026-03-26 15:19:40 +00:00
Vadym Samoilenko
46db7476a3 fix: timeline chart linear scale, date filter on daily chart, null-safe chart init 2026-03-26 15:16:12 +00:00
Vadym Samoilenko
0ee3c3fbd9 fix: filter collector to only scan projects under CC_ROOT_PATH 2026-03-26 15:09:49 +00:00
Vadym Samoilenko
4d69a4ac64 feat: project metadata fields (client, job#, repo) with inline editing + time calc audit 2026-03-26 15:08:14 +00:00
Vadym Samoilenko
9ebe5a92fa fix: filter invalid timestamps in timeline chart to prevent Chart.js crash 2026-03-26 14:50:28 +00:00
Vadym Samoilenko
94ae1af2c2 fix: eager load ApiKey.user with selectinload to avoid lazy load in async context 2026-03-26 14:46:14 +00:00
Vadym Samoilenko
080eec32d1 fix: use execute(delete()) instead of session.delete() to avoid MissingGreenlet 2026-03-26 14:44:19 +00:00
Vadym Samoilenko
7f8048dcee fix: accept HTTP 422 as valid connection (key works, empty body rejected); remove curl preview 2026-03-26 14:43:03 +00:00
Vadym Samoilenko
6b2f811ee5 fix: use heredoc temp file for Python settings update — avoid quote conflicts in bash 2026-03-26 14:37:56 +00:00
Vadym Samoilenko
d7198fdc59 fix: don't rebuild DOM while modal is open — reload table only on close 2026-03-26 14:34:40 +00:00
Vadym Samoilenko
99f1ff6bec fix: escape bash ${VAR} in JS template literal — use array join instead 2026-03-26 14:31:30 +00:00
Vadym Samoilenko
0c7a7b8082 feat: logout button, revoke/delete keys, setup script download
- Sidebar: Add Sign Out button below user info
- Keys API: split revoke (PATCH /{id}/revoke) and delete (DELETE /{id})
- Keys page: Revoke + Delete buttons per key; delete removes from DB
- New key flow: after creation show download setup script step
  - Script embeds API key, asks for projects root folder
  - Downloads cc-collector.py, merges Claude Code hook into settings.json
  - Tests connection and reports result

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:05:14 +00:00
Vadym Samoilenko
354aec0995 style: brighter text colors — white primary, lighter muted 2026-03-26 13:59:55 +00:00
Vadym Samoilenko
b7b49110da fix: login page width 100% to center within flex #app container 2026-03-26 13:59:14 +00:00
Vadym Samoilenko
c42d4491cb fix: correct static file layout for Apache Alias serving
Apache serves /cc-dashboard/* from /var/www/html/cc-dashboard/ directly.
index.html goes to the root, CSS/JS to static/ subdirectory to match
/cc-dashboard/static/... paths referenced in index.html.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:57:33 +00:00
Vadym Samoilenko
805566f1e5 redesign: premium dark UI — Montserrat, black + #FFC407
- Full CSS redesign: OLED black (#0a0a0a), #FFC407 accent, Montserrat 400–900
- Bold high-contrast typography (900 weight values, -0.04em tracking)
- No glassmorphism — flat sharp surfaces with subtle borders
- KPI cards: 2px yellow top accent bar on hover + lift shadow
- Login: centered card with yellow top bar + subtle grid bg pattern
- Modals: yellow top accent line
- Active nav: solid yellow bg, black text
- Buttons: yellow CTA with glow, ghost with yellow hover
- Badges: pill with yellow tint
- Favicon: dark card with yellow CC

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:51:18 +00:00
Vadym Samoilenko
968738a245 chore: add deploy.sh script
git pull → sync static to /var/www/html → docker compose build → restart app
Migrations run automatically on container start via alembic upgrade head.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:46:45 +00:00
Vadym Samoilenko
b90e335f2b feat: apply glassmorphism design system from Oliver dashboard
- Full CSS rewrite: deep dark bg (#060714), glass cards with backdrop-filter,
  Fira Sans/Code fonts, indigo/violet accent palette replacing yellow
- Add animated orb background to index.html
- Update favicon to indigo gradient
- Wrap admin/settings forms in <form> tags with autocomplete attributes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:43:36 +00:00
Vadym Samoilenko
436a089e63 fix: wrap login in form tag, remove QUIC for SSE endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:22:48 +00:00
Vadym Samoilenko
b2777958b3 remove tmp debug logging 2026-03-26 13:19:17 +00:00
Vadym Samoilenko
9d292f80da tmp: debug login to diagnose 401 2026-03-26 13:18:17 +00:00