┌─────────────────────────────────────────────────────────────────┐
│ User Interface │
│ (StatusPageNew.jsx) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Stats │ │ Search │ │ Filters │ │ Sync │ │
│ │ Cards │ │ Bar │ │ Tabs │ │ Button │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Contribution Cards (with PR status) │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────────┐
│ React Hook Layer │
│ (useGitHubSync.js) │
│ │
│ • Auto-sync on mount (if > 5 min) │
│ • Manual sync trigger │
│ • State management (loading, error, data) │
│ • Statistics calculation │
└─────────────────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────────┐
│ Service Layer │
│ (githubSync.js) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Fetch │ │ Parse │ │ Transform │ │
│ │ GitHub │→ │ GitHub │→ │ Data │ │
│ │ Data │ │ URLs │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Detect │ │ Link │ │ Upsert │ │
│ │ Status │→ │ PRs to │→ │ to │ │
│ │ │ │ Issues │ │ Database │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────┴─────────┐
↓ ↓
┌──────────────────────────┐ ┌──────────────────────────┐
│ GitHub REST API │ │ Supabase Database │
│ │ │ │
│ • GET /user │ │ ┌──────────────────┐ │
│ • GET /search/issues │ │ │ contributions │ │
│ • GET /repos/.../pulls │ │ │ table │ │
│ • GET /repos/.../issues │ │ └──────────────────┘ │
│ │ │ │
│ Rate Limit: 5000/hour │ │ RLS Policies Enabled │
└──────────────────────────┘ └──────────────────────────┘
┌──────────┐
│ User │
│ Logs In │
│ (GitHub)│
└────┬─────┘
│
↓
┌────────────────┐
│ Supabase Auth │
│ Stores Token │
└────┬───────────┘
│
↓
┌────────────────────┐
│ Navigate to │
│ Status Page │
└────┬───────────────┘
│
↓
┌────────────────────┐
│ useGitHubSync │
│ Hook Initializes │
└────┬───────────────┘
│
↓
┌────────────────────┐ No
│ Last Sync │─────────→ Display Cached Data
│ > 5 minutes? │
└────┬───────────────┘
│ Yes
↓
┌────────────────────┐
│ Trigger Sync │
└────┬───────────────┘
│
↓
┌────────────────────────────────────────┐
│ Fetch from GitHub API (Parallel) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Assigned │ │ User's │ │
│ │ Issues │ │ PRs │ │
│ └──────────────┘ └──────────────┘ │
└────┬───────────────────────────────────┘
│
↓
┌────────────────────┐
│ Parse & Transform │
│ • Extract repo │
│ • Link PRs │
│ • Detect status │
└────┬───────────────┘
│
↓
┌────────────────────┐
│ Upsert to DB │
│ (contributions) │
└────┬───────────────┘
│
↓
┌────────────────────┐
│ Update UI │
│ • Show cards │
│ • Update stats │
│ • Show success │
└────────────────────┘
StatusPage
├── Header
│ ├── SearchBar
│ └── UserProfile
│
├── SyncStatusBar
│ ├── LastSyncTime
│ └── SyncButton
│
├── ProgressCard
│ ├── SuccessRate
│ └── ProgressBar
│
├── StatsRow
│ ├── SavedCard
│ ├── AppliedCard
│ ├── InProgressCard
│ ├── MergedCard
│ └── ClosedCard
│
├── FilterTabs
│ └── StatusFilter[]
│
└── ContributionsList
└── ContributionCard[]
├── RepoInfo
├── IssueTitle
├── StatusBadge
├── ActivityInfo
└── ActionButtons
┌─────────────────┐
│ auth.users │
│ │
│ • id (PK) │
│ • email │
│ • metadata │
└────────┬────────┘
│
│ 1:N
│
↓
┌─────────────────────────────┐
│ contributions │
│ │
│ • id (PK) │
│ • user_id (FK) ────────────┘
│ • github_issue_number │
│ • github_repo_owner │
│ • github_repo_name │
│ • issue_url │
│ • pr_url │
│ • pr_status │
│ • is_assigned │
│ • last_synced_at │
│ │
│ UNIQUE(user_id, owner, │
│ repo, issue_number) │
└─────────────────────────────┘
│
│ N:1 (optional)
│
↓
┌─────────────────┐
│ bookmarks │
│ │
│ • id (PK) │
│ • user_id │
│ • issue_url │
│ • status │
└─────────────────┘
┌─────────────────┐
│ Contribution │
│ Data │
└────────┬────────┘
│
↓
┌────────────┐
│ Has PR? │
└─┬────────┬─┘
│ No │ Yes
│ │
↓ ↓
┌────────┐ ┌──────────────┐
│Assigned│ │ PR Status? │
│ or │ └─┬──────────┬─┘
│Comment?│ │ │
└─┬────┬─┘ │ │
│Yes │No │ │
│ │ ↓ ↓
↓ ↓ ┌────┐ ┌────┐
┌────┐ ┌──┐│Open│ │Mer-│
│App-│ │Sa││Dra-│ │ged │
│lied│ │ve││ft │ └─┬──┘
└────┘ └──┘└─┬──┘ │
│ │
↓ ↓
┌─────────┐ ┌────────┐
│ In │ │Merged │
│Progress │ │ ✅ │
│ 🔨 │ └────────┘
└─────────┘
│
↓
┌─────────┐
│ Closed │
│ (no │
│ merge) │
│ ❌ │
└─────────┘
User Triggers Sync
│
↓
┌──────────────────┐
│ 1. Get User Info │
│ GET /user │
└────────┬─────────┘
│
↓
┌──────────────────────────────┐
│ 2. Fetch Assigned Issues │
│ GET /search/issues? │
│ q=assignee:{user} │
└────────┬─────────────────────┘
│
↓
┌──────────────────────────────┐
│ 3. Fetch User PRs │
│ GET /search/issues? │
│ q=author:{user}+is:pr │
└────────┬─────────────────────┘
│
↓
┌──────────────────────────────┐
│ 4. For Each PR: │
│ GET /repos/{owner}/{repo}/ │
│ pulls/{number} │
└────────┬─────────────────────┘
│
↓
┌──────────────────────────────┐
│ 5. Link PRs to Issues │
│ (Match by repo + number) │
└────────┬─────────────────────┘
│
↓
┌──────────────────────────────┐
│ 6. Upsert to Database │
│ INSERT ... ON CONFLICT │
│ UPDATE │
└──────────────────────────────┘
┌─────────────────────────────────────────┐
│ Frontend (React) │
│ │
│ • No direct GitHub token access │
│ • Uses Supabase client │
│ • RLS enforced automatically │
└────────────┬────────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Supabase Auth Layer │
│ │
│ • Manages GitHub OAuth │
│ • Stores provider_token securely │
│ • Issues JWT for database access │
└────────────┬────────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Supabase Database (Postgres) │
│ │
│ RLS Policies: │
│ • SELECT: auth.uid() = user_id │
│ • INSERT: auth.uid() = user_id │
│ • UPDATE: auth.uid() = user_id │
│ • DELETE: auth.uid() = user_id │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Optimization Layers │
└─────────────────────────────────────────┘
1. Frontend Caching
├── React State (in-memory)
├── Auto-sync throttling (5 min)
└── Debounced search
2. API Optimization
├── Parallel requests (Promise.all)
├── Batch operations
└── Rate limit handling
3. Database Optimization
├── Indexes on user_id, pr_status
├── Upsert instead of insert/update
└── Efficient queries with filters
4. Network Optimization
├── Minimal payload
├── Compression
└── CDN for static assets
┌─────────────────┐
│ API Call │
└────────┬────────┘
│
↓
┌────────────┐
│ Success? │
└─┬────────┬─┘
│ Yes │ No
│ │
↓ ↓
┌────────┐ ┌──────────────┐
│Process │ │ Error Type? │
│ Data │ └─┬──────────┬─┘
└────────┘ │ │
↓ ↓
┌────────┐ ┌────────┐
│Network │ │ API │
│ Error │ │ Error │
└───┬────┘ └───┬────┘
│ │
↓ ↓
┌────────────────────┐
│ Show Error │
│ Message to User │
└────────────────────┘
│
↓
┌────────────────────┐
│ Log to Console │
│ (for debugging) │
└────────────────────┘
Why: Keep bookmarks (manual) separate from contributions (automatic)
- Better separation of concerns
- Can have contributions without bookmarks
- Easier to query and analyze
Why: Handle updates efficiently
- Prevents duplicates
- Updates existing records
- Single operation instead of check + insert/update
Why: Balance freshness with performance
- Reduces unnecessary API calls
- Respects rate limits
- Still feels real-time (5 min is acceptable)
Why: Improve sync speed
- Fetch issues and PRs simultaneously
- Reduces total sync time
- Better user experience
Why: Flexibility and performance
- No server-side logic needed
- Easy to update rules
- Fast computation
Last Updated: January 28, 2026 Version: 1.0.0