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.
- Framework: Next.js 14 (App Router)
- Auth: NextAuth.js v5 (JWT mode, no database)
- UI: shadcn/ui + Tailwind CSS v4
- Icons: Lucide React
- AI: Google Gemini 2.5 Flash
- PM: Milient/Moment (time management)
- APIs: Google (Gmail, Calendar, Drive Activity v2, Drive v3), Slack, Trello, GitHub, Jira Cloud, HubSpot
- Hosting: Vercel
app/
├── api/
│ ├── auth/
│ │ ├── [...nextauth]/route.ts # NextAuth handler
│ │ ├── slack/ # Slack OAuth (connect/callback/disconnect)
│ │ ├── trello/ # Trello OAuth
│ │ ├── github/ # GitHub OAuth
│ │ ├── jira/ # Jira Cloud OAuth 2.0 (3LO)
│ │ └── 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, 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
│ └── ai/
│ ├── AiPanel.tsx # AI time logging sidebar
│ ├── SuggestionCard.tsx # Individual suggestion card
│ └── SuggestionProgress.tsx # Hours progress bar
├── lib/
│ ├── auth.ts # NextAuth config + Google OAuth
│ ├── google.ts # Calendar, Gmail, Drive + Drive Activity APIs
│ ├── slack.ts # Slack search API + user resolution
│ ├── trello.ts # Trello board/card activity
│ ├── github.ts # GitHub commits + events
│ ├── jira.ts # Jira Cloud issue transitions + comments
│ ├── 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 # App version from package.json
│ ├── ai/
│ │ ├── adapter.ts # AiAdapter interface
│ │ ├── index.ts # Factory → GeminiAdapter
│ │ ├── gemini.ts # Gemini 2.5 Flash adapter
│ │ ├── preprocess.ts # Activity preprocessing for AI
│ │ ├── prompt.ts # System prompt assembly
│ │ └── parse.ts # Parse AI response → suggestions
│ ├── pm/
│ │ ├── adapter.ts # PmAdapter interface
│ │ ├── index.ts # Factory → MilientPmAdapter
│ │ └── milient.ts # Milient PM adapter
│ └── types/
│ ├── pm.ts # PmProject, PmActivityType, PmContext
│ └── timelog.ts # TimeLogSuggestion, TimeLogSubmission
├── globals.css # Tailwind + shadcn CSS variables
├── layout.tsx # Root layout with dark mode
├── icon.svg # Favicon (Frontkom logo)
└── page.tsx # Main page (client component)
components/ui/ # shadcn components (Button, Card, Badge, etc.)
lib/utils.ts # cn() class merge utility
prompts/timelog-system.md # Editable AI system prompt
page.tsxfetches/api/activities?date=YYYY-MM-DD- API route gets Google token from session, Slack/Trello/GitHub/Jira/HubSpot tokens from cookies
- Calls all source APIs in parallel
aggregator.tsbuckets activities by hour (6-23)- Returns
{ hours, summary, sources }
- User clicks "Generate" → fetches
/api/ai/pm-context(projects, activity types, allocations, existing records, time lock) - Then POST
/api/ai/suggestwith date, hours, pmContext - Activities preprocessed → prompt assembled (includes "ALREADY LOGGED TODAY" section listing existing records) → Gemini generates suggestions for remaining hours only
- User approves/edits → POST
/api/ai/submit→ creates time records in Milient
- Scopes:
gmail.readonly,calendar.readonly,drive.activity.readonly,drive.metadata.readonly - Tokens stored in encrypted JWT cookie, auto-refreshed on expiry
- Each stores user token in HTTP-only cookie
- Auth required on all API routes and disconnect endpoints
- Scopes:
read:jira-work,read:jira-user,offline_access - Access tokens expire after 1 hour; refresh token stored in base64-encoded HTTP-only cookie
- Fresh access token obtained via refresh on each API call (access token JWT too large for cookies)
- Cookie stores:
{ refreshToken, cloudId, siteUrl }only
- Scope:
crm.objects.deals.read - Short-lived access tokens (30 min) + long-lived refresh tokens
- Cookie stores:
{ accessToken, refreshToken, expiresAt, portalId, ownerId, userId }in base64 - Deals filtered by
hs_updated_by_user_idto show only the current user's changes
- In-memory TTL cache (10 min) with LRU eviction (max 200 entries)
- Request deduplication for concurrent fetches of same key
- Global cache for projects/extensions, per-user cache for memberships
- localStorage per date (
ai-suggestions:YYYY-MM-DD) - Versioned cache entries to prevent stale data
- Mutations (approve/edit/reject) persist immediately
See .env.example for the full list.
npm install
npm run dev # http://localhost:3000Note: Slack OAuth requires HTTPS. For local Slack testing, use ngrok or deploy to Vercel first.