Homepage Rows
The homepage layout is now driven by the API. Instead of hardcoding which rows to show and in what order, the frontend fetches a configuration from the backend and renders rows dynamically.
How it works
- Frontend calls
GET /client/v1/homepage/rowson page load - The API returns an ordered list of active rows, each with a
rowTypethat tells the frontend which component to render - Frontend renders each row in the returned order, using the
rowTypeto pick the right component
API
GET /client/v1/homepage/rows
Returns active homepage rows ordered by displayOrder.
Response:
[
{
"id": "aBcDeFgHiJ",
"rowType": 1,
"title": "Banner",
"displayOrder": 1,
"isActive": true
},
{
"id": "kLmNoPqRsT",
"rowType": 3,
"title": "Slow TV",
"displayOrder": 2,
"isActive": true
},
{
"id": "uVwXyZ1234",
"rowType": 9,
"title": "Filma",
"displayOrder": 3,
"isActive": true
}
]
HomepageRowType enum
public enum HomepageRowType
{
Banner = 1,
LiveChannels = 2,
SlowTv = 3,
LiveEvents = 4,
PreliveEvents = 5,
ContinueWatching = 6,
Shorts = 7,
Videos = 8,
Movies = 9,
Series = 10,
Channels = 11
}
| Value | Name | Component |
|---|---|---|
| 1 | Banner | HomeBanner |
| 2 | LiveChannels | HomeLiveChannels |
| 3 | SlowTv | HomeSlowTv |
| 4 | LiveEvents | HomeLiveEvents |
| 5 | PreliveEvents | HomePreliveEvents |
| 6 | ContinueWatching | HomeContinueWatching |
| 7 | Shorts | HomeShorts |
| 8 | Videos | HomeVideos |
| 9 | Movies | HomeMovies |
| 10 | Series | HomeShows |
| 11 | Channels | HomeChannels |
Frontend implementation
1. Create a component map
Map each rowType to the corresponding component:
const COMPONENT_MAP: Record<number, React.ComponentType<RowProps>> = {
1: HomeBanner,
2: HomeLiveChannels,
3: HomeSlowTv,
4: HomeLiveEvents,
5: HomePreliveEvents,
6: HomeContinueWatching,
7: HomeShorts,
8: HomeVideos,
9: HomeMovies,
10: HomeShows,
11: HomeChannels,
};
2. Fetch rows and render dynamically
function HomePage() {
const { data: rows } = useQuery({
queryKey: ["homepage-rows"],
queryFn: () => api.get("/client/v1/homepage/rows"),
});
if (!rows) return <Loading />;
return (
<>
{rows.map((row) => {
const Component = COMPONENT_MAP[row.rowType];
if (!Component) return null;
return <Component key={row.id} row={row} />;
})}
</>
);
}
Each component keeps its existing data-fetching logic but should check row.filterParams for additional filters (e.g., channelId). The order and visibility of rows is now controlled by the API instead of being hardcoded.
3. Apply filterParams
filterParams is a nullable JSON string with optional filters. Components should parse it and pass the values to their API calls:
// Example filterParams: {"channelId": "2D5jWBl6MZ"}
function HomeVideos({ row }: RowProps) {
const filters = row.filterParams ? JSON.parse(row.filterParams) : {};
const { data } = useQuery({
queryKey: ["videos", filters],
queryFn: () => api.get("/videos/vod", { params: { pageSize: 8, ...filters } }),
});
// ...
}
This allows the admin to create multiple rows of the same type with different filters. For example, two "Videos" rows — one for all videos and one filtered to a specific channel.
What changes from the current implementation
| Before | After |
|---|---|
| Row order is hardcoded in frontend | Row order comes from API |
| Adding/removing a row requires a frontend deploy | Admin toggles isActive or changes displayOrder |
| Row titles are hardcoded | Titles come from API via row.title |
Admin endpoints
These are used by the admin panel to manage rows:
| Method | Endpoint | Description |
|---|---|---|
| GET | /admin/v1/homepage-rows |
List all rows |
| GET | /admin/v1/homepage-rows/{id} |
Get a single row |
| POST | /admin/v1/homepage-rows |
Create a row |
| PUT | /admin/v1/homepage-rows/{id} |
Update a row |
| DELETE | /admin/v1/homepage-rows/{id} |
Delete a row |
Create/Update payload
{
"rowType": 8,
"title": "Gjirafa Studios",
"displayOrder": 3,
"isActive": true,
"filterParams": "{\"channelId\":\"2D5jWBl6MZ\"}"
}
filterParams is optional. When null, the row has no extra filters. Supported keys depend on the row type (e.g., channelId for Videos).
Notes
- The response is cached on the backend. Changes made via admin are reflected after cache invalidation (handled automatically via Debezium CDC).
- Unknown
rowTypevalues should be skipped (future-proofing for new row types). rowTypeis validated on create/update — only values from the enum (1-11) are accepted.