ready to test

Testing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-22 10:07:20 +08:00
parent 56d984a26a
commit fbef7cb6e6
283 changed files with 25961 additions and 1072 deletions
+170 -9
View File
@@ -1,16 +1,177 @@
# React + Vite
# new_starr_app — Frontend
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
React + Vite CSR frontend for the STARR LMS platform.
Currently, two official plugins are available:
---
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## Page Metadata
## React Compiler
Metadata (`<title>`, Open Graph, Twitter Card) is managed with **`react-helmet-async`**.
The system has two layers:
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).
1. **Global fallback** — a `<Helmet>` in `App.jsx` always renders default tags. Any page that does not supply its own metadata falls back to this.
2. **Per-page override** — each page renders a `<PageMeta />` component anywhere in its JSX tree. react-helmet-async lets the deepest/last `<Helmet>` win, so the page always overrides the fallback.
## Expanding the ESLint configuration
This approach fixes the `useNavigate()` stale-title bug: when you navigate away from a page, its `<PageMeta>` unmounts and the global fallback immediately takes over.
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.
---
### The component
```jsx
// src/contexts/MetadataContext.jsx
import { PageMeta } from '@/contexts/MetadataContext'
```
**Props**
| Prop | Type | Default |
|---|---|---|
| `title` | `string \| undefined` | `'STARR \| Philproperties'` |
| `description` | `string \| undefined` | app default description |
| `keywords` | `string \| undefined` | app default keywords |
| `ogImage` | `string \| undefined` | app default OG image URL |
| `ogType` | `string` | `'website'` |
Every prop is optional. Any omitted prop falls back to the app-level default.
Passing `title={undefined}` explicitly is the same as omitting it — the fallback title renders.
---
### Static pages
Pages with no data dependency just pass a hardcoded string.
```jsx
import { PageMeta } from '@/contexts/MetadataContext'
export default function CourseList() {
return (
<section className="bg-muted/60 h-full">
<PageMeta
title="Courses - STARR"
description="Browse and manage your training courses."
/>
{/* rest of page */}
</section>
)
}
```
---
### Dynamic pages — data from context
When the page title comes from an API response, pass the derived string.
Use `undefined` (not a hardcoded fallback string) while the data is loading — this lets the global default render during the loading state and then automatically updates when data arrives.
```jsx
import { PageMeta } from '@/contexts/MetadataContext'
import { useCourses } from '@/contexts/AdminCoursesContext'
export default function ViewCourse() {
const { course } = useCourses()
return (
<section className="bg-muted/60 min-h-full">
{/* undefined while course is null → global default renders */}
<PageMeta
title={course ? `${course.title} - STARR` : undefined}
description={course?.description}
/>
{/* rest of page */}
</section>
)
}
```
---
### Dynamic pages — data in local state
Same pattern when the title comes from local `useState` instead of context.
```jsx
import { PageMeta } from '@/contexts/MetadataContext'
export default function EditUnit() {
const [unitTitle, setUnitTitle] = useState('')
// ... fetch and setUnitTitle on load
return (
<section className="bg-muted/60 h-full">
<PageMeta title={unitTitle ? `Edit: ${unitTitle} - STARR` : undefined} />
{/* rest of page */}
</section>
)
}
```
---
### Placement rules
- Drop `<PageMeta>` as the **first child** of the root element in the page's main `return`.
- Do **not** add it to early/loading returns (spinner-only returns). The global fallback covers those.
- Do **not** wrap the entire page in `<PageMeta>` — it is an inline element, not a wrapper.
```jsx
// CORRECT — inline inside root element
return (
<section>
<PageMeta title="..." />
<div>...</div>
</section>
)
// WRONG — do not use as a wrapper
return (
<PageMeta title="...">
<section>...</section>
</PageMeta>
)
```
---
### Title convention
| Page type | Pattern | Example |
|---|---|---|
| List | `{Entity} - STARR` | `Courses - STARR` |
| Archived list | `Archived {Entity} - STARR` | `Archived Courses - STARR` |
| Create | `Add {Entity} - STARR` | `Add Course - STARR` |
| View (dynamic) | `{name} - STARR` | `Intro to Sales - STARR` |
| Edit (dynamic) | `Edit: {name} - STARR` | `Edit: Intro to Sales - STARR` |
| Sub-list | `{SubEntity} – {parentName} - STARR` | `Units – Intro to Sales - STARR` |
| Nested action | `{Action} – {parentName} - STARR` | `Page Builder – Lesson 1 - STARR` |
---
### Adding metadata to a new page — checklist
1. Import `PageMeta` from `@/contexts/MetadataContext`.
2. Identify what data (if any) drives the title — context state, local state, or nothing.
3. Place `<PageMeta title="..." />` as the first child of the root element in the main `return`.
4. Use the title convention above.
5. Pass `description` if the page has meaningful content to describe (e.g. a course description).
6. Pass `title={undefined}` (or omit `title`) while data is still loading.
---
### MetadataProvider (legacy)
`Login.jsx` and `Register.jsx` still use the old wrapper pattern via `MetadataProvider`. This is a backward-compat shim that internally renders `<PageMeta {...value} />` and then its children. It works but should not be used for new pages — use the inline `<PageMeta />` pattern instead.
```jsx
// Old pattern (Login / Register only — do not copy for new pages)
<MetadataProvider value={{ title: 'Login - Philproperties', description: '...' }}>
<LoginContent />
</MetadataProvider>
// New pattern (use this for all new pages)
<section>
<PageMeta title="Login - Philproperties" description="..." />
<LoginContent />
</section>
```