Skip to content

Commit 00335ff

Browse files
committed
Phase 3 comleted
1 parent aba6fd7 commit 00335ff

26 files changed

Lines changed: 3246 additions & 0 deletions

docs/react/06-migration-plan.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Phase 3.3 — Migration Plan: Vanilla JS → React
2+
3+
## What we are doing
4+
5+
Replacing the old plain HTML + JS frontend with a React app built with Vite.
6+
The vault API (FastAPI) stays unchanged — only the frontend changes.
7+
8+
---
9+
10+
## Why this structure
11+
12+
In development — two servers run side by side:
13+
```
14+
Terminal 1: uvicorn (port 8000) ← API
15+
Terminal 2: npm run dev (port 5173) ← React frontend
16+
```
17+
18+
React calls the API directly at http://localhost:8000.
19+
CORS middleware on FastAPI allows requests from port 5173.
20+
21+
In production (Phase 7) — Nginx will serve the React build and proxy API calls.
22+
23+
---
24+
25+
## Folder structure after migration
26+
27+
```
28+
vault/
29+
├── app/ ← FastAPI (unchanged)
30+
└── frontend/ ← React app (Vite)
31+
├── src/
32+
│ ├── main.jsx ← entry point
33+
│ ├── App.jsx ← root component
34+
│ └── components/
35+
│ ├── Header.jsx
36+
│ ├── HealthCheck.jsx
37+
│ ├── NoteList.jsx
38+
│ ├── NoteForm.jsx
39+
│ └── NoteCard.jsx
40+
├── index.html
41+
├── package.json
42+
└── vite.config.js
43+
```
44+
45+
---
46+
47+
## Features to rebuild (same as vanilla JS, now in React)
48+
49+
| Feature | Vanilla JS | React |
50+
|---|---|---|
51+
| Health check | button + fetch + innerHTML | HealthCheck component with useState |
52+
| Load notes | fetch + map to HTML string | NoteList component with useEffect |
53+
| Create note | form + POST fetch | NoteForm component with controlled inputs |
54+
| Delete note | DELETE fetch + reload | delete handler in NoteList |
55+
56+
---
57+
58+
## Steps
59+
60+
1. `npm install` — install dependencies
61+
2. Clean App.jsx — remove Vite default code
62+
3. Build Header component
63+
4. Build HealthCheck component
64+
5. Build NoteList component (load notes)
65+
6. Build NoteForm component (create note)
66+
7. Add delete to NoteList
67+
68+
---
69+
70+
## Dev server commands
71+
72+
```bash
73+
# Start React dev server
74+
cd vault/frontend
75+
npm run dev → http://localhost:5173
76+
77+
# Build for production
78+
npm run build → generates vault/frontend/dist/
79+
```
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# HealthCheck Component
2+
3+
## Stage 1 — What we are building
4+
5+
A `HealthCheck` component that:
6+
- Shows a "Check Health" button
7+
- On click, calls `GET /health` on the FastAPI API
8+
- Displays the response: status, database, version
9+
10+
This is the React equivalent of the health check we built in vanilla JS.
11+
12+
### Vanilla JS equivalent (for comparison)
13+
```javascript
14+
document.getElementById("check-health-btn").addEventListener("click", async () => {
15+
const response = await fetch("/health");
16+
const data = await response.json();
17+
result.textContent = `Status: ${data.status} | DB: ${data.database}`;
18+
});
19+
```
20+
21+
### React version (what we will write)
22+
```jsx
23+
function HealthCheck() {
24+
const [status, setStatus] = useState(null);
25+
26+
async function checkHealth() {
27+
const response = await fetch(`${API_URL}/health`);
28+
const data = await response.json();
29+
setStatus(data);
30+
}
31+
32+
return (
33+
<div>
34+
<button onClick={checkHealth}>Check Health</button>
35+
{status && <p>Status: {status.status} | DB: {status.database}</p>}
36+
</div>
37+
);
38+
}
39+
```
40+
41+
### Files
42+
- `vault/frontend/src/components/HealthCheck.jsx` ← new
43+
- `vault/frontend/.env` ← new (API base URL)
44+
- `vault/frontend/src/App.jsx` ← updated (import HealthCheck)
45+
46+
---
47+
48+
## Stage 2 — KT: New concepts in this component
49+
50+
### 1. VITE_API_URL — frontend environment variable
51+
52+
We never hardcode the API URL in code because:
53+
- In development the API is at `http://localhost:8000`
54+
- In production it will be at `https://yourdomain.com`
55+
56+
Vite reads from a `.env` file in the frontend folder:
57+
```
58+
VITE_API_URL=http://localhost:8000
59+
```
60+
61+
In code, access it with:
62+
```javascript
63+
import.meta.env.VITE_API_URL
64+
```
65+
66+
Rules:
67+
- Must start with `VITE_` — Vite only exposes variables with this prefix to the browser
68+
- Never put secrets here — this gets bundled into the JS the browser downloads
69+
70+
### 2. Conditional rendering — `{status && <p>...</p>}`
71+
72+
```jsx
73+
{status && <p>Status: {status.status}</p>}
74+
```
75+
76+
If `status` is `null` (before the button is clicked), nothing renders.
77+
Once `status` has data, the `<p>` appears.
78+
79+
This is the React equivalent of:
80+
```javascript
81+
if (status) {
82+
element.textContent = status.status;
83+
}
84+
```
85+
86+
### 3. onClick on a button
87+
88+
```jsx
89+
<button onClick={checkHealth}>Check Health</button>
90+
```
91+
92+
`onClick` is React's event handler — equivalent to `addEventListener("click", ...)`.
93+
You pass the function reference directly — no need to find the element by id first.
94+
95+
### 4. DevTools to watch during this component
96+
97+
| Tab | What to look for |
98+
|---|---|
99+
| Network | `GET /health` request — status 200, response JSON |
100+
| Components | Click `HealthCheck` in tree — watch `status` state change from null to object after button click |
101+
| Console | Any fetch errors will appear here |
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# NoteList + NoteCard Components
2+
3+
## Stage 1 — What we are building
4+
5+
Two components:
6+
- `NoteCard.jsx` — displays a single note (title + body)
7+
- `NoteList.jsx` — fetches all notes for user_id=1 on load, renders a NoteCard for each
8+
9+
### Files
10+
- `vault/frontend/src/components/NoteCard.jsx` ← new
11+
- `vault/frontend/src/components/NoteList.jsx` ← new
12+
- `vault/frontend/src/App.jsx` ← updated
13+
14+
### Vanilla JS equivalent (for comparison)
15+
```javascript
16+
const response = await fetch("/notes?user_id=1");
17+
const notes = await response.json();
18+
list.innerHTML = notes.map(note => `
19+
<li><strong>${note.title}</strong><p>${note.body}</p></li>
20+
`).join("");
21+
```
22+
23+
### React version
24+
```jsx
25+
// NoteList auto-fetches on load using useEffect
26+
useEffect(() => {
27+
fetch(`${API_URL}/notes?user_id=1`)
28+
.then(res => res.json())
29+
.then(data => setNotes(data));
30+
}, []);
31+
32+
// Renders a NoteCard for each note
33+
notes.map(note => <NoteCard key={note.id} note={note} />)
34+
```
35+
36+
---
37+
38+
## Stage 2 — KT: New concepts in this component
39+
40+
### 1. Component decomposition
41+
42+
NoteList is split into two components on purpose:
43+
- `NoteList` — owns the data fetching and state
44+
- `NoteCard` — only knows how to display one note, receives it as a prop
45+
46+
This mirrors the FastAPI pattern:
47+
- Router = NoteList (orchestrates)
48+
- Schema = NoteCard (defines the shape of one item)
49+
50+
### 2. useEffect auto-fetches on load
51+
52+
Unlike HealthCheck (which fetches on button click), NoteList fetches
53+
automatically when the component first appears on screen:
54+
55+
```jsx
56+
useEffect(() => {
57+
fetch(...)
58+
}, []); // ← empty array = run once on mount
59+
```
60+
61+
No button needed. The data loads as soon as the page opens.
62+
63+
### 3. Passing the full note object as a prop
64+
65+
Instead of passing individual fields:
66+
```jsx
67+
<NoteCard title={note.title} body={note.body} /> // ← works but repetitive
68+
```
69+
70+
We pass the whole object:
71+
```jsx
72+
<NoteCard note={note} /> // ← cleaner
73+
```
74+
75+
Inside NoteCard: `{ note }` destructures the prop, then `note.title`, `note.body`.
76+
77+
### 4. DevTools to watch
78+
79+
| Tab | What to look for |
80+
|---|---|
81+
| Network | `notes?user_id=1` request fires automatically on page load — no button click needed |
82+
| Components | Click `NoteList` → watch `notes` state go from `[]` to the array of notes |
83+
| Components | Click `NoteCard` → see the `note` prop with all its fields |
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# NoteForm Component
2+
3+
## Stage 1 — What we are building
4+
5+
A form with title and body inputs. On submit, calls `POST /notes` and
6+
tells NoteList to refresh so the new note appears immediately.
7+
8+
### Files
9+
- `vault/frontend/src/components/NoteForm.jsx` ← new
10+
- `vault/frontend/src/components/NoteList.jsx` ← updated (receives onNoteAdded callback)
11+
- `vault/frontend/src/App.jsx` ← updated (wires NoteForm and NoteList together)
12+
13+
---
14+
15+
## Stage 2 — KT: New concepts in this component
16+
17+
### 1. Controlled inputs
18+
19+
Every input's value is stored in state and kept in sync:
20+
21+
```jsx
22+
const [title, setTitle] = useState('');
23+
24+
<input value={title} onChange={e => setTitle(e.target.value)} />
25+
```
26+
27+
React owns the value — not the browser. This is called a controlled input.
28+
29+
### 2. Lifting state up — callback props
30+
31+
NoteForm creates a note. NoteList shows notes. They are siblings — neither
32+
is parent of the other. How does NoteList know to refresh after NoteForm submits?
33+
34+
Answer: lift the responsibility up to App, pass a callback down:
35+
36+
```
37+
App
38+
├── NoteForm onNoteAdded={refresh} ← calls refresh after POST
39+
└── NoteList onNoteAdded={refresh} ← refresh re-fetches notes
40+
```
41+
42+
App owns the refresh function and passes it to both children as a prop.
43+
This is called "lifting state up" — the most common React pattern for
44+
sibling communication.
45+
46+
### 3. POST request from fetch
47+
48+
```javascript
49+
fetch(`${API_URL}/notes`, {
50+
method: "POST",
51+
headers: { "Content-Type": "application/json" },
52+
body: JSON.stringify({ user_id: 1, title, body }),
53+
});
54+
```
55+
56+
Same as vanilla JS and Postman — method, Content-Type header, JSON body.
57+
58+
### 4. DevTools to watch
59+
60+
| Tab | What to look for |
61+
|---|---|
62+
| Network | POST /notes on submit — check Payload tab for the JSON body sent |
63+
| Network | GET /notes?user_id=1 fires right after — that is the auto-refresh |
64+
| Components | NoteForm state clears after submit (title and body back to empty) |

vault/frontend/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Backend url
2+
VITE_API_URL=http://localhost:8000

vault/frontend/.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
.env
15+
16+
# Editor directories and files
17+
.vscode/*
18+
!.vscode/extensions.json
19+
.idea
20+
.DS_Store
21+
*.suo
22+
*.ntvs*
23+
*.njsproj
24+
*.sln
25+
*.sw?

vault/frontend/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# React + Vite
2+
3+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4+
5+
Currently, two official plugins are available:
6+
7+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
8+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
9+
10+
## React Compiler
11+
12+
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
13+
14+
## Expanding the ESLint configuration
15+
16+
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.

vault/frontend/eslint.config.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import js from '@eslint/js'
2+
import globals from 'globals'
3+
import reactHooks from 'eslint-plugin-react-hooks'
4+
import reactRefresh from 'eslint-plugin-react-refresh'
5+
import { defineConfig, globalIgnores } from 'eslint/config'
6+
7+
export default defineConfig([
8+
globalIgnores(['dist']),
9+
{
10+
files: ['**/*.{js,jsx}'],
11+
extends: [
12+
js.configs.recommended,
13+
reactHooks.configs.flat.recommended,
14+
reactRefresh.configs.vite,
15+
],
16+
languageOptions: {
17+
globals: globals.browser,
18+
parserOptions: { ecmaFeatures: { jsx: true } },
19+
},
20+
},
21+
])

0 commit comments

Comments
 (0)