Commit graph

73 commits

Author SHA1 Message Date
Vadym Samoilenko
70a6b5e6ef fix(ai): strip markdown code block from Anthropic response before JSON parse
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
Claude Haiku wraps JSON in ```json ... ``` blocks, causing json.loads to fail
silently. The except clause swallowed the error, leaving ai_title empty for all
303 sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:25:14 +01:00
Vadym Samoilenko
e0f541b082 fix(auth): rebuild frontend with session-restore-before-router fix
Static assets were built before the auth fix commit (13:29 vs 14:14),
so the deployed container ran old code that registered the router before
awaiting authStore.init() — causing page refresh to redirect to login.

Also adds migration 0011 to set job_number=2940884 for Oliver client
projects that had no job number assigned.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 09:52:04 +01:00
Vadym Samoilenko
1ff57fff15 fix(router): redirect authenticated users away from login page
When init() restores session from cookie and user's current URL is /login,
the guard was calling next() without checking isAuthenticated — leaving the
user on the login page despite having a valid session. Now redirects to
dashboard if already authenticated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:32:29 +01:00
Vadym Samoilenko
c10932649d fix(auth): set refresh cookie path to "/" for proxy compatibility
Narrow path "/cc-dashboard/api/auth" caused matching issues depending on
how the reverse proxy presents headers to the browser. Using "/" is safe
since the cookie is HttpOnly and the server validates the token type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:20:25 +01:00
Vadym Samoilenko
ff8b20857f fix(ingest): preserve active_hours when backfill sends zero
On conflict update now skips active_hours if incoming value is 0,
preventing the cost-backfill script from clobbering existing session
hours with its sentinel 0.0 value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:13:48 +01:00
Vadym Samoilenko
0148b81e9c fix(auth): correct refresh cookie path for reverse proxy
Cookie was set with path="/api/auth" but browser-visible URL is
"/cc-dashboard/api/auth/..." — browser never sent the cookie back.
Fix: derive path from BASE_PATH so it matches the full proxy-prefixed URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:04:11 +01:00
Vadym Samoilenko
fde9b61465 feat(cost): track Claude Code token usage and cost per project
- cc-collector.py: extract input/output/cache tokens from JSONL usage
  fields and calculate cost_usd using model-based pricing table
- Session model: add input_tokens, output_tokens, cost_usd columns
- Migration 0010: ALTER TABLE sessions ADD cost columns
- Ingest: persist cost fields on upsert (updated on every sync)
- Dashboard /projects: aggregate total_cost_usd per project from sessions
- ProjectHours schema + ProjectSummary TS type: expose total_cost_usd
- ProjectsView: replace Budget% column with "Cost $" showing total spend;
  Grid cards show CC Cost row when cost > 0
- backfill_session_costs.py: one-time script to populate cost for all
  historical sessions from local JSONL files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 12:47:26 +01:00
Vadym Samoilenko
c3022c0c66 feat(projects): replace list view with DataTable — search, column filters, resize, sort
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 12:36:16 +01:00
Vadym Samoilenko
26127061ec fix: OMG auto-sync, Projects OMG# column, ADO OMG Deliverable Number, session persistence
- Auto-create/update OmgEntry when Project.job_number changes (PATCH /api/projects);
  delete stale entry on clear; sync name/client when those fields change too
- Backfill script: scripts/backfill_omg_from_projects.py
- Projects List-view: add OMG # column with link to /omg?highlight=<job_number>;
  Grid-view badge also made clickable; OmgView supports ?highlight= deep-link with scroll+highlight
- AzureWorkItem: add omg_number column (migration 0009), extracted from
  fields_json[Custom.OMGDeliverableNumber] on sync; DevOps table shows OMG # column
  with CC-project link when matched; toolbar badge shows count of items without OMG #
- Session no longer lost on F5: refresh_token moved to HttpOnly+SameSite=Lax cookie;
  authStore.init() restores session on app start; axios interceptor retries on 401
  via cookie refresh before logging out; POST /api/auth/logout clears cookie

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 12:30:40 +01:00
Vadym Samoilenko
b7a1dc9fdf feat: OMG project dropdown + DevOps toolbar + ADO full fields
OMG page:
- Form now has "Fill from project" dropdown that auto-fills name/client/job#
  from any cc-dashboard project (projects loaded in parallel on mount)
- Job # placeholder updated to show numeric format

DevOps Work Items:
- Count "19" moved into the section heading next to "Work Items" title
- DataTable toolbar (separate row with count+filter) hidden via showToolbar=false
- DataTable: add showToolbar prop (default true) to control toolbar visibility

ADO sync:
- Fetch all fields from ADO API (fields=None) to capture custom fields
  including "OMG Deliverable Number" in fields_json
- Expose fields_json in AzureWorkItemOut schema for frontend inspection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:55:25 +01:00
Vadym Samoilenko
dacbe1de6e feat(omg): add CC Project column linked by job_number
On the OMG page, each entry now shows a "CC Project" column that finds
the matching cc-dashboard project by job_number and links to its detail page.
Projects are loaded in parallel with OMG entries on mount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:44:51 +01:00
Vadym Samoilenko
82208cab61 chore(build): update static assets for ui-polish release
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 11:36:38 +01:00
Vadym Samoilenko
36118cb759 feat: replace Planka with in-app Kanban + add OMG page
- Remove Planka: docker-compose services, apache /board/ proxy, env vars, custom CSS dir
- Add Kanban board at /tasks: 4 columns (To Do / Doing / Testing / Done),
  native HTML5 drag-and-drop, card modal (TaskForm reuse), per-column "+" button
- Add 'testing' status to Task model validator and frontend union type
- Add GET /api/tasks/{id} endpoint (was missing, frontend already called it)
- Enrich DevOps clone: live-fetches description, AC, assignee, iteration,
  comments and attachments from ADO; renders as Markdown in task.notes
- Add /omg page: standalone project/client/job# registry with inline editing
  and create/edit/delete dialog; backed by new omg_entries table (migration 0008)
- Add omg router to main.py; add OMG + Tasks to sidebar and router
- Fix dead /planner link on Dashboard -> /tasks

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 14:09:36 +01:00
Vadym Samoilenko
7c15f884c1 fix: Planka admin user + branding CSS; project job_number edit form; report filename; devops title
- docker-compose: add DEFAULT_ADMIN_* passthrough + CUSTOM_UI_OVERRIDE_STYLESHEETS + mount planka-custom/
- planka-custom/planka.css: hide Planka logo/title/footer, override to orange theme matching CC Dashboard
- ProjectDetailView: inline edit form for display_name/client/job_number/repo_url via PATCH /api/projects/:id
- ReportsView: download filename uses report.period_date + report.type (report-2026-05-07-daily.md)
- AppLayout: add devops → 'Azure DevOps' title entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 13:49:00 +01:00
Vadym Samoilenko
09cfc0a89a feat: light theme redesign + DataTable with sort/filter/resize + bug fixes
- Sidebar: white background, orange gradient logo, orange active pill
- TopBar: glassmorphism (white/80 + backdrop-blur-xl)
- AppLayout: warm gradient background mesh
- DataTable: new reusable component with column sort, filter, resize
- DevopsView: rebuilt with DataTable; connection shows "all assigned work items"
- ADO work item URLs: use org-level URL (no project in path)
- CalendarBlock: planned blocks show task title instead of project name
- Reports export: replaced <a download> with fetch+blob to send JWT auth header
- Sidebar Tasks: fixed path /board/ (trailing slash for Apache ProxyPass)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 13:15:19 +01:00
Vadym Samoilenko
e16e4a16a5 fix(project-detail): show all-time data when no date range given
Default 30d window hid projects inactive for >30 days (e.g. Enterprise Ai Hub
Nexus, last session 2026-04-01). Project detail now returns all history unless
explicit from/to params are passed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:52:51 +01:00
Vadym Samoilenko
e401f9a60b feat(planka): add Planka kanban to docker-compose + wire UI
- docker-compose: add planka + planka-db services (port 1337, isolated volume)
- apache.conf: add ProxyPass /board/ → localhost:1337
- .env.example: add PLANKA_SECRET_KEY + PLANKA_BASE_URL
- Sidebar: rename Planner → Tasks, open /board in new tab via <a>
- Router: remove /planner route
- Delete PlannerView.vue, TaskList.vue, TaskCard.vue (Calendar keeps TaskForm)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:15:25 +01:00
Vadym Samoilenko
cbce273b49 feat(reports): structured prompt sections + HTML/MD export
ai_reports.py:
- Daily context now includes tasks_done_detail/in_progress_detail with
  actual hours and notes for each task
- New prompt structure: Sessions Summary / Tasks Completed / In Progress
  / Blockers / Tomorrow's Plan — replaces generic "motivating summary"
- Fallback markdown updated to match new context shape

reports.py:
- GET /api/reports/{id}/export?format=md|html returns file download
  with proper Content-Disposition header

ReportsView.vue:
- "↓ Markdown" and "↓ HTML" download links shown in expanded report

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:07:29 +01:00
Vadym Samoilenko
99a20ed97f feat(devops): clone work item to Tasks + UI button
Backend: POST /api/devops/work-items/{id}/clone creates a Task with
today's date, copies title and priority from ADO work item.
Frontend DevopsView: + icon button on each row, spinner while cloning,
success/error toasts on completion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:05:31 +01:00
Vadym Samoilenko
48b2e71daa fix(devops): account-wide WIQL sync + priority/project columns in UI
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
- ADO client: query_work_items_account + get_work_items_batch_account
  use org-level URLs, removing the project-scoped URL that 400'd on
  projects with spaces in their names
- sync.py: switched to account-scope methods, added System.CreatedDate
  and Microsoft.VSTS.Common.Priority to FIELDS list
- AzureWorkItem model: @property team_project/priority/created_date
  derived from fields_json (no migration needed)
- AzureWorkItemOut schema: exposed the 3 new derived fields
- DevopsView: table layout with Project / Priority / Created / State columns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:04:10 +01:00
Vadym Samoilenko
75c78635bc fix: planner crash — make Task.tags optional, guard task.tags?.length
Backend TaskOut doesn't include tags field; TaskCard.vue read
task.tags.length causing TypeError on undefined. Making tags optional
in the Task type and using optional chaining is the correct fix since
the tags feature is unused (always empty).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:01:39 +01:00
Vadym Samoilenko
afdb08f11a feat: remove AI assistant widget and all related backend
Deleted router, service (948 LOC), Vue widget, scheduler anomaly_scan
job, and App.vue integration. DB tables intentionally preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 11:59:56 +01:00
Vadym Samoilenko
0d8284a7d7 fix(sidebar): use text-white/60 instead of text-foreground/60
Sidebar is always dark regardless of app theme. text-foreground resolves
to dark color in light theme, making nav items invisible on dark sidebar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 11:28:16 +01:00
Vadym Samoilenko
4ff23500f3 fix(ui): calendar edit/delete, projects view toggle, planner mount race, devops inline form, reports overflow, sidebar contrast, dashboard interactivity
- Calendar: popover now shows Edit/Delete buttons for planned blocks; Edit pre-populates TaskForm; Delete calls deleteBlock API + removes from store. Sessions show read-only label. CalendarView handles ?date= query param from Dashboard chart click.
- Projects: grid/list view toggle with localStorage persistence
- Project detail: Daily Breakdown section (per-day hours + sessions, clickable → filtered view); always-visible 'All time' link; sessions not double-sliced (backend limit bumped 50→200); date-filtered view shows full summaries without line-clamp
- Planner: fixed mount race — onMounted now calls fetchForDate(today) instead of fetchAll()
- DevOps: inline connect form on /devops when not connected; form extracted to DevopsConnectForm.vue shared with Settings
- Reports: added break-words, pre-wrap and overflow-x: auto on prose pre/code blocks to prevent text overflow
- Sidebar: increased text contrast (text-foreground/60 for inactive items, text-foreground/40 for icons)
- Dashboard: KpiCard supports 'to' prop via RouterLink; Projects count + Top Project KPIs link to /projects; Projects list rows link to project detail; Hours by Day bars click → /calendar?date=

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 11:19:46 +01:00
Vadym Samoilenko
96e6f4ee14 feat: replace local auth with Azure AD SSO (MSAL PKCE)
- New POST /api/auth/microsoft endpoint validates Azure ID token via JWKS
- Removed POST /api/auth/login and /change-password
- Added azure_oid + nullable password_hash to users (migration 0007)
- Auto-provisions all @oliver.agency accounts on first SSO login
- Case-insensitive email matching links existing vadymsamoilenko@ account
- DEV_AUTH_BYPASS flag for local development without MSAL
- Frontend: MSAL loginPopup replaces email/password form
- Added scripts/grant_admin.py for role management

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 10:43:19 +01:00
Vadym Samoilenko
dc50dd1d3b fix: chart bars visible + logout 405 + deploy static assets
- Dashboard charts: use pixel heights (not %) and items-end so bars align
  to bottom correctly and are visible at proper scale
- logout(): remove non-existent POST /api/auth/logout call; JWT is stateless,
  client-side token clear is sufficient
- Include rebuilt src/static/ assets so server serves updated frontend bundle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:39:28 +01:00
Vadym Samoilenko
e456fd2b09 fix: chart bars visible + tool usage pct computed correctly
- Dashboard bar charts: increase opacity from /40 to /70 so bars are visible on light theme
- Tools endpoint: compute pct = count/total*100 instead of returning 0.0 default

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 21:27:49 +01:00
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
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
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
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
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