diff --git a/.cursor/rules/kpi-hybrid.mdc b/.cursor/rules/kpi-hybrid.mdc
index cd111f8..2bf203c 100644
--- a/.cursor/rules/kpi-hybrid.mdc
+++ b/.cursor/rules/kpi-hybrid.mdc
@@ -1,11 +1,11 @@
---
-description: Только hybrid KPI — KpiStatGrid / row tile DNA (stats-12). Запрет SectionCards и hand-roll.
+description: Только hybrid KPI — KpiStatGrid / row tile DNA (stats-12 + IconTile). Запрет SectionCards и hand-roll.
alwaysApply: true
---
-# KPI hybrid — только kit (stats-12 DNA)
+# KPI hybrid — только kit (stats-12 + IconTile)
-Preview: [stats-12](https://reui.io/preview/base/stats-12). SoT DNA = EvoBGP. Markup в проекте: `apps/web/src/components/reui-kit/kpi-stat-grid.tsx`.
+Preview: [stats-12](https://reui.io/preview/base/stats-12). Primitive: [icon-tile](https://reui.io/docs/components/base/icon-tile). SoT DNA = EvoBGP. Markup: `apps/web/src/components/reui-kit/kpi-stat-grid.tsx`.
Связанные: [`reui-mcp.mdc`](reui-mcp.mdc), [`frontend-ui-patterns.mdc`](frontend-ui-patterns.mdc).
@@ -14,16 +14,17 @@ Preview: [stats-12](https://reui.io/preview/base/stats-12). SoT DNA = EvoBGP. Ma
| Зона | Компонент / DNA |
|------|-----------------|
| KPI-полосы / dashboard metrics | только `reui-kit/KpiStatGrid` (через `OpsDashboard` / `DetailPanel.Metrics` при наличии) |
-| Markup | horizontal compact hybrid: icon left `Item` `size-10.5` `bg-muted` + `border-background` + shadow + `ItemMedia` + label/Badge + value ± `variant` |
-| Row icon tiles (data-grid) | та же DNA — semantic `text-*` на `bg-muted` |
-| Quick Actions | только `reui-kit/QuickActionGrid` (sibling hybrid DNA) |
+| Markup | horizontal compact hybrid: icon left `IconTile` `variant="elevated"` `className="size-10.5"` + label/Badge + value ± `variant` |
+| Row icon tiles (data-grid) | та же DNA — semantic `text-*` на IconTile elevated |
+| Quick Actions | только `reui-kit/QuickActionGrid` (sibling hybrid DNA, IconTile elevated) |
-Импорты UI: `@authportal/ui/components/*`.
+Импорты UI: `@authportal/ui/components/*`. IconTile: `@/components/reui/icon-tile`.
## NEVER
- SectionCards / vertical-only KPI / hand-roll Frame/Card KPI
-- Другой size / radius / solid brand fill вместо `bg-muted`
+- `Item` `size-10.5` `bg-muted` hybrid вместо IconTile
+- Другой size / radius / solid brand fill вместо `elevated`
- `card-35` как замена stats-12 hybrid KPI
- Копипаст ReUI block в route — adapt через `reui-kit/`
-- Голый lucide `size-4` в name-cell без hybrid tile
+- Голый lucide `size-4` в name-cell без IconTile
diff --git a/.cursor/rules/reui-mcp.mdc b/.cursor/rules/reui-mcp.mdc
index 6845166..11703fc 100644
--- a/.cursor/rules/reui-mcp.mdc
+++ b/.cursor/rules/reui-mcp.mdc
@@ -75,9 +75,9 @@ alwaysApply: true
## Установленные ReUI (apps/web)
-**Components:** `frame`, `data-grid/*`, `filters`, `kanban`, `badge`, `alert`, `autocomplete`, `number-field`, `date-selector`, `color-picker`, `timeline`, `rating`, `phone-input`, `icon-stack`
+**Components:** `frame`, `data-grid/*`, `filters`, `badge`, `alert`, `timeline`, `icon-tile`, `icon-stack`
-**Kit:** `ResourcePage`, `KpiStatGrid`, `QuickActionGrid`, `OpsDashboard`, `SettingsShell`, `DetailPanel`, `SettingsShell`
+**Kit:** `KpiStatGrid`, `QuickActionGrid`, `AdminUsersGrid` (lists = PageShell + Frame + DataGrid; `ResourcePage` не list-kit)
**Blocks (reference):** `stats-12`, `card-35`, `auth-13`, `app-shell-12`, `settings-16`, `settings-8`, `empty-state-12`, `form-7`, `data-grid-filtering-2`, `dashboard-1`, …
diff --git a/AGENTS.md b/AGENTS.md
index 044f950..60d5574 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -22,8 +22,8 @@
- Skill: `.claude/skills/reui` (`668fb463eb`, 20 free components)
- Rules: `.cursor/rules/reui-mcp.mdc`, `frontend-shadcn.mdc`, `frontend-ui-patterns.mdc`, `shadcn-mcp.mdc`, `shadcn-ui-production.mdc`
- Primary MCP: `user-reui`; primitives: `plugin-shadcn-shadcn`
-- Chrome: NavUser in sidebar footer (theme segmented); no ModeToggle in header
-- Kit: `KpiStatGrid`, `QuickActionGrid` (SoT markup = EvoBGP); lists = PageShell + Frame + DataGrid
+- Chrome: header AppsMenu → SystemMonitorPopover; NavUser in sidebar footer (theme segmented); no ModeToggle in header
+- Kit: `KpiStatGrid`, `QuickActionGrid` (SoT markup = EvoBGP + IconTile); lists = `AdminUsersGrid` / PageShell + Frame + DataGrid
## License
diff --git a/apps/web/src/components/empty-state.tsx b/apps/web/src/components/empty-state.tsx
new file mode 100644
index 0000000..a311d5b
--- /dev/null
+++ b/apps/web/src/components/empty-state.tsx
@@ -0,0 +1,82 @@
+/**
+ * ReUI Empty + IconStack — Frame-friendly (empty-state-14 / empty-state-12).
+ * Preview: https://reui.io/preview/base/empty-state-14 · https://reui.io/preview/base/empty-state-12
+ * Docs: https://reui.io/docs/components/base/icon-stack
+ */
+import { InboxIcon, type LucideIcon } from 'lucide-react'
+import { IconStack } from '@/components/reui/icon-stack'
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from '@authportal/ui/components/empty'
+import { cn } from '@authportal/ui/lib/utils'
+import type { ReactNode } from 'react'
+
+interface EmptyStateProps {
+ icon?: LucideIcon
+ title: string
+ description?: string
+ action?: ReactNode
+ className?: string
+ stackedIcon?: boolean
+ centered?: boolean
+}
+
+export function EmptyState({
+ icon: Icon = InboxIcon,
+ title,
+ description,
+ action,
+ className,
+ stackedIcon = true,
+ centered = true,
+}: EmptyStateProps) {
+ const body = (
+
@@ -62,9 +61,40 @@ function QuickActionBody({ action }: { action: QuickActionItem }) {
)
}
+function QuickActionHit({ action }: { action: QuickActionItem }) {
+ const label = `${action.title}: ${action.description}`
+ const body =
Нет доступных приложений
@@ -84,36 +99,25 @@ function AppsPage() { ) : ( -