Розробка адміністративної панелі на React Admin

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка адміністративної панелі на React Admin
Середня
від 1 тижня до 3 місяців
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка адміністративної панелі на React Admin

React Admin — це фреймворк для побудови адміністративних інтерфейсів поверх REST або GraphQL API. Він надає готові компоненти для CRUD-операцій, фільтрації, пагінації, аутентифікації та управління правами. Основна цінність — швидкість розробки: типовий розділ з таблицею та формами створення й редагування займає години, а не дні.

Встановлення та структура

npm install react-admin ra-data-simple-rest
# або для GraphQL:
npm install react-admin ra-data-graphql-simple
src/
  admin/
    App.tsx          — кореневий компонент
    dataProvider.ts  — адаптер API
    authProvider.ts  — аутентифікація
    resources/
      users/
        UserList.tsx
        UserEdit.tsx
        UserCreate.tsx
      products/
        ProductList.tsx
        ProductEdit.tsx

dataProvider

Адаптер між React Admin та вашим API. ra-data-simple-rest працює з REST API де:

  • GET /users — список з заголовком Content-Range
  • GET /users/1 — запис
  • POST /users — створення
  • PUT /users/1 — оновлення
  • DELETE /users/1 — видалення
// dataProvider.ts
import simpleRestProvider from 'ra-data-simple-rest';
import { fetchUtils } from 'react-admin';

const httpClient = (url: string, options: fetchUtils.Options = {}) => {
  const token = localStorage.getItem('auth_token');
  const headers = new Headers(options.headers);
  if (token) headers.set('Authorization', `Bearer ${token}`);
  return fetchUtils.fetchJson(url, { ...options, headers });
};

export const dataProvider = simpleRestProvider(
  import.meta.env.VITE_API_URL,
  httpClient
);

Для нестандартного API — користувацький dataProvider:

import { DataProvider } from 'react-admin';

export const dataProvider: DataProvider = {
  getList: async (resource, params) => {
    const { page, perPage } = params.pagination;
    const { field, order } = params.sort;

    const res = await api.get(`/${resource}`, {
      params: {
        _page: page,
        _limit: perPage,
        _sort: field,
        _order: order.toLowerCase(),
        ...params.filter,
      },
    });

    return {
      data: res.data.items,
      total: res.data.total,
    };
  },

  getOne: async (resource, { id }) => {
    const { data } = await api.get(`/${resource}/${id}`);
    return { data };
  },

  create: async (resource, { data }) => {
    const res = await api.post(`/${resource}`, data);
    return { data: res.data };
  },

  update: async (resource, { id, data }) => {
    const res = await api.put(`/${resource}/${id}`, data);
    return { data: res.data };
  },

  delete: async (resource, { id }) => {
    await api.delete(`/${resource}/${id}`);
    return { data: { id } as any };
  },

  deleteMany: async (resource, { ids }) => {
    await Promise.all(ids.map((id) => api.delete(`/${resource}/${id}`)));
    return { data: ids };
  },

  updateMany: async (resource, { ids, data }) => {
    await Promise.all(ids.map((id) => api.put(`/${resource}/${id}`, data)));
    return { data: ids };
  },

  getManyReference: async (resource, params) => {
    // Для пов'язаних записів (наприклад, коментарі поста)
    const res = await api.get(`/${resource}`, {
      params: { [params.target]: params.id },
    });
    return { data: res.data.items, total: res.data.total };
  },

  getMany: async (resource, { ids }) => {
    const res = await api.get(`/${resource}`, {
      params: { ids: ids.join(',') },
    });
    return { data: res.data };
  },
};

authProvider

import { AuthProvider } from 'react-admin';

export const authProvider: AuthProvider = {
  login: async ({ username, password }) => {
    const res = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    });
    if (!res.ok) throw new Error('Неверний логін або пароль');
    const { token, user } = await res.json();
    localStorage.setItem('auth_token', token);
    localStorage.setItem('auth_user', JSON.stringify(user));
  },

  logout: () => {
    localStorage.removeItem('auth_token');
    localStorage.removeItem('auth_user');
    return Promise.resolve();
  },

  checkAuth: () =>
    localStorage.getItem('auth_token') ? Promise.resolve() : Promise.reject(),

  checkError: ({ status }) => {
    if (status === 401 || status === 403) {
      localStorage.removeItem('auth_token');
      return Promise.reject();
    }
    return Promise.resolve();
  },

  getPermissions: () => {
    const user = JSON.parse(localStorage.getItem('auth_user') ?? '{}');
    return Promise.resolve(user.role ?? 'guest');
  },

  getIdentity: () => {
    const user = JSON.parse(localStorage.getItem('auth_user') ?? '{}');
    return Promise.resolve({
      id: user.id,
      fullName: user.name,
      avatar: user.avatar,
    });
  },
};

Ресурс: список та форми

// resources/users/UserList.tsx
import { List, Datagrid, TextField, EmailField, DateField, EditButton, DeleteButton, Filter, TextInput, SelectInput } from 'react-admin';

const UserFilter = () => (
  <Filter>
    <TextInput label="Пошук" source="q" alwaysOn />
    <SelectInput source="role" choices={[
      { id: 'admin', name: 'Адміністратор' },
      { id: 'editor', name: 'Редактор' },
      { id: 'user', name: 'Користувач' },
    ]} />
  </Filter>
);

export const UserList = () => (
  <List filters={<UserFilter />} sort={{ field: 'createdAt', order: 'DESC' }}>
    <Datagrid rowClick="edit" bulkActionButtons={false}>
      <TextField source="id" />
      <TextField source="name" label="Ім'я" />
      <EmailField source="email" />
      <TextField source="role" label="Роль" />
      <DateField source="createdAt" label="Створено" showTime />
      <EditButton />
      <DeleteButton />
    </Datagrid>
  </List>
);
// resources/users/UserEdit.tsx
import { Edit, SimpleForm, TextInput, SelectInput, required, email } from 'react-admin';

export const UserEdit = () => (
  <Edit>
    <SimpleForm>
      <TextInput source="name" label="Ім'я" validate={required()} />
      <TextInput source="email" label="Email" validate={[required(), email()]} />
      <SelectInput source="role" label="Роль" choices={[
        { id: 'admin', name: 'Адміністратор' },
        { id: 'editor', name: 'Редактор' },
        { id: 'user', name: 'Користувач' },
      ]} />
    </SimpleForm>
  </Edit>
);

App.tsx

import { Admin, Resource } from 'react-admin';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';
import { UserList, UserEdit, UserCreate } from './resources/users';
import { ProductList, ProductEdit, ProductCreate } from './resources/products';
import PeopleIcon from '@mui/icons-material/People';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';

export function AdminApp() {
  return (
    <Admin
      dataProvider={dataProvider}
      authProvider={authProvider}
      basename="/admin"
    >
      <Resource
        name="users"
        list={UserList}
        edit={UserEdit}
        create={UserCreate}
        icon={PeopleIcon}
        options={{ label: 'Користувачі' }}
      />
      <Resource
        name="products"
        list={ProductList}
        edit={ProductEdit}
        create={ProductCreate}
        icon={ShoppingCartIcon}
        options={{ label: 'Товари' }}
      />
    </Admin>
  );
}

Кастомізація теми та макета

import { defaultTheme, Layout, AppBar, Menu } from 'react-admin';

const myTheme = {
  ...defaultTheme,
  palette: {
    primary: { main: '#1976d2' },
    secondary: { main: '#dc004e' },
    background: { default: '#f5f5f5' },
  },
  components: {
    ...defaultTheme.components,
    RaDatagrid: {
      styleOverrides: {
        root: {
          '& .RaDatagrid-headerCell': {
            backgroundColor: '#f0f0f0',
          },
        },
      },
    },
  },
};

const CustomLayout = (props: any) => (
  <Layout {...props} appBar={AppBar} menu={Menu} />
);

<Admin theme={myTheme} layout={CustomLayout} />

Управління правами доступу

// Показуємо кнопки тільки потрібним ролям
import { usePermissions, EditButton, DeleteButton } from 'react-admin';

const UserActions = () => {
  const { permissions } = usePermissions();
  return (
    <>
      {['admin', 'editor'].includes(permissions) && <EditButton />}
      {permissions === 'admin' && <DeleteButton />}
    </>
  );
};

Строки виконання

  • MVP (3–5 ресурсів, стандартний CRUD, базова автентифікація): 3–5 днів
  • Повноцінна панель (користувацький dataProvider, складні форми, завантаження файлів, права, користувацька тема): 2–3 тижні
  • З дашбордом, графіками, користувацькими компонентами: ще 1–2 тижні