Skip to content

Commit 6b4c2c4

Browse files
committed
feat: add architecture overview and ESLint configuration
- Introduced ARCHITECTURE.md detailing project structure, data flow, and key implementation decisions. - Documented tech stack, app bootstrapping, folder layout, data model, UI structure, styling, error handling, tooling, and quality gates. - Added recommendations for future improvements including achievement engine integration, state management, routing, performance, accessibility, and testing. - Created eslint.config.js with a flat configuration for React and TypeScript, optimizing for speed and including recommended rules for hooks and refresh.
1 parent 5f3868d commit 6b4c2c4

File tree

3 files changed

+3786
-4238
lines changed

3 files changed

+3786
-4238
lines changed

ARCHITECTURE.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Architecture Overview
2+
3+
This document explains how the project is structured, how data flows through the app, and the key decisions behind the implementation.
4+
5+
## Stack
6+
7+
- Runtime/UI: React 19 + Radix primitives (custom UI in `src/components/ui`)
8+
- Styling: Tailwind CSS v4, design tokens via `theme.json`
9+
- Build: Vite 6 + SWC React plugin, Spark vite plugins
10+
- DX: TypeScript 5 (no type-check on build for speed), ESLint v9 flat config
11+
- Spark: `@github/spark` for integrations and the `useKV` persistent state hook
12+
13+
## App bootstrapping
14+
15+
- `index.html` mounts the app into `#root` and includes base CSS.
16+
- `src/main.tsx`
17+
- Imports Spark web components: `import "@github/spark/spark"`
18+
- Renders `<App />` under `react-error-boundary` with a custom fallback (`src/ErrorFallback.tsx`). In dev, errors are rethrown for a better dev experience.
19+
- Loads global styles: `main.css`, `styles/theme.css`, `index.css`.
20+
21+
## Folder layout
22+
23+
```
24+
src/
25+
App.tsx # main page; tabs for Achievements/Repositories
26+
ErrorFallback.tsx # production-only error boundary UI
27+
components/ # feature and shared UI components
28+
AchievementCard.tsx
29+
AchievementDetails.tsx
30+
RepositoryList.tsx
31+
UserProfileHeader.tsx
32+
ui/ # Radix-driven UI primitives styled with Tailwind
33+
hooks/
34+
use-mobile.ts # responsive helper (matchMedia)
35+
lib/
36+
achievements.ts # domain model and static achievement catalog
37+
utils.ts # helpers (cn())
38+
styles/
39+
theme.css # CSS variables for theme tokens
40+
```
41+
42+
## Data model and flow
43+
44+
- Achievements are defined statically in `src/lib/achievements.ts`.
45+
- The app resolves a GitHub username in two ways:
46+
1) If running inside a Spark-enabled context, `window.spark.user()` is used to prefill the current GitHub user.
47+
2) A user can search any username in `UserProfileHeader`.
48+
- `App.tsx` fetches user data from the public GitHub REST API (`GET /users/:username`).
49+
- Errors are surfaced using `sonner` toasts (404 vs. generic/network).
50+
- The data is normalized to `UserData`.
51+
- Persistence: `useKV` from Spark stores small pieces of state locally
52+
- `github-user-data` — last loaded user
53+
- `unlocked-achievements` — ids of achievements the app considers unlocked (simulated)
54+
55+
### Simulated achievement logic
56+
57+
`simulateUnlockedAchievements(user)` in `App.tsx` marks some achievements as unlocked based on simple heuristics (public repos, followers, account age). `getAchievementProgress` derives a percentage for each item to show partial progress.
58+
59+
Note: This is intentionally heuristic; it does not call any private APIs. Real unlock status would require more detailed GitHub events/PR/Dicussions data.
60+
61+
## UI structure
62+
63+
- `App.tsx` renders two top-level tabs:
64+
- Achievements: grid of `AchievementCard` filtered by All/Unlocked/Locked.
65+
- Repositories: `RepositoryList` shows user repos with simple sorting; uses public GitHub API (`/users/:username/repos`).
66+
- `AchievementDetails` is a dialog showing requirements and tips, with progress if partially complete.
67+
- `UserProfileHeader` renders avatar, progress bar, and the username search.
68+
- All common primitives (Button, Card, Tabs, Dialog, etc.) live under `src/components/ui` and are styled with Tailwind + Radix.
69+
70+
## Styling & theming
71+
72+
- Tailwind v4 config in `tailwind.config.js` reads tokens from `theme.json` if present.
73+
- Custom CSS variables (colors, radii, spacing) are referenced in the Tailwind theme.
74+
- Dark mode is driven by a data attribute: `[data-appearance="dark"]`.
75+
76+
## Error handling
77+
78+
- Global: `react-error-boundary` wraps the tree; dev mode rethrows to surface stacktraces via Vite overlay. In production, an error alert with a "Try Again" button is displayed.
79+
- Fetch calls: explicit 404 handling with toasts; generic network failures also show toasts.
80+
81+
## Tooling
82+
83+
- Scripts (`package.json`):
84+
- `dev`: start Vite dev server
85+
- `build`: `tsc -b --noCheck && vite build`
86+
- `preview`: preview static build
87+
- `lint`: ESLint v9 (flat config)
88+
- `kill`: Linux-only helper (not used on Windows)
89+
- ESLint: flat config in `eslint.config.js` with `@eslint/js`, `typescript-eslint`, `react-hooks`, and `react-refresh`.
90+
91+
## External dependencies & constraints
92+
93+
- Public GitHub API calls are unauthenticated and subject to rate limits (60/hr per IP). Heavy usage may require a token-proxy or server.
94+
- Spark-specific functionality:
95+
- `window.spark.user()` is available only in Spark-aware environments. The app guards this with try/catch and works without it by using manual search.
96+
97+
## Quality gates (current status)
98+
99+
- Build: PASS (Vite build succeeds). Note: CSS optimizer reports warnings for container query-like tokens; these are benign with current setup.
100+
- Lint: PASS with warnings (hooks exhaustive-deps, fast-refresh guidance). No blocking errors.
101+
- Tests: Not configured.
102+
103+
## Opportunities / recommendations
104+
105+
1. Achievement engine
106+
- Move from simulated unlocks to real checks by calling relevant GitHub endpoints (PRs, issues, discussions, stars). Cache results via `react-query` (`@tanstack/react-query` already present) with per-user keys.
107+
- Define per-achievement `checkProgress(user)` functions directly in `achievements.ts` for co-located logic.
108+
109+
2. State & data
110+
- Adopt `react-query` for fetches (users, repos), retries, cache, and loading states. Keeps `App.tsx` slimmer and handles race conditions.
111+
- Persist last-searched username in `useKV` for better UX.
112+
113+
3. Routing
114+
- Consider adding client-side routing (e.g., `/user/:login`) for shareable links to a profile and deep-linking to an achievement via query params.
115+
116+
4. DX
117+
- Keep `eslint.config.js` (added) and optionally add Prettier or formatting rules.
118+
- The `kill` script is Linux-only; consider removing or guarding for Windows environments.
119+
120+
5. Performance
121+
- Split the Achievements grid via code-splitting or windowing if the catalog grows large.
122+
- Memoize derived lists (`filteredAchievements`) and progress calculations if they become expensive.
123+
124+
6. A11y
125+
- Radix provides a strong base; ensure all interactive controls have labels and keyboard focus states (most are covered). Consider testing with Axe.
126+
127+
7. Testing
128+
- Add Vitest + React Testing Library to validate rendering and critical flows (user search, repo list, details dialog open/close). Keep a couple of unit tests for `simulateUnlockedAchievements` and any future `checkProgress` functions.
129+
130+
8. API limits & resiliency
131+
- Show remaining rate-limit in the UI if detected; back off with friendly messaging.
132+
- Optional: add a tiny proxy/server for authenticated requests when needed.
133+
134+
## Key contracts
135+
136+
- `UserData`: minimal normalized user object used across components.
137+
- `Achievement`:
138+
- id, name, description, tier, icon, category, requirements, tips
139+
- optional `checkProgress(userData) -> { progress, current, target, unlocked }`
140+
141+
## Edge cases considered
142+
143+
- Unknown user (404)
144+
- Network failures
145+
- No public repos / no topics
146+
- Non-Spark environment (no `window.spark`) — app still usable via manual search
147+
148+
---
149+
150+
If you have questions or want to iterate on the achievement engine (real data integration), see the recommendations above; happy to help wire it up.

eslint.config.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// ESLint flat config for React + TypeScript (ESLint v9)
2+
// Lightweight, no type-aware rules to keep linting fast without TS project setup.
3+
import js from "@eslint/js";
4+
import tseslint from "typescript-eslint";
5+
import reactHooks from "eslint-plugin-react-hooks";
6+
import reactRefresh from "eslint-plugin-react-refresh";
7+
import globals from "globals";
8+
9+
export default [
10+
{
11+
ignores: [
12+
"dist/**",
13+
"node_modules/**",
14+
"**/*.d.ts",
15+
],
16+
},
17+
js.configs.recommended,
18+
...tseslint.configs.recommended,
19+
{
20+
files: ["**/*.{ts,tsx,js,jsx}"],
21+
languageOptions: {
22+
ecmaVersion: 2023,
23+
sourceType: "module",
24+
globals: {
25+
...globals.browser,
26+
...globals.node,
27+
},
28+
},
29+
plugins: {
30+
"react-hooks": reactHooks,
31+
"react-refresh": reactRefresh,
32+
},
33+
rules: {
34+
// React hooks best practices
35+
...reactHooks.configs.recommended.rules,
36+
// Warn on exports causing full reloads during Vite HMR
37+
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
38+
// Mild TS rule tweaks to reduce noise in React apps
39+
"@typescript-eslint/no-explicit-any": "off",
40+
"@typescript-eslint/no-unused-vars": [
41+
"warn",
42+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" }
43+
],
44+
},
45+
},
46+
];

0 commit comments

Comments
 (0)