Files
starr-philproperties/apps/api/controllers/admin/documentation/advertisements.md
T

8.1 KiB

Advertisements Controller Documentation

File: controllers/admin/advertisements.controller.js
Base URL: /api/admin/advertisements
Guards: authenticate → requireAdmin() → adminLimiter


Table of Contents


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:

Condition Derived Status
deletedAt is set archived
is_active = false draft
end_date < now expired
start_date > now scheduled
Otherwise active

archived is the only status that bypasses derivation (set explicitly by archive/restore).


CTAs

Each advertisement supports up to 2 CTAs:

[
  { "label": "Learn More", "link": "/courses", "variant": "default" },
  { "label": "Sign Up",    "link": "/register", "variant": "outline" }
]
  • First CTA defaults to "default" variant; second defaults to "outline".
  • An explicit valid variant from the client ("default" or "outline") always wins.
  • Items beyond 2 are silently discarded.

Get All Advertisements

GET /api/admin/advertisements

Returns a paginated list of active (non-deleted) advertisements. Status is resynced on the way out.

Query Parameters

Parameter Type Required Description
page number No Default: 1
limit number No Default: 10, max: 1000
filters array No JSON array of filter objects
sort array No JSON array of sort objects

Response 200

{
  "status": "success",
  "message": "Advertisements retrieved.",
  "data": [...],
  "pagination": { "page": 1, "limit": 10, "total": 5, "totalPages": 1 }
}

Get Single Advertisement

GET /api/admin/advertisements/:advertisementId

Returns one advertisement with its image asset and audit user info.

Response 200

{
  "status": "success",
  "message": "Advertisement retrieved.",
  "data": {
    "advertisement_id": 1,
    "uuid": "...",
    "placement": "dashboard.hero",
    "type": "hero",
    "status": "active",
    "badge_label": "New",
    "headline": "Welcome",
    "description": "...",
    "image_url": null,
    "image_asset_id": 12,
    "image": { "asset_id": 12, "display_name": "hero.jpg", "file_url": "...", "thumbnail_url": "..." },
    "ctas": [{ "label": "Start", "link": "/start", "variant": "default" }],
    "start_date": "2026-01-01T00:00:00.000Z",
    "end_date": null,
    "order": 0,
    "is_active": true,
    "size": null,
    "click_count": 0,
    "creator": { "user_id": 1, "full_name": "Admin User" },
    "updater": null,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  }
}

Error Responses

Status Message
400 Invalid advertisement ID.
404 Advertisement not found.

Create Advertisement

POST /api/admin/advertisements

Request Body

Field Type Required Description
placement string Yes A placement registry key, e.g. dashboard.hero — see 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
description string No Body text
image_url string No Direct image URL
image_asset_id number No FK to assets table
ctas array No Up to 2 CTA objects [{ label, link, variant }]
start_date date No ISO date string
end_date date No ISO date string
order number No Display order. Default: 0
is_active boolean No Default: true
size string No sm, md, lg — banner only

Response 201

{
  "status": "success",
  "message": "Advertisement created.",
  "data": { ... }
}

Error Responses

Status Message
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

Update Advertisement

PATCH /api/admin/advertisements/:advertisementId

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. type is never accepted — it's always derived from placement. Accepts updatedBy.

Response 200

{ "status": "success", "message": "Advertisement updated.", "data": { ... } }

Archive Advertisement

DELETE /api/admin/advertisements/:advertisementId

Soft-deletes the advertisement (deletedAt set, status → archived).

Response 200

{ "status": "success", "message": "Advertisement archived." }

Bulk Archive Advertisements

DELETE /api/admin/advertisements/bulk

Rate-limited. Soft-deletes multiple advertisements.

Request Body

{ "ids": [1, 2, 3], "deletedBy": 1 }

Response 200

{
  "status": "success",
  "message": "3 advertisement(s) archived.",
  "archived_ids": [1, 2, 3],
  "skipped_ids": []
}

Restore Advertisement

PATCH /api/admin/advertisements/:advertisementId/restore

Restores a soft-deleted advertisement.

Response 200

{ "status": "success", "message": "Advertisement restored.", "data": { ... } }

Bulk Restore Advertisements

PATCH /api/admin/advertisements/bulk-restore

Request Body

{ "ids": [1, 2] }

Response 200

{
  "status": "success",
  "message": "2 advertisement(s) restored.",
  "restored_ids": [1, 2],
  "skipped_ids": []
}

Get Archived Advertisements

GET /api/admin/advertisements/archived

Returns paginated list of soft-deleted advertisements.

Response 200

{
  "status": "success",
  "message": "Archived advertisements retrieved.",
  "data": [...],
  "pagination": { ... }
}

Get Field Values

GET /api/admin/advertisements/field-values

Returns distinct values for filterable advertisement fields. Used by DataTable filter dropdowns.

Response 200

{
  "status": "success",
  "message": "Field values retrieved.",
  "data": {
    "type": ["hero", "banner", "popup", "sidebar"],
    "placement": ["dashboard.hero", "dashboard.popup", "course_list.banner"],
    "status": ["active", "draft"]
  }
}