new commits

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-03 16:20:58 +08:00
parent 1372f4e975
commit 1d12f04967
93 changed files with 3849 additions and 1063 deletions
@@ -20,6 +20,27 @@
---
## Placement Registry
Every advertisement belongs to a `placement` — a page + position slug drawn from a fixed
registry (`models/advertisements/advertisements.placements.js`). The placement determines
the advertisement's `type` (visual format) automatically; `type` is **never** accepted from
the client and is denormalized from the placement on every write.
| Placement key | Page | Position | Format |
|---|---|---|---|
| `dashboard.hero` | Dashboard | Hero (top of page) | `hero` |
| `dashboard.popup` | Dashboard | Popup (on load) | `popup` |
| `course_list.banner` | Courses | Banner (above course grid) | `banner` |
| `course_details.banner` | Course Details | Banner (below hero) | `banner` |
| `course_details.sidebar` | Course Details | Sidebar (beside course content) | `sidebar` |
| `plans.banner` | Plans | Banner (above plan cards) | `banner` |
Adding a new placement is a one-line addition to that registry file plus wiring the
corresponding client page to fetch/render it — nothing else needs to change.
---
## Status Derivation
Status is **never** trusted as stored — it is recomputed on every read and write:
@@ -93,6 +114,7 @@ Returns one advertisement with its `image` asset and audit user info.
"data": {
"advertisement_id": 1,
"uuid": "...",
"placement": "dashboard.hero",
"type": "hero",
"status": "active",
"badge_label": "New",
@@ -131,7 +153,7 @@ Returns one advertisement with its `image` asset and audit user info.
### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | **Yes** | `hero`, `banner`, `popup`, `sidebar` |
| `placement` | string | **Yes** | A placement registry key, e.g. `dashboard.hero` — see [Placement Registry](#placement-registry). Determines `type` automatically. |
| `createdBy` | number | **Yes** | User ID of creator |
| `badge_label` | string | No | Small label shown on the ad |
| `headline` | string | No | Main heading |
@@ -157,8 +179,8 @@ Returns one advertisement with its `image` asset and audit user info.
### Error Responses
| Status | Message |
|--------|---------|
| `400` | `type is required.` |
| `400` | `Invalid type. Must be one of: hero, banner, popup, sidebar` |
| `400` | `placement is required.` |
| `400` | `Invalid placement. Must be one of: dashboard.hero, dashboard.popup, ...` |
| `400` | `createdBy is required.` |
| `400` | `Invalid size. Must be one of: sm, md, lg` |
@@ -171,7 +193,7 @@ Returns one advertisement with its `image` asset and audit user info.
Partial update. Only fields present in the body are changed. Status is recomputed after all fields are applied.
### Request Body
Same optional fields as Create. Does not accept `type` once set. Accepts `updatedBy`.
Same optional fields as Create. `type` is never accepted — it's always derived from `placement`. Accepts `updatedBy`.
### Response `200`
```json
@@ -279,6 +301,10 @@ Returns distinct values for filterable advertisement fields. Used by DataTable f
{
"status": "success",
"message": "Field values retrieved.",
"data": { "type": ["hero", "banner"], "status": ["active", "draft"] }
"data": {
"type": ["hero", "banner", "popup", "sidebar"],
"placement": ["dashboard.hero", "dashboard.popup", "course_list.banner"],
"status": ["active", "draft"]
}
}
```