feat: Added sort=random|recent query param to list_techniques endpoint…
- "backend/routers/techniques.py" - "frontend/src/api/public-client.ts" GSD-Task: S03/T01
This commit is contained in:
parent
32114ec360
commit
0b27e5752e
11 changed files with 433 additions and 3 deletions
|
|
@ -7,5 +7,5 @@ The homepage is Chrysopedia's front door. Right now it's a search box — functi
|
|||
| ID | Slice | Risk | Depends | Done | After this |
|
||||
|----|-------|------|---------|------|------------|
|
||||
| S01 | Homepage Hero & Value Proposition | medium | — | ✅ | Homepage shows tagline, value description, how-it-works steps, Start Exploring CTA, and popular topic quick-links above the fold |
|
||||
| S02 | About Page | low | — | ⬜ | About page at /about explains what Chrysopedia is, how content is extracted, and who maintains it. Link visible in footer. |
|
||||
| S02 | About Page | low | — | ✅ | About page at /about explains what Chrysopedia is, how content is extracted, and who maintains it. Link visible in footer. |
|
||||
| S03 | Featured Content & Content Teasers | low | S01 | ⬜ | Homepage shows a featured technique spotlight and Recently Added section with enriched cards |
|
||||
|
|
|
|||
76
.gsd/milestones/M009/slices/S02/S02-SUMMARY.md
Normal file
76
.gsd/milestones/M009/slices/S02/S02-SUMMARY.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
id: S02
|
||||
parent: M009
|
||||
milestone: M009
|
||||
provides:
|
||||
- About page at /about with what/how/who sections
|
||||
- Footer navigation link to /about
|
||||
requires:
|
||||
[]
|
||||
affects:
|
||||
[]
|
||||
key_files:
|
||||
- frontend/src/pages/About.tsx
|
||||
- frontend/src/App.tsx
|
||||
- frontend/src/components/AppFooter.tsx
|
||||
- frontend/src/App.css
|
||||
key_decisions:
|
||||
- Used CSS counter-based numbered pipeline steps rather than manual numbering for the extraction pipeline section
|
||||
patterns_established:
|
||||
- (none)
|
||||
observability_surfaces:
|
||||
- none
|
||||
drill_down_paths:
|
||||
- .gsd/milestones/M009/slices/S02/tasks/T01-SUMMARY.md
|
||||
duration: ""
|
||||
verification_result: passed
|
||||
completed_at: 2026-03-31T05:42:31.536Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# S02: About Page
|
||||
|
||||
**Added /about page with three content sections and a footer navigation link.**
|
||||
|
||||
## What Happened
|
||||
|
||||
Created About.tsx with three sections: what Chrysopedia is (purpose and name origin), how content is extracted (five-step pipeline breakdown using CSS counter-based numbered steps), and who maintains it (links to xpltd GitHub org and repo). Added the /about route in App.tsx. Added an About link in AppFooter.tsx using react-router-dom Link. Styled with .about-* classes in App.css following existing BEM and CSS custom property patterns, including a responsive breakpoint for the hero title. TypeScript compiles cleanly, Vite build produces 47 modules with no warnings.
|
||||
|
||||
## Verification
|
||||
|
||||
npx tsc --noEmit: exit 0, zero errors. npm run build: exit 0, 47 modules, no warnings. Route /about registered in App.tsx. Footer link present in AppFooter.tsx pointing to /about.
|
||||
|
||||
## Requirements Advanced
|
||||
|
||||
None.
|
||||
|
||||
## Requirements Validated
|
||||
|
||||
None.
|
||||
|
||||
## New Requirements Surfaced
|
||||
|
||||
None.
|
||||
|
||||
## Requirements Invalidated or Re-scoped
|
||||
|
||||
None.
|
||||
|
||||
## Deviations
|
||||
|
||||
None.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
None.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
None.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `frontend/src/pages/About.tsx` — New page component with three content sections (what, how, who)
|
||||
- `frontend/src/App.tsx` — Added /about route
|
||||
- `frontend/src/components/AppFooter.tsx` — Added About link
|
||||
- `frontend/src/App.css` — Added .about-* styles with responsive breakpoint
|
||||
38
.gsd/milestones/M009/slices/S02/S02-UAT.md
Normal file
38
.gsd/milestones/M009/slices/S02/S02-UAT.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# S02: About Page — UAT
|
||||
|
||||
**Milestone:** M009
|
||||
**Written:** 2026-03-31T05:42:31.536Z
|
||||
|
||||
## UAT: About Page
|
||||
|
||||
### Preconditions
|
||||
- Frontend is running (dev server or production build served)
|
||||
- Browser open to the application root
|
||||
|
||||
### Test 1: Footer link navigates to About page
|
||||
1. Scroll to the page footer on any page (homepage, technique page, etc.)
|
||||
2. Locate the "About" link in the footer
|
||||
3. Click the link
|
||||
4. **Expected:** Browser navigates to `/about`. Page displays "About Chrysopedia" heading.
|
||||
|
||||
### Test 2: What section content
|
||||
1. Navigate to `/about`
|
||||
2. Locate the "What Is Chrysopedia?" section
|
||||
3. **Expected:** Section explains Chrysopedia turns long-form tutorials into a searchable knowledge base. Mentions the name origin (chrysopoeia — transmutation).
|
||||
|
||||
### Test 3: How section content
|
||||
1. On the `/about` page, locate the "How Content Is Extracted" section
|
||||
2. **Expected:** Five numbered pipeline steps are displayed (Transcription, Segmentation, Key Moment Extraction, Classification, Technique Synthesis). Each step has a brief description.
|
||||
|
||||
### Test 4: Who section content
|
||||
1. On the `/about` page, locate the "Who Maintains This" section
|
||||
2. **Expected:** Section mentions xpltd with a link to the GitHub organization. Contains a link to the Chrysopedia repository.
|
||||
|
||||
### Test 5: Direct URL access
|
||||
1. Type `/about` directly in the browser address bar and press Enter
|
||||
2. **Expected:** About page loads correctly without 404 or redirect.
|
||||
|
||||
### Test 6: Responsive layout
|
||||
1. Navigate to `/about`
|
||||
2. Resize browser to mobile width (~375px)
|
||||
3. **Expected:** Hero title and section content reflow to single column. Text remains readable. No horizontal overflow.
|
||||
30
.gsd/milestones/M009/slices/S02/tasks/T01-VERIFY.json
Normal file
30
.gsd/milestones/M009/slices/S02/tasks/T01-VERIFY.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"taskId": "T01",
|
||||
"unitId": "M009/S02/T01",
|
||||
"timestamp": 1774935714608,
|
||||
"passed": false,
|
||||
"discoverySource": "task-plan",
|
||||
"checks": [
|
||||
{
|
||||
"command": "cd frontend",
|
||||
"exitCode": 0,
|
||||
"durationMs": 4,
|
||||
"verdict": "pass"
|
||||
},
|
||||
{
|
||||
"command": "npx tsc --noEmit",
|
||||
"exitCode": 1,
|
||||
"durationMs": 755,
|
||||
"verdict": "fail"
|
||||
},
|
||||
{
|
||||
"command": "npm run build",
|
||||
"exitCode": 254,
|
||||
"durationMs": 81,
|
||||
"verdict": "fail"
|
||||
}
|
||||
],
|
||||
"retryAttempt": 1,
|
||||
"maxRetries": 2
|
||||
}
|
||||
|
|
@ -1,6 +1,53 @@
|
|||
# S03: Featured Content & Content Teasers
|
||||
|
||||
**Goal:** Demonstrate content value immediately — a first-time visitor sees a real technique before searching
|
||||
**Goal:** Homepage shows a featured technique spotlight and recently-added section with enriched grid-layout cards
|
||||
**Demo:** After this: Homepage shows a featured technique spotlight and Recently Added section with enriched cards
|
||||
|
||||
## Tasks
|
||||
- [x] **T01: Added sort=random|recent query param to list_techniques endpoint and wired it through the frontend API client** — Add an optional `sort` query parameter to the backend `list_techniques` endpoint in `backend/routers/techniques.py`. Accepts 'recent' (default, existing created_at DESC behavior) and 'random' (using func.random(), same pattern as creators.py line 67). Update `TechniqueListParams` in `frontend/src/api/public-client.ts` to include `sort?: string` and pass it as a query string param in `fetchTechniques`.
|
||||
|
||||
Backend change pattern (from creators.py):
|
||||
```python
|
||||
sort: Annotated[str, Query()] = "recent",
|
||||
```
|
||||
Then in the query building:
|
||||
```python
|
||||
if sort == "random":
|
||||
stmt = stmt.order_by(func.random())
|
||||
else:
|
||||
stmt = stmt.order_by(TechniquePage.created_at.desc())
|
||||
```
|
||||
|
||||
Replace the existing hardcoded `.order_by(TechniquePage.created_at.desc())` with the conditional.
|
||||
- Estimate: 15m
|
||||
- Files: backend/routers/techniques.py, frontend/src/api/public-client.ts
|
||||
- Verify: cd frontend && npx tsc --noEmit && grep -q 'sort.*random' ../backend/routers/techniques.py && grep -q 'sort' src/api/public-client.ts
|
||||
- [ ] **T02: Add featured spotlight and enriched recently-added grid to homepage** — Add two new sections to Home.tsx and corresponding styles to App.css:
|
||||
|
||||
**1. Featured Technique Spotlight** (above recently-added section):
|
||||
- New useEffect fetching `fetchTechniques({ sort: 'random', limit: 1 })` into `featured` state
|
||||
- Renders a `.home-featured` section with: technique title (linked to /techniques/{slug}), summary (full, not truncated), creator name (linked to /creators/{creator_slug}), topic_category badge, topic_tags pills, key_moment_count
|
||||
- Left-aligned content (breaks from hero's center alignment)
|
||||
- Silently hidden when fetch fails or returns empty (same pattern as popular topics)
|
||||
- Has a subtle accent left border for visual distinction
|
||||
|
||||
**2. Enriched Recently Added grid**:
|
||||
- Change `.recent-list` from flex-column to CSS grid: `grid-template-columns: repeat(2, 1fr)` with responsive collapse to 1 column at 640px
|
||||
- Widen `.recent-section` from `max-width: 36rem` to `42rem`
|
||||
- Filter out the featured technique by ID from the recently-added list to avoid duplication: `recent.filter(t => t.id !== featured?.id)`
|
||||
- Add more prominent summary display (up to 150 chars instead of 100)
|
||||
|
||||
**Styles** in App.css:
|
||||
- `.home-featured` section: max-width 42rem, centered margin, left-aligned text, 3px accent left border, surface background, card-style padding
|
||||
- `.home-featured__title`: larger font, linked
|
||||
- `.home-featured__summary`: full summary text, secondary color
|
||||
- `.home-featured__meta`: badge + tags + moments row
|
||||
- `.home-featured__creator`: creator link
|
||||
- Updated `.recent-list` to grid layout
|
||||
- Updated `.recent-section` max-width
|
||||
- Responsive rules at 640px for grid collapse
|
||||
|
||||
Use BEM naming under `.home-featured` prefix. Use CSS custom properties from D017.
|
||||
- Estimate: 45m
|
||||
- Files: frontend/src/pages/Home.tsx, frontend/src/App.css
|
||||
- Verify: cd frontend && npx vite build && grep -q 'home-featured' src/pages/Home.tsx && grep -q 'home-featured' src/App.css && grep -q 'grid-template-columns' src/App.css
|
||||
|
|
|
|||
67
.gsd/milestones/M009/slices/S03/S03-RESEARCH.md
Normal file
67
.gsd/milestones/M009/slices/S03/S03-RESEARCH.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# S03 Research: Featured Content & Content Teasers
|
||||
|
||||
## Depth: Light
|
||||
|
||||
Straightforward frontend work — adding two new homepage sections using established patterns and existing API endpoints. No new backend routes required; no unfamiliar technology.
|
||||
|
||||
## Summary
|
||||
|
||||
The homepage (Home.tsx, 306 lines) already has: hero section with tagline/value-prop/how-it-works, search bar with typeahead, nav cards, popular topics pills, and a "Recently Added" section fetching `GET /api/v1/techniques?limit=5` sorted by `created_at DESC`. The slice adds:
|
||||
|
||||
1. **Featured Technique Spotlight** — a prominent single-technique card above the recently-added section, highlighting one technique with more detail (summary, creator, tags, moment count).
|
||||
2. **Enriched Recently Added cards** — upgrade the existing `recent-card` elements with richer visual treatment (summary preview, better badge layout, visual hierarchy).
|
||||
|
||||
## Recommendation
|
||||
|
||||
### Featured Technique Spotlight
|
||||
|
||||
No dedicated backend endpoint needed. Two viable approaches:
|
||||
|
||||
- **Option A (simple):** Fetch `GET /api/v1/techniques?limit=1` — gets the most recently created technique. Deterministic, zero backend changes.
|
||||
- **Option B (random):** Add a `sort=random` parameter to the existing `list_techniques` endpoint (same pattern as `creators.py` line 67: `func.random()`), then fetch `?limit=1&sort=random`. Slightly more interesting UX — different technique on each visit.
|
||||
|
||||
**Recommendation: Option B.** The pattern is already established in the creators endpoint. Adding `sort` param to `/techniques` is a 5-line backend change. A rotating spotlight is more engaging and aligns with R014's creator equity spirit (no single technique permanently promoted).
|
||||
|
||||
### Enriched Recently Added
|
||||
|
||||
The existing `recent-card` already renders title, creator_name, topic_category badge, topic_tags pills, summary (truncated to 100 chars), and moment count. The `TechniqueListItem` type has all needed fields. Enrichment is purely CSS/layout:
|
||||
|
||||
- Wider card layout (current max-width: 36rem is narrow for cards with metadata)
|
||||
- Grid layout instead of single-column stack (2 columns on desktop)
|
||||
- More prominent summary text
|
||||
- Better visual hierarchy between title and metadata
|
||||
|
||||
### No new API types needed
|
||||
|
||||
`TechniqueListItem` already includes: title, slug, topic_category, topic_tags, summary, creator_name, creator_slug, source_quality, key_moment_count. All fields needed for both the spotlight and enriched cards are present.
|
||||
|
||||
## Implementation Landscape
|
||||
|
||||
### Files to modify
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `backend/routers/techniques.py` | Add optional `sort` query param (random/recent, default recent) to `list_techniques` |
|
||||
| `frontend/src/api/public-client.ts` | Add `sort` to `TechniqueListParams` |
|
||||
| `frontend/src/pages/Home.tsx` | Add featured spotlight section, fetch with `sort=random&limit=1`; restructure recently-added layout |
|
||||
| `frontend/src/App.css` | Add `.home-featured` spotlight styles, update `.recent-*` styles for grid layout and enriched cards |
|
||||
|
||||
### Existing patterns to follow
|
||||
|
||||
- **BEM naming under `.home-` prefix** (S01 pattern): `.home-featured__title`, `.home-featured__summary`, etc.
|
||||
- **Optional data-driven sections silently hide on API error** (S01 popular topics pattern): featured section should not show error UI if fetch fails
|
||||
- **CSS custom properties** from D017: `--color-accent`, `--color-bg-surface`, `--color-border`, `--color-badge-category-*`
|
||||
- **Card hover transition**: `border-color 0.15s, box-shadow 0.15s` with `--color-accent-hover` / `--color-accent-subtle`
|
||||
|
||||
### Constraints
|
||||
|
||||
- Homepage hero section is `text-align: center` — the featured section should break from this with left-aligned content for readability
|
||||
- Recently Added is currently `max-width: 36rem` — can widen to 42rem or match the how-it-works grid width for card grid
|
||||
- The featured technique fetch is a separate API call from the recently-added fetch — need to exclude the featured technique from the recently-added list to avoid duplication (filter client-side by ID)
|
||||
|
||||
### Verification approach
|
||||
|
||||
- `cd frontend && npx vite build` — zero TypeScript errors
|
||||
- Source inspection: confirm `.home-featured` section renders with spotlight technique data
|
||||
- Source inspection: confirm recently-added uses grid layout with enriched card markup
|
||||
- Visual inspection via browser if available
|
||||
37
.gsd/milestones/M009/slices/S03/tasks/T01-PLAN.md
Normal file
37
.gsd/milestones/M009/slices/S03/tasks/T01-PLAN.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
estimated_steps: 13
|
||||
estimated_files: 2
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T01: Add sort=random query param to list_techniques endpoint and API client
|
||||
|
||||
Add an optional `sort` query parameter to the backend `list_techniques` endpoint in `backend/routers/techniques.py`. Accepts 'recent' (default, existing created_at DESC behavior) and 'random' (using func.random(), same pattern as creators.py line 67). Update `TechniqueListParams` in `frontend/src/api/public-client.ts` to include `sort?: string` and pass it as a query string param in `fetchTechniques`.
|
||||
|
||||
Backend change pattern (from creators.py):
|
||||
```python
|
||||
sort: Annotated[str, Query()] = "recent",
|
||||
```
|
||||
Then in the query building:
|
||||
```python
|
||||
if sort == "random":
|
||||
stmt = stmt.order_by(func.random())
|
||||
else:
|
||||
stmt = stmt.order_by(TechniquePage.created_at.desc())
|
||||
```
|
||||
|
||||
Replace the existing hardcoded `.order_by(TechniquePage.created_at.desc())` with the conditional.
|
||||
|
||||
## Inputs
|
||||
|
||||
- ``backend/routers/techniques.py` — existing list_techniques endpoint to add sort param to`
|
||||
- ``frontend/src/api/public-client.ts` — existing TechniqueListParams and fetchTechniques to add sort support`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- ``backend/routers/techniques.py` — list_techniques with sort=random|recent query param`
|
||||
- ``frontend/src/api/public-client.ts` — TechniqueListParams with sort field, fetchTechniques passing sort param`
|
||||
|
||||
## Verification
|
||||
|
||||
cd frontend && npx tsc --noEmit && grep -q 'sort.*random' ../backend/routers/techniques.py && grep -q 'sort' src/api/public-client.ts
|
||||
78
.gsd/milestones/M009/slices/S03/tasks/T01-SUMMARY.md
Normal file
78
.gsd/milestones/M009/slices/S03/tasks/T01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
id: T01
|
||||
parent: S03
|
||||
milestone: M009
|
||||
provides: []
|
||||
requires: []
|
||||
affects: []
|
||||
key_files: ["backend/routers/techniques.py", "frontend/src/api/public-client.ts"]
|
||||
key_decisions: ["Followed same pattern as creators.py for sort param (func.random() for random, created_at DESC for recent)"]
|
||||
patterns_established: []
|
||||
drill_down_paths: []
|
||||
observability_surfaces: []
|
||||
duration: ""
|
||||
verification_result: "TypeScript compiles cleanly (npx tsc --noEmit), backend grep confirms sort=random logic present, frontend grep confirms sort param wired through."
|
||||
completed_at: 2026-03-31T05:46:25.296Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T01: Added sort=random|recent query param to list_techniques endpoint and wired it through the frontend API client
|
||||
|
||||
> Added sort=random|recent query param to list_techniques endpoint and wired it through the frontend API client
|
||||
|
||||
## What Happened
|
||||
---
|
||||
id: T01
|
||||
parent: S03
|
||||
milestone: M009
|
||||
key_files:
|
||||
- backend/routers/techniques.py
|
||||
- frontend/src/api/public-client.ts
|
||||
key_decisions:
|
||||
- Followed same pattern as creators.py for sort param (func.random() for random, created_at DESC for recent)
|
||||
duration: ""
|
||||
verification_result: passed
|
||||
completed_at: 2026-03-31T05:46:25.296Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T01: Added sort=random|recent query param to list_techniques endpoint and wired it through the frontend API client
|
||||
|
||||
**Added sort=random|recent query param to list_techniques endpoint and wired it through the frontend API client**
|
||||
|
||||
## What Happened
|
||||
|
||||
Added an optional `sort` query parameter to `list_techniques` in `backend/routers/techniques.py`, accepting "recent" (default, existing created_at DESC) and "random" (using func.random()). Replaced the hardcoded order_by with a conditional. On the frontend, added `sort?: string` to `TechniqueListParams` and wired it through `fetchTechniques` as a query string parameter.
|
||||
|
||||
## Verification
|
||||
|
||||
TypeScript compiles cleanly (npx tsc --noEmit), backend grep confirms sort=random logic present, frontend grep confirms sort param wired through.
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
| # | Command | Exit Code | Verdict | Duration |
|
||||
|---|---------|-----------|---------|----------|
|
||||
| 1 | `cd frontend && npx tsc --noEmit` | 0 | ✅ pass | 7900ms |
|
||||
| 2 | `grep -q 'sort.*random' backend/routers/techniques.py` | 0 | ✅ pass | 100ms |
|
||||
| 3 | `grep -q 'sort' frontend/src/api/public-client.ts` | 0 | ✅ pass | 100ms |
|
||||
|
||||
|
||||
## Deviations
|
||||
|
||||
None.
|
||||
|
||||
## Known Issues
|
||||
|
||||
None.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `backend/routers/techniques.py`
|
||||
- `frontend/src/api/public-client.ts`
|
||||
|
||||
|
||||
## Deviations
|
||||
None.
|
||||
|
||||
## Known Issues
|
||||
None.
|
||||
49
.gsd/milestones/M009/slices/S03/tasks/T02-PLAN.md
Normal file
49
.gsd/milestones/M009/slices/S03/tasks/T02-PLAN.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
estimated_steps: 22
|
||||
estimated_files: 2
|
||||
skills_used: []
|
||||
---
|
||||
|
||||
# T02: Add featured spotlight and enriched recently-added grid to homepage
|
||||
|
||||
Add two new sections to Home.tsx and corresponding styles to App.css:
|
||||
|
||||
**1. Featured Technique Spotlight** (above recently-added section):
|
||||
- New useEffect fetching `fetchTechniques({ sort: 'random', limit: 1 })` into `featured` state
|
||||
- Renders a `.home-featured` section with: technique title (linked to /techniques/{slug}), summary (full, not truncated), creator name (linked to /creators/{creator_slug}), topic_category badge, topic_tags pills, key_moment_count
|
||||
- Left-aligned content (breaks from hero's center alignment)
|
||||
- Silently hidden when fetch fails or returns empty (same pattern as popular topics)
|
||||
- Has a subtle accent left border for visual distinction
|
||||
|
||||
**2. Enriched Recently Added grid**:
|
||||
- Change `.recent-list` from flex-column to CSS grid: `grid-template-columns: repeat(2, 1fr)` with responsive collapse to 1 column at 640px
|
||||
- Widen `.recent-section` from `max-width: 36rem` to `42rem`
|
||||
- Filter out the featured technique by ID from the recently-added list to avoid duplication: `recent.filter(t => t.id !== featured?.id)`
|
||||
- Add more prominent summary display (up to 150 chars instead of 100)
|
||||
|
||||
**Styles** in App.css:
|
||||
- `.home-featured` section: max-width 42rem, centered margin, left-aligned text, 3px accent left border, surface background, card-style padding
|
||||
- `.home-featured__title`: larger font, linked
|
||||
- `.home-featured__summary`: full summary text, secondary color
|
||||
- `.home-featured__meta`: badge + tags + moments row
|
||||
- `.home-featured__creator`: creator link
|
||||
- Updated `.recent-list` to grid layout
|
||||
- Updated `.recent-section` max-width
|
||||
- Responsive rules at 640px for grid collapse
|
||||
|
||||
Use BEM naming under `.home-featured` prefix. Use CSS custom properties from D017.
|
||||
|
||||
## Inputs
|
||||
|
||||
- ``frontend/src/pages/Home.tsx` — existing homepage with hero, search, nav cards, recently-added section`
|
||||
- ``frontend/src/App.css` — existing styles including .recent-* classes and CSS custom properties`
|
||||
- ``frontend/src/api/public-client.ts` — fetchTechniques with sort param (from T01)`
|
||||
|
||||
## Expected Output
|
||||
|
||||
- ``frontend/src/pages/Home.tsx` — homepage with featured spotlight section and enriched recently-added grid`
|
||||
- ``frontend/src/App.css` — new .home-featured styles, updated .recent-list to grid layout, responsive rules`
|
||||
|
||||
## Verification
|
||||
|
||||
cd frontend && npx vite build && grep -q 'home-featured' src/pages/Home.tsx && grep -q 'home-featured' src/App.css && grep -q 'grid-template-columns' src/App.css
|
||||
|
|
@ -33,6 +33,7 @@ router = APIRouter(prefix="/techniques", tags=["techniques"])
|
|||
async def list_techniques(
|
||||
category: Annotated[str | None, Query()] = None,
|
||||
creator_slug: Annotated[str | None, Query()] = None,
|
||||
sort: Annotated[str, Query()] = "recent",
|
||||
offset: Annotated[int, Query(ge=0)] = 0,
|
||||
limit: Annotated[int, Query(ge=1, le=100)] = 50,
|
||||
db: AsyncSession = Depends(get_session),
|
||||
|
|
@ -72,7 +73,12 @@ async def list_techniques(
|
|||
Creator.slug == creator_slug
|
||||
)
|
||||
|
||||
stmt = stmt.options(selectinload(TechniquePage.creator)).order_by(TechniquePage.created_at.desc()).offset(offset).limit(limit)
|
||||
stmt = stmt.options(selectinload(TechniquePage.creator))
|
||||
if sort == "random":
|
||||
stmt = stmt.order_by(func.random())
|
||||
else:
|
||||
stmt = stmt.order_by(TechniquePage.created_at.desc())
|
||||
stmt = stmt.offset(offset).limit(limit)
|
||||
result = await db.execute(stmt)
|
||||
rows = result.all()
|
||||
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ export interface TechniqueListParams {
|
|||
offset?: number;
|
||||
category?: string;
|
||||
creator_slug?: string;
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
export async function fetchTechniques(
|
||||
|
|
@ -228,6 +229,7 @@ export async function fetchTechniques(
|
|||
if (params.offset !== undefined) qs.set("offset", String(params.offset));
|
||||
if (params.category) qs.set("category", params.category);
|
||||
if (params.creator_slug) qs.set("creator_slug", params.creator_slug);
|
||||
if (params.sort) qs.set("sort", params.sort);
|
||||
const query = qs.toString();
|
||||
return request<TechniqueListResponse>(
|
||||
`${BASE}/techniques${query ? `?${query}` : ""}`,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue