Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

Worklog — a summary of your day. Shows hour-by-hour activity from Google (Calendar, Gmail, Docs), Slack, Trello, GitHub, Jira Cloud, and HubSpot. AI-powered time logging via Gemini + Milient/Moment integration.
Worklog — a summary of your day. Shows hour-by-hour activity from Google (Calendar, Gmail, Docs), Slack, Trello, GitHub, Jira, and HubSpot. AI-powered time logging via Gemini + Milient/Moment integration.

## Tech Stack

Expand All @@ -26,16 +26,15 @@ app/
│ │ ├── trello/ # Trello OAuth
│ │ ├── github/ # GitHub OAuth
│ │ ├── jira/ # Jira Cloud OAuth 2.0 (3LO)
│ │ └── hubspot/ # HubSpot OAuth (connect/callback/disconnect)
│ │ └── hubspot/ # HubSpot OAuth
│ ├── activities/route.ts # GET /api/activities?date=YYYY-MM-DD
│ ├── status/route.ts # GET /api/status (auth required)
│ └── ai/
│ ├── pm-context/route.ts # GET - projects, activity types, allocations, time lock
│ ├── pm-context/route.ts # GET - projects, activity types, allocations, existing records, time lock
│ ├── suggest/route.ts # POST - generate AI suggestions via Gemini
│ └── submit/route.ts # POST - submit time logs to Milient
├── components/
│ ├── Activity.tsx # Activity item with source icons
│ ├── WelcomePage.tsx # Unauthenticated landing page
│ └── ai/
│ ├── AiPanel.tsx # AI time logging sidebar
│ ├── SuggestionCard.tsx # Individual suggestion card
Expand All @@ -47,11 +46,11 @@ app/
│ ├── trello.ts # Trello board/card activity
│ ├── github.ts # GitHub commits + events
│ ├── jira.ts # Jira Cloud issue transitions + comments
│ ├── hubspot.ts # HubSpot deal activity via CRM Search API
│ ├── hubspot.ts # HubSpot deal activity + OAuth helpers
│ ├── aggregator.ts # Hour bucketing logic
│ ├── milient.ts # Milient API client + cache
│ ├── i18n.tsx # Translations (NO/EN)
│ ├── version.ts # Version from package.json
│ ├── version.ts # App version from package.json
│ ├── ai/
│ │ ├── adapter.ts # AiAdapter interface
│ │ ├── index.ts # Factory → GeminiAdapter
Expand All @@ -64,7 +63,7 @@ app/
│ │ ├── index.ts # Factory → MilientPmAdapter
│ │ └── milient.ts # Milient PM adapter
│ └── types/
│ ├── pm.ts # PmProject, PmTask, PmActivityType, PmContext
│ ├── pm.ts # PmProject, PmActivityType, PmContext
│ └── timelog.ts # TimeLogSuggestion, TimeLogSubmission
├── globals.css # Tailwind + shadcn CSS variables
├── layout.tsx # Root layout with dark mode
Expand All @@ -86,12 +85,10 @@ prompts/timelog-system.md # Editable AI system prompt
5. Returns `{ hours, summary, sources }`

### AI Time Logging
1. User clicks "Generate" → fetches `/api/ai/pm-context` (projects, activity types, allocations, time lock)
1. User clicks "Generate" → fetches `/api/ai/pm-context` (projects, activity types, allocations, existing records, time lock)
2. Then POST `/api/ai/suggest` with date, hours, pmContext
3. Activities preprocessed → prompt assembled → Gemini generates suggestions
4. Suggestions now include a client-facing `description` and an optional `internalNote` for technical context
5. User approves/edits → POST `/api/ai/submit` → creates time records in Milient
6. Already-logged Milient records are excluded from AI suggestions to prevent duplicates
3. Activities preprocessed → prompt assembled (includes "ALREADY LOGGED TODAY" section listing existing records) → Gemini generates suggestions for remaining hours only
4. User approves/edits → POST `/api/ai/submit` → creates time records in Milient

## Auth

Expand All @@ -112,9 +109,8 @@ prompts/timelog-system.md # Editable AI system prompt
### HubSpot (OAuth 2.0)
- Scope: `crm.objects.deals.read`
- Short-lived access tokens (30 min) + long-lived refresh tokens
- Cookie stores base64-encoded: `{ accessToken, refreshToken, expiresAt, portalId, ownerId, userId }`
- Token refresh via `POST https://api.hubapi.com/oauth/v3/token`
- Deals filtered by `hs_updated_by_user_id` to show only the current user's activity
- Cookie stores: `{ accessToken, refreshToken, expiresAt, portalId, ownerId, userId }` in base64
- Deals filtered by `hs_updated_by_user_id` to show only the current user's changes

## Caching

Expand All @@ -139,4 +135,4 @@ npm install
npm run dev # http://localhost:3000
```

Note: Slack and HubSpot OAuth require HTTPS. For local testing, use `next dev --experimental-https` or deploy to Vercel first.
Note: Slack OAuth requires HTTPS. For local Slack testing, use ngrok or deploy to Vercel first.
18 changes: 14 additions & 4 deletions docs/DOMAIN-LOGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@
- Slack DMs: No `#` prefix; channels get `#` prefix
- Docs: Shows edit/create/delete/rename/move actions
- Jira: Shows issue transitions (status changes) and comments; displays `issueKey: summary`
- HubSpot: Shows deal activity; displays deal name with readable stage name (UUIDs and numeric IDs hidden)
- Duration: Click to copy (shows checkmark feedback)

## AI Time Logging

### Suggestion Flow
1. User clicks "Generate" → fetches `/api/ai/pm-context` (projects, activity types, allocations, time lock)
1. User clicks "Generate" → fetches `/api/ai/pm-context` (projects, activity types, allocations, existing records, time lock)
2. Then POST `/api/ai/suggest` with date, hours, pmContext
3. Activities preprocessed → prompt assembled → Gemini generates suggestions
4. User approves/edits individual suggestions → clicks "Submit" → POST `/api/ai/submit` → creates time records in Milient

### Existing Records Deduplication
- The pm-context endpoint fetches existing time records for the requested date
- These are passed to the AI prompt as "ALREADY LOGGED TODAY" so the AI skips already-logged work
- The remaining hours budget is adjusted: `7.5h - already_logged_hours`
- This prevents duplicate suggestions when re-generating after partial submission

### Suggestion Caching
- Cached per date in localStorage: `ai-suggestions:YYYY-MM-DD`
- Cache entries are versioned (`CACHE_VERSION` in AiPanel.tsx) and include date field for validation
Expand All @@ -44,15 +51,18 @@
- `projectExtensions` — activity types, tied to `projectId`
- `projectMemberships` — user's project allocations (NOT `/allocations`, which doesn't exist)
- `timeRecords` — create/read time entries
- `tasks` — tasks per activity type (derived from recent time records, not fetched directly)

### Field Mapping (Suggestion → TimeRecord)
- `projectId` → from matched project
- `projectExtensionId` → from matched activity type
- `userAccountId` → resolved from Google session email
- `date` → YYYY-MM-DD
- `hours` → decimal hours
- `description` → invoice-ready text (Norwegian + English)
- `internalNote` → internal context
- `hours` → decimal hours (converted to minutes for API)
- `description` → client-facing text for invoices
- `internalNote` → technical detail for internal use
- `projectMembershipId` → for multi-role projects
- `taskId` → optional Milient task

## Drive Activity Notes

Expand Down