Расширяемый CRUD: как строить кастомные интерфейсы на базе Admiral

Время прочтения — 10 минут
Содержание
В последнее время к нам в студию часто поступают запросы на разработку систем бронирования. Часто это актуально для небольших B2C-сервисов вроде студий красоты и клиник, где нужно быстро и недорого собрать решение, которое сразу включится в работу, без остановки процессов и долгого онбординга.

С каким кейсом столкнулись мы

Многие системы бронирования не дают достаточно гибкости при работе с записями. Обычно всё устроено так: один мастер оказывает одну услугу одному клиенту за единицу времени – например, парикмахер стрижёт одного человека. Возможности для групповых записей, где участвуют несколько клиентов и мастеров, сильно ограничены.
Чтобы записать пару на спа-процедуру по одному звонку, часто приходится создавать несколько отдельных записей – а это лишние действия для администратора и больше путаницы в системе.
Поэтому компании все чаще обращаются за кастомными решениями. Но у нас как у разработчиков возникали следующие вопросы:
  • Как предложить клиенту решение с разумным бюджетом и быстрым сроком запуска?
  • Необходима ли разработка решения с нуля, если значительная часть функционала представляет собой CRUD’ы (справочники с клиентами, мастерами, местами обслуживания, прайс-листами), а для решения задач пользователя необходима буквально пара кастомных интерфейсов?
  • Можно ли сделать кастомный интерфейс на базе Admiral?

Как Admiral обеспечивает кастомизацию без компромиссов

Большинство админок сегодня можно собирать на готовых фреймворках. И мы тоже раньше использовали популярные генераторы CRUD-интерфейсов, которые за 10 минут обещают готовые интерфейсы. Но сталкивались с тем, что у большинства из них – жестко определенная структура, в которой нельзя реализовать или настроить что-либо по своему усмотрению.
Нам нужно было больше свободы, поэтому мы пришли к созданию собственного фреймворка Admiral. И теперь используем его в 70% наших проектов.
⚡ Это open-source решение, познакомиться с которым можно по ссылке: https://github.com/dev-family/admiral.
Главные преимущества Admiral заключается в возможности:
  • Реализовать уникальные рабочие процессы, которые не вписываются в стандартную модель CRUD;
  • Создавать интерактивные дашборды с графиками, статистикой и другими виджетами, которые отображают важные данные в удобном формате.

Пример API-клиента

В основе Admiral лежит расширяемая архитектура, которая позволяет вам интегрировать собственные компоненты и логику. Это достигается за счёт гибкой системы маршрутизации и компонентов, а не генерации статических файлов с жёстко заданной структурой.
Так у нас получилось создать мощный и универсальный инструмент, который полностью адаптируется под бизнес-процессы наших клиентов.

Добавление кастомных страниц в Admiral

А теперь хочу поделиться собственным опытом кастомизации CRUD-интерфейсов с помощью Admiral на примере одного из недавних проектов. Нашим клиентом была сеть спа-салонов, где администраторам нужно было решать две основные задачи:
  • Создавать и редактировать записи;
  • Отслеживать загруженность мастеров через календарь.
В итоге мы разработали два удобных и эстетичных интерфейса, которые можно адаптировать под разные сценарии. Разберем процесс по шагам, чтобы смогли легко и быстро сделать то же самое для вашего проекта.

Пример API-клиента

Начнем с разработки кастомной страницы создания/редактирования записи с кастомными компонентами.
Создайте React-компонент, который будет отображать вашу кастомную страницу. В нём можно использовать как кастомные компоненты, так и стандартные компоненты Admiral, чтобы сохранить единый стиль и функциональность.
import React, { useCallback } from 'react'
import { Page, Form, Notification } from '@devfamily/admiral'
import { RecordSection, ClientSection, DiscountSection, PaymentSection } from './components'
import api from './api'

export enum RecordPageType {
    CREATE = 'create',
    EDIT = 'edit',
}

const RecordPage = ({ type }: { type: RecordPageType }) => {
    // Function to get default data if this is an edit page
    const fetchInitialData = useCallback(async () => {
        if (type === RecordPageType.EDIT) {
            let data = {}
            try {
            	// Connect API request to get record data
            } catch (err) {
                // Displaying an error notification
                Notification({
                    message: `Error getting record: ${err?.message}`,
                    type: 'error',
                })
            }

            return Promise.resolve({
                data, 
                values: {},
            })
        } else {
            // Return empty data for the new record
            return Promise.resolve({
                data: {},  // Сan use default data
                values: {},
            })
        }
    }, [type, idRecord])

    // Function for submitting form data
    const onSubmit = useCallback(async (values) => {
        // Connect API request to create or edit record
    }, [])

    return (
        <Page title={type === RecordPageType.CREATE ? 'Add record' : `Edit record`}>
            <Form submitData={onSubmit} fetchInitialData={fetchInitialData}>
                {/* Include custom form sections as separate components */}
                <RecordSection />
                <ClientSection />
                <DiscountSection />
                <PaymentSection />
            </Form>
        </Page>
    )
}

export default RecordPage
В результате у вас получиться вот такой интерфейс c использование кастомных компонентов.
Теперь, когда у вас есть основной компонент RecordPage, нужно создать файлы, которые будут использовать его в зависимости от типа операции (создание и редактирование). Эти файлы будут служить точками входа для маршрутизации Admiral.
Далее создайте следующие файлы в директории. Например, /pages/records/:
  • create.tsx – страница для создания новой записи
import React from 'react'
import RecordPage, { RecordPageType } from '../RecordPage'

const CreateRecordPage = () => {
    return <RecordPage type={RecordPageType.CREATE} />
}

export default CreateRecordPage
  • [id].tsx – страница редактирования существующей записи (`[id]` в имени файла позволяет Admiral динамически определять маршрут)
import React from 'react'
import RecordPage, { RecordPageType } from '../RecordPage'

const EditRecordPage = () => {
    return <RecordPage type={RecordPageType.EDIT} />
}

export default EditRecordPage
  • index.tsx – базовая страница CRUD для отображения таблицы записей:
import { CRUD } from '@/src/crud/records'
export default CRUD.IndexPage
В результате у нас получился полноценный CRUD со списком всех актуальных записей и кастомной страницей создания/редактирования записи.

Создание календаря с записями

Получившаяся CRUD-таблица решает задачу хранения и поиска записей. Но для администраторов этого часто недостаточно, так как им важно следить за загрузкой всех мастеров: где есть свободные окна, какие записи перенесли, и какие услуги пересекаются. Поэтому мы добавили в нашу админ-панель кастомную страницу с календарём, где все бронирования сразу отображаются в общей сетке.
В Admiral кастомные страницы подключаются так же, как и CRUD. Создайте отдельный React-компонент в директории /pages/main/, который отображает календарь с интерактивными элементами:
import React, { useEffect, useState } from 'react'
import { CalendarHeader, CalendarTable, CalendarRecordNote } from './ui'
import { TCalendarHeaderData, TGroupedServices, TCalendarHeaderData } from './ts'
import styles from './CalendarPage.module.scss'

const CelendarPage = () => {
    const [isLoading, setIsLoading] = useState<boolean>(false)
    const [timeSlots, setTimeSlots] = useState<string[]>([])
const [services, setServices] = useState<Array<TGroupedServices> | null>(null)
    const [calendarHeaderData, setCalendarHeaderData] = useState<Array<TCalendarHeaderData>>([])
    const [comment, setComment] = useState<string>('')

    const fetchCalendarData = async ({date}: {date: string}) => {
// Connect API request to get data
}

   useEffect(() => {
            fetchCalendarData({
                date: urlState.date || new Date()
            })
    }, [])

    return <Page title="Главная">
            <div className={styles.calendar_page}>
                <CalendarHeader />
                <CalendarRecordNote comment={comment} />
                <CalendarTable
                    isLoading={isLoading}
                    timeSlots={timeSlots}
                    services={services}
                    calendarHeaderData={calendarHeaderData}
                />
            </div>
</Page>

}

export default EditRecordPage
В результате администраторы могут работать с вот таким наглядным календарем.

Интеграция в навигацию

После создания страниц необходимо добавить их в навигацию проекта, чтобы пользователи могли легко переходить на них.
Для этого создайте файл с навигацией. Например, menu.tsx (если его ещё нет), и добавьте туда новые пункты навигации.
import React from 'react'
import { Menu, MenuItemLink } from '@devfamily/admiral'
import { FiUsers, FiUser, FiSettings } from 'react-icons/fi'

const CustomMenu = () => {
    return (
        <Menu>
        	<MenuItemLink icon="FiUsers" name="Main" to="/main" /> // navigate to custom page
     		<MenuItemLink icon="FiUser" name="Records" to="/records" /> // navigate to page with custom components
   <MenuItemLink icon="FiSettings" name="Services" to="/services" />
   <MenuItemLink icon="FiSettings" name="Masters" to="/masters" />
        </Menu>
    )
}

export default CustomMenu

Заключение

Работая с Admiral над разными проектами, мы продолжаем убеждаться в его преимуществах:
  • Подходит для проектов с нестандартной логикой – там, где типовые CRUD-генераторы быстро упираются в структурные ограничения, мы можем продолжать расширять интерфейс;
  • Предоставляет гибкость без лишнего кода – фреймворк обладает открытой архитектурой, в которую можно встроить что угодно: кастомные компоненты, бизнес-логику, календарные интерфейсы.
  • Обеспечивает скорость и масштабируемость – после быстрой сборки базового CRUD, вы сможете и дальше наращивать любые интерфейсы по мере роста проекта.
Благодаря Admiral команда сэкономит десятки часов на разработку и может более глубоко сосредоточиться на ценности проекта для бизнеса.
Остались вопросы по кастомизации админ-панели? Мы здесь, чтобы помочь!
Читайте также