Skip to content

Home page grid

Purpose

The Home page grid screen configures what the player site shows on its landing page: featured-games carousel, top categories, hero banners. Saving here changes the public site immediately (no deploy). It is one of the few screens in the admin panel where a single click is visible to every visitor.

Audience

Marketing / merchandising. Engineering uses it to recover from a bad launch (revert to known-good ordering).

Path in admin-fe

Screen URL Page
Home grid editor /manage-home-grid ebit-admin-fe/src/app/(dashboard)/manage-home-grid/page.tsx

Backing API endpoints

The grid is persisted via the casino-games admin endpoints (it's a games surface):

Endpoint Source
POST /admin/casino/games/main (set the main / home grid composition) apps/api/src/casino/games/controller/games.admin.controller.ts:60
POST /admin/casino/games/hydrate (refresh game catalog from providers) apps/api/src/casino/games/controller/games.admin.controller.ts:50
GET /admin/casino/games/all (catalog source) apps/api/src/casino/games/controller/games.admin.controller.ts:141
POST /admin/casino/games/:slug/images (banner art per game) apps/api/src/casino/games/controller/games.admin.controller.ts:153
POST /admin/casino/games/providers/:slug/images (provider art) apps/api/src/casino/games/controller/games.admin.controller.ts:81

Frontend wiring: ebit-admin-fe/src/queries/slots/index.tsuseUpdateHomeGridMutation (line 125).

Key actions

Action Required permission API call DB tables touched Audit-logged?
List all games for ordering casino.games.view GET /admin/casino/games/all Game, GameProvider yes
Save grid (composition + order) casino.games.edit POST /admin/casino/games/main Game.featured*, ordering columns yes
Hydrate provider catalog (sync new slots) casino.games.edit POST /admin/casino/games/hydrate Game, GameProvider (upserts) yes
Upload banner per game casino.games.edit POST /admin/casino/games/:slug/images Game.imageUrl, S3 yes
Upload provider logo casino.games.edit POST /admin/casino/games/providers/:slug/images GameProvider.imageUrl, S3 yes

Filters and views

  • Provider — filter the source catalog list.
  • Game type — slots / house / sportbook.
  • Search — by slug / display name.
  • Drag-and-drop ordering on the right pane (saves on Apply).
  • Hero / Carousel / Category sub-sections — each maps to its own column on Game.

Common workflows

  1. Promote a new launch. Marketing toggles "featured" on the new game, drags it to position 1 of the hero carousel. Click Save → backend updates the column. Player site re-fetches the grid via GET /casino/games/main and renders the new order on the next page load.
  2. Hydrate a new provider catalog. After an integration ships (e.g., new slots from PM8), engineering hits Hydrate. Backend pulls catalog from the provider API, upserts Game rows. Marketing then orders them in the grid.
  3. Revert a bad arrangement. No undo button. Marketing must remember the previous arrangement or check admin-logs.md for the previous POST /admin/casino/games/main request body.
  4. Upload a new banner. Marketing uploads PNG/WebP via the per-game image button. Backend stores in S3 (configured via env), updates Game.imageUrl. CDN cache invalidation is automatic for new URLs (object-versioned).
  5. Push a category change without a deploy. All grid edits are runtime; no code change required.

Edge cases / gotchas

  • No staging on this screen. "Save" goes live. Test on staging environment first.
  • Hydrate is destructive for catalog metadata. It overrides display names from the provider's source-of-truth. Marketing's tweaks to Game.displayName may be reverted. Plan around hydrate windows.
  • No revert/undo. Read admin-logs.md POST /admin/casino/games/main rows to reconstruct prior state.
  • Image upload doesn't validate aspect ratio. Backend accepts any PNG/JPG; UI on the player site assumes 16:9 hero / 1:1 thumbnail. Wrong ratios stretch.
  • Provider logos cached aggressively. CDN can take up to 1 hour to refresh. Override the URL hash to force re-pull.
  • Sportbook is not on this grid. It has its own landing-page management out of scope here.
  • POST /admin/casino/games/main accepts a complex DTO. See apps/api/src/casino/games/dto/ for the schema. Misshapen payloads are 400'd.
  • No per-locale grid. Single grid across en/de (player site uses next-intl, but the catalog is single-language for game slugs).

Sequence — promoting a new game on the home grid

sequenceDiagram
    actor mkt as Marketing
    participant admin-fe
    participant api
    participant s3 as S3
    participant pg as Postgres
    mkt->>admin-fe: open /manage-home-grid
    admin-fe->>api: GET /admin/casino/games/all
    api-->>admin-fe: catalog
    mkt->>admin-fe: drag "newslot-x" to hero position 1, click Save
    admin-fe->>api: POST /admin/casino/games/main { hero: [...], carousel: [...] }
    api->>api: PermissionGuard('casino.games.edit')
    api->>pg: UPDATE Game SET featured*, ordering*, etc.
    api->>pg: INSERT AdminActionLog with full request body
    api-->>admin-fe: 200 OK
    Note over admin-fe,pg: player site picks it up on next /casino/games/main fetch
    mkt->>admin-fe: optionally upload banner
    admin-fe->>api: POST /admin/casino/games/newslot-x/images (multipart)
    api->>s3: PUT object
    api->>pg: UPDATE Game.imageUrl
    api-->>admin-fe: new URL