feat(api, web): enhance list entry management and UI consistency
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m57s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- Updated API to support structured list entry inputs, allowing for nested list references.
- Improved error handling for list operations to prevent cyclic references.
- Refactored UI components to ensure consistent labeling and navigation for IP lists.
- Enhanced list detail and catalog pages with better filtering and entry management features.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-20 23:25:05 +07:00
co-authored by Cursor
parent 3f7672ab7c
commit c505ab82e8
13 changed files with 952 additions and 518 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ const overviewNav = [
const opsNav = [
{ to: '/agents', label: 'Агенты', icon: ServerIcon, exact: false },
{ to: '/lists', label: 'Списки IP', icon: ListIcon, exact: false },
{ to: '/lists', label: 'Списки', icon: ListIcon, exact: false },
{ to: '/rules', label: 'Наборы правил', icon: ShieldIcon, exact: false },
{ to: '/stats', label: 'Статистика', icon: BarChart3Icon, exact: false },
] as const
@@ -21,7 +21,7 @@ export interface RouteBreadcrumbLoaderData {
const routeTitles: Record<string, string> = {
'/': 'Панель управления',
'/agents': 'Агенты',
'/lists': 'Списки IP',
'/lists': 'Списки',
'/rules': 'Наборы правил',
'/stats': 'Статистика',
'/settings': 'Настройки',
@@ -77,6 +77,7 @@ export interface ResourcePageProps<T extends object> {
}) => ReactNode
toolbarExtra?: ReactNode
hideHeader?: boolean
onRowClick?: (row: T) => void
}
function ResourcePageSkeleton() {
@@ -124,6 +125,7 @@ export function ResourcePage<T extends object>({
selectionToolbar,
toolbarExtra,
hideHeader = false,
onRowClick,
}: ResourcePageProps<T>) {
const [internalTab, setInternalTab] = useState(tabs?.[0]?.id ?? 'all')
const activeTab = controlledTab ?? internalTab
@@ -287,6 +289,7 @@ export function ResourcePage<T extends object>({
recordCount={filteredData.length}
emptyMessage={emptyMessage}
tableLayout={{ dense: true }}
onRowClick={onRowClick}
>
<Frame dense variant="default" spacing="sm" className="w-full">
{!hideHeader ? (
+2
View File
@@ -33,6 +33,7 @@ const STATUS_VARIANT: Record<string, BadgeVariant> = {
ip: 'info-light',
cidr: 'secondary',
hostname: 'warning-light',
list: 'info-light',
unknown: 'outline',
}
@@ -75,6 +76,7 @@ const STATUS_LABELS: Record<string, string> = {
ip: 'IP',
cidr: 'CIDR',
hostname: 'Домен',
list: 'Список',
unknown: 'Неизвестно',
}