Интерфейс можно расширить точечно или заменить страницу целиком. В первом случае генератор продолжает строить стандартный список и форму, а custom-код добавляет нужные элементы. Во втором ваша команда поддерживает страницу и её поведение.
ENTITY_NAME в пути — каталог сущности, например product. EntityName в имени экспорта — её PascalCase-имя, например Product. Откройте уже созданные файлы своей сущности и используйте их реальные имена.
Файлы расширений
| Файл | Что менять |
|---|---|
admin/src/features/ENTITY_NAME/custom/ui-extension.tsx |
Ячейки, дополнительные колонки, действия и элементы стандартной формы |
admin/src/features/ENTITY_NAME/custom/extension.tsx |
Заголовки, действия, преобразование данных и полные PageOverride |
admin/src/features/ENTITY_NAME/generated/ui-extension.tsx |
Контракт расширений для чтения; файл обновляет генератор |
admin/custom/vite.ts |
Поддерживаемые дополнительные настройки Vite |
Изменить список
В useEntityNameListUI(context) доступны текущие записи, состояние загрузки, ошибка и сгенерированные колонки. Верните только нужные дополнения:
| Свойство | Назначение |
|---|---|
columns |
Новые колонки с уникальными идентификаторами |
cells |
Своё отображение существующих ячеек по имени поля |
rowActions |
Действия со строкой: ID, подпись, проверка isAllowed, обработчик и при необходимости disabled |
status |
Дополнительное состояние списка |
Новая custom-колонка не получает SQL-фильтрацию и сортировку автоматически. Не используйте имена существующих колонок и служебные __actions, __customActions, __related. Если нужны данные из другого API, запрашивайте их ограниченной партией для ID текущей страницы и обрабатывайте загрузку, ошибку и смену страницы.
Изменить поле формы
useEntityNameFormUI() возвращает расширения формы. textFields подходит для поддерживаемых обычных видимых редактируемых string/text; imageFields — для изображений, когда они есть в модели; wrappers — для обёрток отдельных полей.
Собственный текстовый контрол получает inputProps. Сохраните их, включая name, required, disabled и значение: стандартная форма собирает FormData. Обёртка должна вывести defaultControl ровно один раз. Для сложного состояния используйте предусмотренный onDirtyChange, чтобы предупреждение о несохранённых изменениях оставалось корректным.
У локализованного поля name обозначает поле модели, а inputName/inputProps.name — реальное имя HTML-ввода для выбранного языка. Не собирайте его из подписи вроде name-ru. Учитывайте readonly/disabled и серверные права, даже если в демо можно печатать в поле без возможности сохранить.
Полностью заменить страницу
В custom/extension.tsx найдите существующий экспорт EntityNameListPageOverride или EntityNameEditPageOverride. Вместо null укажите свой React-компонент:
function MyListPage() {
return <section>Содержимое вашей страницы</section>
}
export const EntityNameListPageOverride: ComponentType | null = MyListPage
Замените существующее объявление, не добавляйте второе. Сгенерированный маршрут выберет override вместо стандартной страницы — обычная таблица снизу не добавляется автоматически. Не вставляйте её в свой компонент, если она не нужна. У virtual-страниц предусмотрены EntityNameListPageOverride и EntityNameDetailsPageOverride.
В полной замене вы реализуете запросы, сохранение, состояние ожидания, ошибки, пагинацию и доступность. Для одной изменённой ячейки используйте cells, а не полный override. Custom-интерфейс не обходит серверные права.
После изменения соберите admin через npm run build из admin/ и проверьте свой экран с подходящими правами пользователя. Контракты и порядок генерации описаны в рабочем цикле.