md-platform

homepage-rows.md
View raw Back to list

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

  1. Frontend calls GET /client/v1/homepage/rows on page load
  2. The API returns an ordered list of active rows, each with a rowType that tells the frontend which component to render
  3. Frontend renders each row in the returned order, using the rowType to 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