initial commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Adding a New Advisor-Only Page to SPQ v2
|
||||
|
||||
Five files to touch. Follow the exact pattern below.
|
||||
|
||||
## Step-by-step
|
||||
|
||||
### 1. Create the page component
|
||||
```
|
||||
src/pages/advisor/<PageName>.tsx
|
||||
```
|
||||
Use `export default function PageName()` — lazy import expects a default export.
|
||||
|
||||
Page shell pattern:
|
||||
```tsx
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { pb } from '../../lib/pocketbase';
|
||||
|
||||
export default function PageName() {
|
||||
const userId = pb.authStore.model?.id as string | undefined;
|
||||
// ... state, effects, return JSX using the same Tailwind classes
|
||||
// as other advisor pages (AdvisorTeam as canonical reference)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `src/App.tsx` — lazy import + route
|
||||
```tsx
|
||||
const PageName = lazy(() => import('./pages/advisor/PageName'));
|
||||
```
|
||||
Route inside `<Route element={<OwnerLayout />}>`:
|
||||
```tsx
|
||||
<Route path="/route-path" element={<AdvisorOnly><PageName /></AdvisorOnly>} />
|
||||
```
|
||||
`AdvisorOnly` restricts access to `advisor` role only.
|
||||
|
||||
### 3. `src/components/Layout.tsx` — nav item
|
||||
Add the icon to the lucide-react import, then add a nav item in the `navItems` array:
|
||||
```tsx
|
||||
{ to: "/route-path", label: "Page Label", icon: IconName },
|
||||
```
|
||||
Also add preload import and wire handlers:
|
||||
```tsx
|
||||
import { preloadSettingsPage, preloadTimeCards } from "../lib/routePreload";
|
||||
// In the NavLink loop:
|
||||
onMouseEnter={item.to === "/route-path" ? preloadPageName : ...}
|
||||
```
|
||||
|
||||
### 4. `src/components/mobile/MobileLayout.tsx` — same nav pattern
|
||||
Mirror the import, nav item, and preload wiring from step 3.
|
||||
|
||||
### 5. `src/lib/routePreload.ts` — preload helpers
|
||||
```ts
|
||||
export const loadPageName = () => import('../pages/advisor/PageName');
|
||||
export function preloadPageName() {
|
||||
void loadPageName();
|
||||
}
|
||||
```
|
||||
|
||||
## Common page components available
|
||||
|
||||
- `ListSkeleton` / `ListError` — from `../../components/ui/`
|
||||
- `StatusBadge` — define inline or import from existing pages
|
||||
- Loading/error/empty pattern: `loading ? <ListSkeleton /> : error ? <ListError /> : actualContent`
|
||||
|
||||
## Verification
|
||||
```bash
|
||||
npm run build && npm run lint && npm run test
|
||||
```
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Lazy import path**: Must point to the .tsx file directly. The `lazy(() => import(...))` wrapper uses dynamic import — Vite code-splits it.
|
||||
- **Don't forget MobileLayout** — every new page needs a nav entry in both layouts or mobile users can't reach it.
|
||||
- **Preload wiring**: Both `onMouseEnter` and `onFocus` need the preload call in both Layout.tsx and MobileLayout.tsx. Missing either means no prefetch.
|
||||
Reference in New Issue
Block a user