Batch of 4 parallel units, rebased onto current develop and integrated: - PHASE 1 split-view: clicking an email opens an in-place, collapsible + resizable reading pane (shared EmailDetail extracted from Senders) instead of a new tab; list stays interactive, selection preserved, mobile stacks (<768px); list gains Skeleton loading + EmptyState. (FolderView, SearchResults, +EmailDetail, +split.css) - PHASE 3 row polish: EmailRow gains onOpen (Gmail fallback kept), a new accessible Checkbox primitive (ui/checkbox.jsx), 44px rows, clearer hierarchy + hover; keeps the search why-matched highlight rendering. (EmailRow, ui/checkbox, styles.css) - PHASE 4 bulk+keys: BulkToolbar hierarchy/responsive/clear-selection; keyboard nav adds ArrowUp/Down + u=unread, hardened input guard. (BulkToolbar, useListKeyboardNav) - PHASE 3/4 dashboard: remove dead HeatmapWidget, replace CategoryHeatmap with a clickable Emails-by-Category bar (a11y: rank by text+count, not colour); Skeleton + EmptyState on widgets. (widgets, Dashboard, client.js, styles.css) Each unit was self-code-reviewed and built green; integrated build passes. Supersedes PRs #40-43 (their branches had a stale 38-commit base). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
InboxIntel — Inbox Intelligence Platform
A Gmail analytics, cleanup, and automation app. Connects via Google OAuth2, syncs your inbox into PostgreSQL, and gives you a draggable analytics dashboard, safe bulk cleanup, unsubscribe management, optional AI analysis, advanced search, and PDF/CSV/JSON exports.
This repository is a full-stack scaffold structured for the 15-step implementation plan. Every layer is wired and compiles; the business logic is functional, with a few integration points (Gmail parsing edge cases, AI prompt tuning) intentionally left as clearly-marked extension points.
Architecture
Clean Architecture across four backend projects plus a React SPA:
src/
InboxIntel.Domain Entities + enums. No external dependencies.
InboxIntel.Application Service interfaces, DTOs, validators, query parser.
InboxIntel.Infrastructure EF Core, Gmail client, sync worker, AI, export.
InboxIntel.Api ASP.NET Core Web API: auth, controllers, DI, Serilog.
frontend/ React + Vite dashboard (Chart.js, react-grid-layout).
tests/ Unit + integration test projects.
Dependency rule: Api -> Infrastructure -> Application -> Domain. Controllers contain no business logic; they delegate to Application-layer service interfaces resolved through DI.
See docs/ARCHITECTURE.md for the full design, data model, and request flow.
Tech stack
.NET 8 / ASP.NET Core, EF Core + Npgsql (PostgreSQL), Hosted background worker, Serilog, FluentValidation, Polly (retry + backoff), Google.Apis.Gmail, QuestPDF/CsvHelper for exports, React 18 + Vite + Chart.js + react-grid-layout.
Running with Docker (recommended)
cp .env.example .env # then fill in Google OAuth credentials
docker compose up --build
Services: PostgreSQL (5432), API (8080), frontend (8081). Optional reverse proxy:
docker compose --profile proxy up --build # everything on port 80
The API applies EF migrations automatically on startup (Database:AutoMigrate).
Running locally (without Docker)
-
Start PostgreSQL and set the connection string in
src/InboxIntel.Api/appsettings.Development.json. -
Create the initial migration and database:
dotnet tool install --global dotnet-ef dotnet ef migrations add InitialCreate \ -p src/InboxIntel.Infrastructure -s src/InboxIntel.Api dotnet ef database update -p src/InboxIntel.Infrastructure -s src/InboxIntel.Api -
Run the API and the frontend:
dotnet run --project src/InboxIntel.Api # http://localhost:5080 cd frontend && npm install && npm run dev # http://localhost:5173
Google OAuth2 setup
Create an OAuth client (type: Web application) in the Google Cloud Console. Add the Gmail API. Authorized redirect URI: http://localhost:5080/signin-google (dev) and your production URL. Put the client id/secret in .env or user-secrets. Scopes requested: openid email profile gmail.readonly gmail.modify (no send scope).
Security notes
OAuth refresh tokens are encrypted at rest with the ASP.NET Core Data Protection API (AES) and never logged. Keys persist to a mounted /keys volume. All destructive cleanup and unsubscribe actions require an explicit Confirmed flag and a server-side preview. The AI layer is advisory only — it never performs destructive actions.
Tests
dotnet test
Unit tests cover the Gmail query parser and unsubscribe extraction; integration tests boot the API host and assert authorization is enforced.