Разработка административной панели на 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 — фреймворк для построения 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,
    });
  },
};

Resource: список и формы

// 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>
  );
}

Кастомизация темы и Layout

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, базовая auth): 3–5 дней
  • Полноценная панель (кастомный dataProvider, сложные формы, загрузка файлов, права, кастомная тема): 2–3 недели
  • С дашбордом, графиками, нестандартными компонентами: ещё 1–2 недели