Skip to content

Commit ee39ac6

Browse files
authored
feat: launch readiness — OG tags, 404 route, screenshot, README, CONTRIBUTING (#48)
* feat: adds Open Graph and Twitter Card meta tags * chore: adds Playwright screenshot capture script * docs: rewrites README with current features and screenshot * feat: adds catch-all route for unknown paths * docs: adds CONTRIBUTING.md * fix: addresses review findings in screenshot spec and README * fix: corrects CONTRIBUTING setup ref and adds PrivacyPage * fix: corrects E2E spec count and dev setup reference * refactor: moves screenshot to docs/ to avoid deploying it * fix: addresses PR review findings in E2E test infrastructure - extracts duplicated setupAuth to shared e2e/helpers.ts - removes ghu_ prefix from fake tokens to avoid secret scanning - fixes waitFor({state:'detached'}) with scoped expand assertion - adds data-presence wait before screenshot capture - adds authenticated 404 redirect E2E test - updates README E2E count to 13 * fix: address PR review findings across E2E tests and docs
1 parent 32ece43 commit ee39ac6

File tree

13 files changed

+812
-160
lines changed

13 files changed

+812
-160
lines changed

CONTRIBUTING.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Contributing
2+
3+
## Development setup
4+
5+
**Prerequisites:** Node.js 24+, pnpm 10+
6+
7+
```bash
8+
git clone https://github.com/gordon-code/github-tracker.git
9+
cd github-tracker
10+
pnpm install
11+
pnpm run dev
12+
```
13+
14+
The dev server starts at `http://localhost:5173`. You'll need a GitHub OAuth app client ID in `.env` (copy `.env.example` and fill in your value).
15+
16+
## Running checks
17+
18+
```bash
19+
pnpm test # unit tests (Vitest)
20+
pnpm test:e2e # Playwright E2E tests (chromium)
21+
pnpm run typecheck # TypeScript validation
22+
pnpm run screenshot # Capture dashboard screenshot (saves to docs/)
23+
```
24+
25+
CI runs typecheck, unit tests, and E2E tests on every PR. Make sure they pass locally before pushing.
26+
27+
To run a specific test file:
28+
29+
```bash
30+
pnpm test -- tests/path/to/test.ts
31+
```
32+
33+
## Code style
34+
35+
**TypeScript:** strict mode throughout. Don't use `any` — if you're reaching for it, there's usually a better type.
36+
37+
**SolidJS patterns:**
38+
- Use `createMemo` for derived state; don't recompute inside JSX
39+
- Use `<Show>` and `<Switch>`/`<Match>` instead of ternaries or early returns
40+
- Early returns in components break SolidJS reactivity — use `<Show>` as a wrapper instead
41+
42+
**UI components:**
43+
- Tailwind v4 + daisyUI v5 for styling — use semantic classes (`btn`, `card`, `badge`) over raw utilities where possible
44+
- @kobalte/core for interactive primitives that need accessibility (Select, Tabs, Dialog)
45+
- Don't reach for a custom implementation when Kobalte has a well-tested one
46+
47+
**Validation:** Zod v4 for all runtime validation. Note that nested `.default({})` doesn't apply inner field defaults — be explicit.
48+
49+
## Testing
50+
51+
Tests live in `tests/` and mirror the `src/` directory structure. Test files end in `.test.ts` or `.test.tsx`.
52+
53+
Factory helpers in `tests/helpers/index.tsx` (`makeIssue`, `makePullRequest`, `makeWorkflowRun`) give you typed test fixtures — use them instead of hand-rolling objects.
54+
55+
A few things to know:
56+
- `createResource` error state is unreliable in happy-dom; use manual signals with `onMount` + async functions instead
57+
- Kobalte Select uses `aria-labelledby`, which overrides `aria-label` — query by regex in tests
58+
- If you're testing auth state, call `vi.resetModules()` and use dynamic imports — `auth.ts` reads localStorage at module scope
59+
60+
## Branch and commit conventions
61+
62+
Branch from `main`. Use one of these prefixes:
63+
64+
- `feat/` — new functionality
65+
- `fix/` — bug fixes
66+
- `docs/` — documentation only
67+
- `refactor/` — code changes with no behavior change
68+
- `test/` — test additions or fixes
69+
- `chore/` — build, deps, tooling
70+
71+
Commits follow [Conventional Commits](https://www.conventionalcommits.org/):
72+
73+
```
74+
type(scope): description
75+
```
76+
77+
Scope is optional. Use imperative mood: "add feature", not "adds feature" or "added feature".
78+
79+
## Pull requests
80+
81+
All PRs target `main` on `gordon-code/github-tracker`. Keep PRs focused — one feature or fix per PR makes review faster and reverts cleaner.
82+
83+
In the PR body, describe what changed and why. CI runs typecheck, unit tests, and E2E tests automatically. PRs need a passing CI run before merge.

README.md

Lines changed: 109 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,88 +4,143 @@
44

55
# GitHub Tracker
66

7-
Dashboard SPA tracking GitHub issues, PRs, and GHA workflow runs across multiple repos/orgs. Built with SolidJS on Cloudflare Workers.
7+
A dashboard for tracking GitHub issues, PRs, and Actions workflow runs across many repos and orgs. Built with SolidJS, deployed on Cloudflare Workers.
8+
9+
**Live demo:** https://gh.gordoncode.dev
10+
11+
![Dashboard](docs/dashboard-screenshot.png)
812

913
## Features
1014

11-
- **Issues Tab** — Open issues where you're the creator, assignee, or mentioned. Sortable, filterable, paginated. Dependency Dashboard issues hidden by default (toggleable).
12-
- **Pull Requests Tab** — Open PRs with CI check status indicators (green/yellow/red dots). Draft badges, reviewer names.
13-
- **Actions Tab** — GHA workflow runs grouped by repo and workflow. Accordion collapse, PR run toggle.
14-
- **Onboarding Wizard** — Single-step repo selection with search filtering and bulk select.
15-
- **PAT Authentication** — Optional Personal Access Token login as alternative to OAuth. Client-side format validation, detailed token creation instructions for classic and fine-grained PATs.
16-
- **Settings Page** — Refresh interval, notification preferences, theme (light/dark/system), density, GitHub Actions limits. Shows current auth method and hides OAuth-specific options for PAT users.
17-
- **Desktop Notifications** — New item alerts with per-type toggles and batching.
18-
- **Ignore System** — Hide specific items with an "N ignored" badge and unignore popover.
19-
- **Dark Mode** — System-aware with flash prevention via inline script + CSP SHA-256 hash.
20-
- **ETag Caching** — Conditional requests (304s are free against GitHub's rate limit).
21-
- **Auto-refresh** — Background polling keeps data fresh even in hidden tabs (requires notifications scope for efficient 304 change detection); hot poll pauses to save API budget.
15+
### Issues
2216

23-
## Tech Stack
17+
Open issues where you're the creator, assignee, or mentioned. A scope filter lets you toggle between "Involves me" and all activity in the repo. Role badges (author, assignee, mentioned) appear on each item. Dependency Dashboard issues — typically noisy bot aggregators — are hidden by default with a toggle to show them. Filterable, sortable, and paginated.
2418

25-
- **Frontend:** SolidJS + Tailwind CSS v4 + TypeScript (strict)
26-
- **Build:** Vite 8 + @cloudflare/vite-plugin
27-
- **Hosting:** Cloudflare Workers (static assets + OAuth endpoint)
28-
- **API:** @octokit/core with throttling, retry, pagination plugins
29-
- **State:** localStorage (config/view) + IndexedDB (API cache with ETags)
30-
- **Testing:** Vitest 4 (happy-dom for browser, @cloudflare/vitest-pool-workers for Worker)
31-
- **Package Manager:** pnpm
19+
### Pull Requests
3220

33-
## Development
21+
Open PRs with CI status dots (green/yellow/red), review decision badges, size badges (XS–XXL by lines changed), and draft indicators. A "blocked" filter catches PRs where checks are failing or a review requested changes. The scope filter works here too. Reviewer avatars stack for multiple reviewers.
3422

35-
```sh
36-
pnpm install
37-
pnpm run dev # Start Vite dev server
38-
pnpm test # Run unit/component tests
39-
pnpm run typecheck # TypeScript check
40-
pnpm run build # Production build (~241KB JS, ~31KB CSS)
41-
```
23+
### Actions
24+
25+
Workflow runs grouped by repo and workflow name, with duration, triggering actor, and conclusion badges. Accordion collapse per group. A toggle hides runs triggered by PRs so you can focus on branch/schedule runs.
26+
27+
### Personal Summary Strip
28+
29+
A row of clickable stat chips at the top of the dashboard: assigned issues, PRs awaiting your review, PRs ready to merge, blocked PRs, and running Actions. Clicking any chip applies the matching filter on the relevant tab.
30+
31+
### Multi-User Tracking
32+
33+
Track other GitHub users' activity alongside your own. Add up to 10 users; each gets an independent global search across all selected repos. Bot accounts (GitHub App bots) are supported and labelled with a bot badge. Items surface the tracked user's avatar so you can see at a glance who triggered what.
34+
35+
### Monitor-All Mode
36+
37+
Per-repo opt-in to see all open issues and PRs in that repo, not just ones involving tracked users. Useful for repos where you want full visibility without filtering by involvement. Monitored repos show a "Monitoring all" badge on their group header.
38+
39+
### Upstream Repo Discovery
40+
41+
When you sign in, the app searches for repos you've interacted with that aren't in your selected list and offers to add them as "upstream" repos. These are included in issue/PR fetches but excluded from workflow run polling.
42+
43+
### Hot Polling
44+
45+
A second, faster poll loop (default 30s, configurable 10–120s) targets only in-flight items — PRs with pending CI checks and actively running workflow runs. These are updated via minimal GraphQL `nodes()` queries and individual REST calls rather than full re-fetches, keeping API usage low during active development.
46+
47+
### Desktop Notifications
48+
49+
Browser notifications for new issues, PRs, and failed runs. Per-type toggles in settings. Notification permission requested on first enable. Uses the GitHub Notifications API as a change-detection gate when the `notifications` scope is available.
50+
51+
### Repo Pinning and Reordering
52+
53+
Lock repos to the top of each tab's list so they don't shift around as activity changes. Drag-to-reorder within the locked set. Lock controls appear on hover on desktop, always visible on mobile.
54+
55+
### State Visibility
56+
57+
Shimmer animations on items being updated by the hot poll, flash highlights when values change (check status, review decision), and an inline peek on collapsed repo headers that shows what changed for 3 seconds. All animations respect `prefers-reduced-motion`.
58+
59+
### Star Counts
60+
61+
Star counts appear in repo group headers, fetched as part of the standard data refresh.
62+
63+
### Themes
64+
65+
9 themes: auto (follows system), corporate, cupcake, light, nord, dim, dracula, dark, forest. Theme is applied immediately on selection with no page reload.
66+
67+
### Ignore System
68+
69+
Hide specific items with a persistent ignore list. An "N ignored" badge on the repo group header lets you see what's hidden and unignore items without leaving the tab.
70+
71+
### ETag Caching and Auto-Refresh
72+
73+
Conditional requests using `If-None-Match` headers — GitHub doesn't count 304 responses against the rate limit. Background polling keeps data fresh even when the tab is hidden (when the notifications scope is available for efficient change detection).
74+
75+
## Tech Stack
76+
77+
- SolidJS + @solidjs/router
78+
- Tailwind CSS v4 + daisyUI v5
79+
- @kobalte/core (accessible headless UI primitives)
80+
- TypeScript (strict)
81+
- Vite 8 + @cloudflare/vite-plugin
82+
- GitHub GraphQL + REST APIs via @octokit/core
83+
- Cloudflare Workers (static assets + OAuth token exchange)
84+
- Vitest 4 (happy-dom) + Playwright (E2E)
85+
- pnpm
4286

4387
## Project Structure
4488

4589
```
4690
src/
4791
app/
4892
components/
49-
dashboard/ # DashboardPage, IssuesTab, PullRequestsTab, ActionsTab, ItemRow, WorkflowRunRow, IgnoreBadge
93+
dashboard/ # DashboardPage, IssuesTab, PullRequestsTab, ActionsTab,
94+
# ItemRow, WorkflowRunRow, WorkflowSummaryCard, IgnoreBadge,
95+
# PersonalSummaryStrip
5096
layout/ # Header, TabBar, FilterBar
5197
onboarding/ # OnboardingWizard, OrgSelector, RepoSelector
52-
settings/ # SettingsPage (7 config sections + data management)
53-
shared/ # FilterInput, LoadingSpinner, StatusDot
54-
pages/ # LoginPage, OAuthCallback
98+
settings/ # SettingsPage, TrackedUsersSection, ThemePicker, Section, SettingRow
99+
shared/ # 18 shared components: FilterInput, FilterChips, StatusDot,
100+
# ReviewBadge, SizeBadge, RoleBadge, SortDropdown, PaginationControls,
101+
# LoadingSpinner, SkeletonRows, ToastContainer, NotificationDrawer,
102+
# RepoLockControls, UserAvatarBadge, ExpandCollapseButtons,
103+
# RepoGitHubLink, ChevronIcon, ExternalLinkIcon
104+
lib/ # 14 modules: format, errors, notifications, oauth, pat, url,
105+
# flashDetection, grouping, reorderHighlight, collections,
106+
# emoji, label-colors, sentry, github-emoji-map.json
107+
pages/ # LoginPage, OAuthCallback, PrivacyPage
55108
services/
56-
api.ts # GitHub API methods (fetchOrgs, fetchRepos, fetchIssues, fetchPRs, fetchWorkflowRuns)
109+
api.ts # GitHub API methods — issues, PRs, workflow runs, user validation,
110+
# upstream repo discovery, tracked user search
57111
github.ts # Octokit client factory with ETag caching and rate limit tracking
58-
poll.ts # Poll coordinator with background refresh + hot poll for in-flight items
112+
poll.ts # Poll coordinator: 5-min full refresh + hot poll loop
59113
stores/
60-
auth.ts # OAuth token management (localStorage persistence, validateToken)
114+
auth.ts # OAuth/PAT token management, localStorage persistence
61115
cache.ts # IndexedDB cache with TTL eviction and ETag support
62116
config.ts # Zod v4-validated config with localStorage persistence
63-
view.ts # View state (tabs, sorting, ignored items, filters)
64-
lib/
65-
pat.ts # PAT format validation and token creation instruction constants
66-
notifications.ts # Desktop notification permission, detection, and dispatch
117+
view.ts # View state (tabs, sorting, filters, ignored items, locked repos)
67118
worker/
68119
index.ts # OAuth token exchange endpoint, CORS, security headers
69-
tests/
70-
fixtures/ # GitHub API response fixtures (orgs, repos, issues, PRs, runs)
71-
services/ # API service, Octokit client, and poll coordinator tests
72-
stores/ # Config and cache store tests
73-
components/ # ItemRow and IssuesTab component tests
74-
lib/ # Notification tests
75-
worker/ # Worker OAuth endpoint tests
120+
tests/ # 1522 unit/component tests across 69 test files
121+
e2e/ # 14 E2E tests across 2 spec files
122+
```
123+
124+
## Development
125+
126+
```sh
127+
pnpm install
128+
pnpm run dev # Start Vite dev server
129+
pnpm test # Run unit/component tests
130+
pnpm test:e2e # Run Playwright E2E tests
131+
pnpm run typecheck # TypeScript check
132+
pnpm run build # Production build
133+
pnpm run screenshot # Capture dashboard screenshot
76134
```
77135

78136
## Security
79137

80-
- Strict CSP: `script-src 'self'` (SHA-256 exception for dark mode script only)
81-
- PAT tokens stored in `localStorage` (same key as OAuth tokens) — single-user personal dashboard threat model
82-
- OAuth CSRF protection via `crypto.getRandomValues` state parameter
83-
- CORS locked to exact origin (strict equality, no substring matching)
84-
- Access token stored in `localStorage` under app-specific key; CSP prevents XSS token theft
85-
- Token validation on page load via `GET /user`; 401 clears auth immediately (no silent refresh)
86-
- All GitHub API strings auto-escaped by SolidJS JSX (no innerHTML)
87-
- `repo` scope granted (required for private repos) — app never performs write operations
138+
OAuth tokens are stored in `localStorage` under an app-specific key — this is standard for single-user personal dashboards and matches the threat model here. CSP headers block script injection via Cloudflare (`script-src 'self'` with a SHA-256 exception for the dark-mode initialization script only). An Octokit hook blocks all non-GET requests except `POST /graphql` as a read-only guard — the `repo` scope is required for private repo access, but the app never performs writes. OAuth state is generated with `crypto.getRandomValues` and verified on callback. Token validation runs on every page load via `GET /user`; a 401 clears the stored token immediately.
88139

89140
## Deployment
90141

91142
See [DEPLOY.md](./DEPLOY.md) for Cloudflare, OAuth App, and CI/CD setup.
143+
144+
## Contributing
145+
146+
See [CONTRIBUTING.md](./CONTRIBUTING.md).

docs/dashboard-screenshot.png

91.5 KB
Loading

0 commit comments

Comments
 (0)