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.ts — useUpdateHomeGridMutation (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¶
- 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/mainand renders the new order on the next page load. - 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
Gamerows. Marketing then orders them in the grid. - 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/mainrequest body. - 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). - 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.displayNamemay be reverted. Plan around hydrate windows. - No revert/undo. Read admin-logs.md
POST /admin/casino/games/mainrows 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/mainaccepts a complex DTO. Seeapps/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).
Cross-links¶
- Player-side grid endpoint:
apps/api/src/casino/games/controller/games.controller.ts(/casino/games/main) - Game catalog & RTP: game-management.md
- Audit: admin-logs.md
- Adding a new game (provider integration):
recipes/add-game-provider-integration.md,recipes/add-game.md - Customer comms for a launch:
handover/customer-comms/
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