Commit graph

61 commits

Author SHA1 Message Date
Vadym Samoilenko
38d7da93e2 fix(auth): restore session before registering router to prevent refresh redirect
- Move app.use(router) after await authStore.init() so the beforeEach guard
  sees the correct isAuthenticated state on page load
- Fix backfill_session_costs._root_prefix: remove underscore→dash replacement
  that caused paths like /Users/ai_leed/... to not match ~/.claude/projects/ folders

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:14:27 +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
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
80c6b4b47e fix(ui): address code review findings
- ConfirmDialog: guard handleConfirm against AlertDialogAction :disabled bypass
- useDebounce: add { deep: true } so object (filter record) mutations trigger debounce
- useFocusTrap: defer focus restore by 150ms to not interrupt close animation
- KpiRow: migrate to ui/KpiCard (proper interface, tone, lucide icons, animated counter)
- ui/KpiCard: add optional `to` prop with RouterLink support
- Remove legacy dashboard/KpiCard.vue (replaced by ui/KpiCard)
- Fix pre-existing lint errors in TopBar and color.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 11:34:18 +01:00
Vadym Samoilenko
56a7ff06d6 fix(ui): lint and type-check fixes
Fix broken ESLint config (.eslintrc.cjs missing @rushstack patch, wrong
@typescript-eslint extend syntax). Remove unused imports and variables in
11 components/views. Fix invalid v-else placement in OmgView (move to
Tooltip wrapper instead of inner span).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:25:23 +01:00
Vadym Samoilenko
b8329904e8 refactor(ui): replace title attrs with Tooltip on interactive elements
Wrap 'Mark done', 'Edit project', 'Generate AI summary' buttons and inline-editable OMG spans with Tooltip component; replace priority dot title attr in KanbanCard; also adds focus-visible ring to 'Mark done' button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:20:54 +01:00
Vadym Samoilenko
ebd5616498 refactor(login): form spacing, consistent inputs
Replace focus: with focus-visible: on Microsoft sign-in button for consistent ring pattern; error message already uses text-destructive correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:19:08 +01:00
Vadym Samoilenko
0ac7d71e9b refactor(admin): ConfirmDialog for destructive actions, spacing
Apply space-y-8 rhythm to admin page sections (no destructive actions currently present in this view — no confirm dialog needed yet).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:18:59 +01:00
Vadym Samoilenko
86607fbf06 refactor(live): Skeleton loading, EmptyState, spacing
Show 3 skeleton rows while SSE is not yet connected, replace emoji empty state with EmptyState component (Radio+Zap icons), add focus-visible ring to Clear button, slightly increase event row padding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:18:51 +01:00
Vadym Samoilenko
ec6fb8c37b refactor(tasks): EmptyState, spacing rhythm, focus-visible
Replace inline 'Drop here' placeholder in KanbanColumn with EmptyState component, remove title attr in favour of aria-label on add-task button, add focus-visible ring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:18:28 +01:00
Vadym Samoilenko
3e3826028c refactor(calendar): ConfirmDialog, Skeleton loading, Button/SegmentedControl toolbar
Replace window.confirm with ConfirmDialog for block delete, upgrade view/week-length toggles to SegmentedControl, swap inline SVG nav arrows for lucide ChevronLeft/Right with ghost icon Button, add focus-visible ring to planner toggle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:18:08 +01:00
Vadym Samoilenko
3921f01aab refactor(settings): DateRangePicker, form spacing, focus-visible
- Replace two separate <input type="date"> fields for export range with
  a single DateRangePicker component; format() from date-fns bridges
  Date <-> string for exportFrom/exportTo refs
- Use space-y-8 between card sections, space-y-2 between label + input
- Add explicit focus-visible:ring-2 on raw Button usages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:16:20 +01:00
Vadym Samoilenko
5cc38b3aae refactor(project-detail): EmptyState, Skeleton loading, spacing
- Replace "No data" / "No sessions" / "No tools" terse text with EmptyState
  components (FileText, Wrench, CalendarDays icons)
- Add Skeleton placeholders for header, chart, and grid during loading
- Replace raw <input> elements in edit mode with shared Input component
- Replace raw save/cancel buttons in edit mode with Button component
- Upgrade summarize button touch target from h-6/w-6 to h-8/w-8
- Add focus-visible rings on interactive buttons and links
- Use space-y-8 between top-level sections, space-y-2 for label+input pairs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:16:15 +01:00
Vadym Samoilenko
b118166693 refactor(keys): ConfirmDialog with type-to-confirm, EmptyState
- Replace window.confirm for key revoke with ConfirmDialog + confirmText prop
  requiring user to type the key label before confirmation
- Replace terse "No API keys" text with EmptyState component (Key, Plus, Shield icons)
- Add focus-visible ring on Revoke button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:16:10 +01:00
Vadym Samoilenko
a605ba44eb refactor(omg): touch targets, shared Input, ConfirmDialog, Tooltip
- Replace raw h-7 w-7 action buttons with Button size="icon" (40px)
- Replace raw inline <input> elements with shared Input component
- Replace handleDelete/window.confirm guard with ConfirmDialog + pendingDeleteId ref
- Wrap action buttons in Tooltip with descriptive content
- Upgrade empty state to EmptyState component
- Add focus-visible rings on interactive elements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:16:06 +01:00
Vadym Samoilenko
fd49ad9865 refactor(projects): SegmentedControl, EmptyState, typed icons
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:12:53 +01:00
Vadym Samoilenko
13c262184a refactor(reports): SegmentedControl, Button, EmptyState
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:12:50 +01:00
Vadym Samoilenko
ba99a4dd65 refactor(devops): SegmentedControl, design tokens, ConfirmDialog, spacing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:12:48 +01:00
Vadym Samoilenko
e2f9f35362 refactor(dashboard): extract ProjectBreakdown and ToolUsageList
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:09:46 +01:00
Vadym Samoilenko
0a6ce6c3c4 refactor(dashboard): extract TimelineChart component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:09:43 +01:00
Vadym Samoilenko
4e9de2d3c3 refactor(dashboard): extract KpiRow with KpiCard components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:09:41 +01:00
Vadym Samoilenko
be6d557622 refactor(dashboard): extract DateRangeFilter component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:09:38 +01:00
Vadym Samoilenko
8a31f46c88 fix(button): ensure icon size meets 40px touch target minimum 2026-05-13 11:06:55 +01:00
Vadym Samoilenko
58f2edadb3 fix(select): use focus-visible for keyboard-only ring 2026-05-13 11:06:28 +01:00
Vadym Samoilenko
823b41e28d feat(dialog): focus trap, autofocus on open, scrollable body 2026-05-13 11:06:19 +01:00
Vadym Samoilenko
2425e241c0 feat(datatable): typed sort, debounced filters, shared Button/Input, EmptyState 2026-05-13 11:06:17 +01:00
Vadym Samoilenko
34c40798be feat(ui): add SegmentedControl, KpiCard with animated counter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:03:33 +01:00
Vadym Samoilenko
d586e9474c feat(ui): add Calendar and DateRangePicker components 2026-05-13 11:01:51 +01:00
Vadym Samoilenko
b382ff640c feat(ui): add EmptyState and ConfirmDialog components 2026-05-13 11:00:14 +01:00
Vadym Samoilenko
f19e5122eb feat(ui): add Tooltip, Popover, Skeleton primitives (radix-vue) 2026-05-13 10:58:41 +01:00
Vadym Samoilenko
9eb7e077ec feat(styles): add spacing scale comment and skeleton shimmer keyframes 2026-05-13 10:56:52 +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
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
fe31ca0fd7 feat(dashboard): add Tasks Today + ADO Priority Items panels
Tasks Today shows pending tasks for today with quick-complete.
ADO Priority Items shows top 5 open items sorted by priority.
Falls back to a connect prompt when no DevOps integration is configured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:11:51 +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
3671657d11 fix: chart bars use pixel heights instead of percentages
Percentage heights relative to a flex parent are unreliable without explicit
height on the parent chain. Compute bar heights directly in pixels (max 160px)
to guarantee visible bars regardless of flex/CSS cascade.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:34:56 +01:00