feat: add direct-save CMS and About archive

This commit is contained in:
2026-09-28 20:29:00 +03:30
parent 94b32310af
commit 0f6220f72c
12 changed files with 957 additions and 41 deletions

View File

@@ -1,15 +1,15 @@
# Public content API v1
Status: approved by the owner on 2026-09-27. These anonymous `GET` endpoints expose the latest saved CMS values. Content saves are live immediately; there are no revision, publication, or preview fields or routes.
Status: direct-save API approved by the owner on 2026-09-27; Home/About and footer extensions follow the owner-reviewed redesign. These anonymous `GET` endpoints expose the latest saved CMS values. Content saves are live immediately; there are no revision, publication, or preview fields or routes.
## Routes
- `GET /api/v1/content/site` returns shared association identity, navigation labels, contacts, footer copy, logo, and SEO defaults.
- `GET /api/v1/content/pages/home` returns the fixed Home fields and SEO.
- `GET /api/v1/content/pages/about` returns the fixed About fields, SEO, and its typed text items.
- `GET /api/v1/content/site` returns shared association identity, navigation labels, contacts, footer copy, logo, the optional eNAMAD image/verification URL, and SEO defaults.
- `GET /api/v1/content/pages/home` returns fixed Home fields, editable CTA labels and destinations, and SEO.
- `GET /api/v1/content/pages/about` returns the fixed About fields, SEO, typed FAQ items, audience cards, association history, academic-year member archives with selected past event highlights, and its separate memories gallery.
- `GET /api/v1/content/pages/events` and `/api/v1/content/pages/blog` return listing copy and SEO. Event and blog records remain owned by their existing APIs.
Unknown page slugs and missing fixed records return `404`. Successful content responses send `Cache-Control: no-store`, so a saved admin edit is available on the next API request. Image values are either `null` or `{ "url": "absolute media URL", "alt": "description" }`. Route paths are code-owned; the API returns only the fixed route keys and editable labels.
Unknown page slugs and missing fixed records return `404`. Successful content responses send `Cache-Control: no-store`, so a saved admin edit is available on the next API request. Image values are either `null` or `{ "url": "absolute media URL", "alt": "description" }`. Navigation route paths are code-owned; Home CTA destinations are editable root-relative paths or secure `https` URLs. About member photos are optional. The footer eNAMAD badge has no link until an administrator enters its verification URL.
## Shapes
@@ -26,8 +26,28 @@ Site response:
"contacts": { "email": "", "phone": "", "telegram": "", "instagram": "", "website": "" },
"address": "",
"footer_copy": "",
"enamad_image": null,
"enamad_url": "",
"seo": { "title": "", "description": "", "image": null }
}
```
Every page response has `slug`, `title`, `intro`, and `seo: { title, description, image }`. Home adds its fixed hero, featured event/post introductions, and participation fields. About adds its fixed identity, mission, activities, values, committee, affiliation, FAQ, and contact fields plus `items: [{ kind, title, text, position }]`. Events and Blog listing responses contain only their page copy and SEO fields.
Every page response has `slug`, `title`, `intro`, and `seo: { title, description, image }`. Home adds its hero, featured event/post sections, participation fields, and labels/destinations for section and card actions. About adds its identity, mission, vision, activities, values, FAQ, history, audience, annual archive, memories gallery, and CTA fields. Its public collections have these shapes:
```json
{
"items": [{ "kind": "mission", "title": "...", "text": "...", "position": 0 }],
"members": [{ "name": "...", "role": "...", "entry_year": "...", "bio": "...", "photo": null, "position": 0 }],
"audiences": [{ "title": "...", "text": "...", "icon": "students", "position": 0 }],
"milestones": [{ "year_label": "...", "title": "...", "description": "...", "position": 0 }],
"years": [{
"year_label": "۱۴۰۴–۱۴۰۵",
"position": 0,
"members": [{ "name": "...", "role": "...", "entry_year": "۱۴۰۴–۱۴۰۵", "bio": "...", "photo": null, "position": 0 }],
"selected_events": [{ "id": 1, "title": "...", "slug": "...", "start_time": "...", "featured_image": null }]
}],
"gallery_images": [{ "image": { "url": "...", "alt": "..." }, "caption": "...", "position": 0 }]
}
```
The legacy top-level `members` list remains available for API compatibility. New year-grouped members are the source for the About archive. Only selected past events that are published or completed are included in `years[].selected_events`; each card links to the existing event detail route. The separate memories gallery remains curated through the About admin page. Legacy committee and multi-association data remains stored for compatibility but is no longer returned or rendered. Events and Blog listing responses contain their page copy and SEO fields; their event/post records remain owned by existing APIs.