diff --git a/.agents/skills/shadcn-svelte/SKILL.md b/.agents/skills/shadcn-svelte/SKILL.md new file mode 100644 index 0000000..fe98fb3 --- /dev/null +++ b/.agents/skills/shadcn-svelte/SKILL.md @@ -0,0 +1,34 @@ +# shadcn-svelte в EvoBGP monorepo + +Использовать при добавлении примитивов, блоков, правке темы в `apps/web` / `packages/ui`. + +## Структура + +- Примитивы: `packages/ui/src/components/` → `@evobgp/ui/components/*` +- Паттерны приложения: `apps/web/src/lib/components/patterns/` +- CLI всегда из `apps/web/` + +## Workflow + +1. Проверить, есть ли компонент в `packages/ui/src/components/` +2. `cd apps/web && pnpm dlx shadcn-svelte@latest add -y -o` +3. Если файлы в `apps/web/@evobgp/ui/` — перенести в `packages/ui/src/components/` +4. Импорты shadcn в app: `@evobgp/ui/components//index.js` +5. `pnpm --filter @evobgp/web check && pnpm --filter @evobgp/web lint` + +## Тема + +- `packages/ui/src/styles/globals.css` с `@source` +- Импорт в `apps/web/src/routes/layout.css`: `@import '@evobgp/ui/styles/globals.css';` + +## Не использовать + +- React shadcn/ui примеры без адаптации под Svelte 5 +- ReUI (`@reui/*`) — только React +- Legacy `$lib/components/ui/` re-exports + +## Документация + +- https://shadcn-svelte.com/docs +- https://shadcn-svelte.com/llms.txt +- Monorepo: `.cursor/rules/frontend-monorepo.mdc` diff --git a/.codegraph/daemon.pid b/.codegraph/daemon.pid index 6dabc54..37fb2a2 100644 --- a/.codegraph/daemon.pid +++ b/.codegraph/daemon.pid @@ -1,6 +1,6 @@ { - "pid": 44608, + "pid": 46304, "version": "0.9.9", "socketPath": "\\\\.\\pipe\\codegraph-97b92efdcc5351da", - "startedAt": 1781240018712 + "startedAt": 1782824332519 } diff --git a/.cursor/rules/frontend-monorepo.mdc b/.cursor/rules/frontend-monorepo.mdc new file mode 100644 index 0000000..5b513f3 --- /dev/null +++ b/.cursor/rules/frontend-monorepo.mdc @@ -0,0 +1,75 @@ +--- +description: shadcn-svelte Monorepo — apps/web + packages/ui + packages/shared, CLI workflow +globs: apps/web/**/*,packages/ui/**/*,packages/shared/**/* +alwaysApply: false +--- + +# Frontend Monorepo (shadcn-svelte) + +Структура по образцу vps-tracker, стек — **SvelteKit 2 + Svelte 5 + shadcn-svelte** (не React/ReUI). + +## Layout + +``` +apps/web/ # SvelteKit (routes, queries, domain components) +packages/ui/ # @evobgp/ui — shadcn-svelte primitives (только CLI output) +packages/shared/ # @evobgp/shared — Zod contracts, API types +``` + +Go API в корне репозитория (`internal/`, `cmd/`) без изменений. + +## Два components.json + +| Файл | Назначение | +|------|------------| +| `apps/web/components.json` | App aliases; `ui` → `@evobgp/ui/components` | +| `packages/ui/components.json` | UI package aliases | + +**Синхронизировать:** `style`, `iconLibrary`, `baseColor` в обоих файлах. + +## CLI — только из apps/web + +```powershell +cd apps/web +pnpm dlx shadcn-svelte@latest add button -y -o +pnpm dlx shadcn-svelte@latest add sidebar -y -o +pnpm dlx shadcn-svelte@latest add breadcrumb -y -o +``` + +Перед обновлением существующих компонентов: `pnpm dlx shadcn-svelte@latest add button --dry-run` + +## Куда CLI кладёт файлы + +| Команда | Куда | +|---------|------| +| `add button` | `packages/ui/src/components/button/` | +| `add sidebar` | `packages/ui/src/components/sidebar/` | + +Если CLI кладёт в `apps/web/@evobgp/ui/` — перенести в `packages/ui/src/components/`. + +## Импорты + +```svelte +import { Button } from '@evobgp/ui/components/button/index.js'; +import { cn } from '@evobgp/ui/lib/utils'; +import '@evobgp/ui/styles/globals.css'; // только в routes/layout.css +import type { ModuleRow } from '@evobgp/shared/types/api.js'; +import { moduleCreateSchema } from '@evobgp/shared/contracts/modules.js'; +``` + +| Запрещено | Разрешено | +|-----------|-----------| +| `$lib/ui/core/*` | `@evobgp/ui/components/*` | +| `apps/web/src/lib/components/ui/` (legacy re-export) | `packages/ui/src/components/` | +| Ручное редактирование темы вне CLI | shadcn-svelte theming docs | + +## globals.css + +`packages/ui/src/styles/globals.css` с `@source` на `packages/ui` и `apps/web/src`. + +## Проверка после правок + +```powershell +pnpm --filter @evobgp/web check +pnpm --filter @evobgp/web lint +``` diff --git a/.cursor/rules/frontend-ui-patterns.mdc b/.cursor/rules/frontend-ui-patterns.mdc new file mode 100644 index 0000000..c439b47 --- /dev/null +++ b/.cursor/rules/frontend-ui-patterns.mdc @@ -0,0 +1,65 @@ +--- +description: UI-паттерны apps/web — shared components, spacing, матрица (Svelte, без ReUI) +globs: apps/web/**/* +alwaysApply: false +--- + +# Frontend UI Patterns (Svelte) + +Эталон UX: vps-tracker (`PageShell`, `QueryState`, `SectionCards`, `DataTableCard`). ReUI **не используется** — Data Table = shadcn-svelte + `AppDataTable` / `DataTableCard`. + +## Иерархия + +``` +@evobgp/ui/components/* ← shadcn-svelte CLI (packages/ui) +apps/web/src/lib/components/ ← shared + domain + layout + page-shell.svelte + query-state.svelte + section-cards.svelte + data-table-card.svelte + status-badge.svelte + form-sheet.svelte + crud-list-page.svelte + list-filters-bar.svelte + patterns/ ← AppDataTable, FormField, ConfirmDialog, EmptyState + layout/app-shell.svelte + domain-*/ ← modules, network, operations… +``` + +## Матрица + +| Элемент | Компонент | Primitive | +|---------|-----------|-----------| +| Page wrapper | `PageShell` | — | +| Page title | `PageHeader` (`ui/app/page-header`) | — | +| Stat metrics | `SectionCards` | `Card` | +| Data list | `DataTableCard` + `AppDataTable` | `Table` | +| Filters | `ListFiltersBar` | `Select`, `Badge` | +| Empty | `EmptyState` | — | +| Loading / Error | `QueryState` | `Skeleton`, `Alert` | +| Status | `StatusBadge` | `Badge` | +| Create/Edit form | `FormSheet` / Dialog | `Sheet`, `Field` | +| Delete confirm | `ConfirmDialog` | `AlertDialog` | +| Nav | `AppShell` | `Sidebar` | +| Breadcrumbs | `AppShell` header | `Breadcrumb` | + +## Spacing + +- `flex` + `gap-*`, не `space-y-*` +- Page: `gap-4 md:gap-6`, padding `p-4 md:p-6` +- Loading страницы → `Skeleton`, не page-level `Spinner` +- Max 1 primary CTA на экран + +## Docs + +- shadcn-svelte: https://shadcn-svelte.com/docs +- llms.txt: https://shadcn-svelte.com/llms.txt +- Data layer: `@tanstack/svelte-query` в `apps/web/src/lib/queries/` + +## Overlay + +| Сценарий | Компонент | +|----------|-----------| +| Create/edit | `Sheet` / `FormSheet` | +| Destructive | `ConfirmDialog` | +| Preview | `Dialog` | diff --git a/.cursor/rules/web-shadcn.mdc b/.cursor/rules/web-shadcn.mdc index f8c7ddb..f2c2fdf 100644 --- a/.cursor/rules/web-shadcn.mdc +++ b/.cursor/rules/web-shadcn.mdc @@ -1,126 +1,55 @@ --- -description: EvoBGP WebUI — shadcn-svelte, Svelte 5, слои ui/core|patterns|app +description: EvoBGP WebUI — shadcn-svelte monorepo, Svelte 5, @evobgp/ui globs: - - web/** + - apps/web/** + - packages/ui/** + - packages/shared/** alwaysApply: false --- -# Web UI — shadcn-svelte +# Web UI — shadcn-svelte (monorepo) -**Источник правды:** https://shadcn-svelte.com/docs (не React shadcn/ui, не Legacy Docs). +**Источник правды:** https://shadcn-svelte.com/docs -Общие правила Go/API: `.cursor/rules/engineering.mdc`. Локальная карта: `web/README.md`. +Monorepo: [`frontend-monorepo.mdc`](frontend-monorepo.mdc), паттерны: [`frontend-ui-patterns.mdc`](frontend-ui-patterns.mdc). Skill: [`.agents/skills/shadcn-svelte/SKILL.md`](.agents/skills/shadcn-svelte/SKILL.md). -## Слои UI +## Слои | Слой | Путь | Назначение | |------|------|------------| -| Примитивы | `src/lib/ui/core/` | shadcn-svelte (только CLI `add`) | -| Паттерны | `src/lib/ui/patterns/` | FormField, AppDataTable, ConfirmDialog, EmptyState | -| App chrome | `src/lib/ui/app/` | Layout, PageHeader, `notify` | -| Legacy | `src/lib/components/ui/` | Re-export; **не добавлять новые файлы** | +| Примитивы | `packages/ui/src/components/` | shadcn-svelte (только CLI) | +| Паттерны | `apps/web/src/lib/components/patterns/` | AppDataTable, FormField, ConfirmDialog | +| Shared UI | `apps/web/src/lib/components/` | PageShell, QueryState, SectionCards | +| App chrome | `apps/web/src/lib/ui/app/` | PageHeader, toast, nav | +| Контракты | `packages/shared/` | types + Zod | -Тема: `src/routes/layout.css`, `src/lib/ui/app/tokens.md`. CLI из `web/`: `npx shadcn-svelte@latest add -y -o`. +Тема: `packages/ui/src/styles/globals.css` → импорт в `apps/web/src/routes/layout.css`. ---- +CLI из `apps/web/`: -## Правила - -**WEB-01** | MUST | Перед новым UI — проверить https://shadcn-svelte.com/docs/components; использовать компонент, не HTML+CSS с нуля. -*Rationale:* Open Code + единый дизайн. -*Проверка:* review; нет голых ` + + {/if} + + diff --git a/apps/web/src/lib/components/layout/app-shell.svelte b/apps/web/src/lib/components/layout/app-shell.svelte new file mode 100644 index 0000000..b6022e4 --- /dev/null +++ b/apps/web/src/lib/components/layout/app-shell.svelte @@ -0,0 +1,116 @@ + + + + + +
+
+ EvoBGP +

Панель управления

+
+ +
+
+ + + Операции + + + {#each mainNav as item (item.href)} + {@const Icon = item.icon} + + + {#snippet child({ props })} + + + {item.label} + + {/snippet} + + + {/each} + + + + + + + {#each bottomNav as item (item.href)} + {@const Icon = item.icon} + + + {#snippet child({ props })} + + + {item.label} + + {/snippet} + + + {/each} + + + + +
+ +
+
+ + EvoBGP +
+
+
+ {@render children()} +
+
+
diff --git a/apps/web/src/lib/components/list-filters-bar.svelte b/apps/web/src/lib/components/list-filters-bar.svelte new file mode 100644 index 0000000..454e608 --- /dev/null +++ b/apps/web/src/lib/components/list-filters-bar.svelte @@ -0,0 +1,45 @@ + + +
+ {#if children} + {@render children()} + {/if} + {#each chips as chip (chip.id)} + + {chip.label} + {#if onRemoveChip} + + {/if} + + {/each} + {#if chips.length > 0 && onClear} + + {/if} +
diff --git a/web/src/lib/components/modules/ModuleAsEntriesCard.svelte b/apps/web/src/lib/components/modules/ModuleAsEntriesCard.svelte similarity index 96% rename from web/src/lib/components/modules/ModuleAsEntriesCard.svelte rename to apps/web/src/lib/components/modules/ModuleAsEntriesCard.svelte index 8f236e9..fb3bd5a 100644 --- a/web/src/lib/components/modules/ModuleAsEntriesCard.svelte +++ b/apps/web/src/lib/components/modules/ModuleAsEntriesCard.svelte @@ -8,17 +8,17 @@ supportsCsvIO } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Checkbox } from '$lib/ui/core/checkbox/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Checkbox } from '@evobgp/ui/components/checkbox/index.js'; import { Card, CardContent, CardHeader, CardTitle, CardDescription - } from '$lib/ui/core/card/index.js'; - import AppDataTable from '$lib/ui/patterns/data-table/app-data-table.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import AppDataTable from '$lib/components/patterns/data-table/app-data-table.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import ModuleAsEntryDialog from '$lib/components/modules/ModuleAsEntryDialog.svelte'; import Plus from '@lucide/svelte/icons/plus'; diff --git a/web/src/lib/components/modules/ModuleAsEntryDialog.svelte b/apps/web/src/lib/components/modules/ModuleAsEntryDialog.svelte similarity index 91% rename from web/src/lib/components/modules/ModuleAsEntryDialog.svelte rename to apps/web/src/lib/components/modules/ModuleAsEntryDialog.svelte index 5b1e55a..3a1dc6f 100644 --- a/web/src/lib/components/modules/ModuleAsEntryDialog.svelte +++ b/apps/web/src/lib/components/modules/ModuleAsEntryDialog.svelte @@ -8,9 +8,9 @@ NONE_OPTION, nullableSelectValue } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; import { Dialog, DialogContent, @@ -18,8 +18,13 @@ DialogTitle, DialogFooter, DialogDescription - } from '$lib/ui/core/dialog/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; + } from '@evobgp/ui/components/dialog/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; type Props = { diff --git a/web/src/lib/components/modules/ModuleCdnSourceDialog.svelte b/apps/web/src/lib/components/modules/ModuleCdnSourceDialog.svelte similarity index 95% rename from web/src/lib/components/modules/ModuleCdnSourceDialog.svelte rename to apps/web/src/lib/components/modules/ModuleCdnSourceDialog.svelte index bc4011f..a7da05c 100644 --- a/web/src/lib/components/modules/ModuleCdnSourceDialog.svelte +++ b/apps/web/src/lib/components/modules/ModuleCdnSourceDialog.svelte @@ -14,17 +14,22 @@ NONE_OPTION, nullableSelectValue } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter - } from '$lib/ui/core/dialog/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; + } from '@evobgp/ui/components/dialog/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; type Props = { diff --git a/web/src/lib/components/modules/ModuleCdnSourcesCard.svelte b/apps/web/src/lib/components/modules/ModuleCdnSourcesCard.svelte similarity index 94% rename from web/src/lib/components/modules/ModuleCdnSourcesCard.svelte rename to apps/web/src/lib/components/modules/ModuleCdnSourcesCard.svelte index 5664935..361234b 100644 --- a/web/src/lib/components/modules/ModuleCdnSourcesCard.svelte +++ b/apps/web/src/lib/components/modules/ModuleCdnSourcesCard.svelte @@ -6,18 +6,18 @@ communityLabel, normalizeCdnSourceKind } from '$lib/components/modules/module-helpers.js'; - import { Badge } from '$lib/ui/core/badge/index.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Checkbox } from '$lib/ui/core/checkbox/index.js'; + import { Badge } from '@evobgp/ui/components/badge/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Checkbox } from '@evobgp/ui/components/checkbox/index.js'; import { Card, CardContent, CardHeader, CardTitle, CardDescription - } from '$lib/ui/core/card/index.js'; - import AppDataTable from '$lib/ui/patterns/data-table/app-data-table.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import AppDataTable from '$lib/components/patterns/data-table/app-data-table.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import ModuleCdnSourceDialog from '$lib/components/modules/ModuleCdnSourceDialog.svelte'; import Plus from '@lucide/svelte/icons/plus'; diff --git a/web/src/lib/components/modules/ModuleCreateDialog.svelte b/apps/web/src/lib/components/modules/ModuleCreateDialog.svelte similarity index 90% rename from web/src/lib/components/modules/ModuleCreateDialog.svelte rename to apps/web/src/lib/components/modules/ModuleCreateDialog.svelte index 5d97705..2e277b9 100644 --- a/web/src/lib/components/modules/ModuleCreateDialog.svelte +++ b/apps/web/src/lib/components/modules/ModuleCreateDialog.svelte @@ -2,9 +2,9 @@ import { apiMutate } from '$lib/api/client.js'; import type { ModuleCreate } from '$lib/api/types.js'; import { moduleTypeRu } from '$lib/ui-labels.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; import { Dialog, DialogContent, @@ -12,9 +12,14 @@ DialogTitle, DialogFooter, DialogDescription - } from '$lib/ui/core/dialog/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; - import { Switch } from '$lib/ui/core/switch/index.js'; + } from '@evobgp/ui/components/dialog/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; + import { Switch } from '@evobgp/ui/components/switch/index.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; type Props = { diff --git a/web/src/lib/components/modules/ModuleDetailHeader.svelte b/apps/web/src/lib/components/modules/ModuleDetailHeader.svelte similarity index 93% rename from web/src/lib/components/modules/ModuleDetailHeader.svelte rename to apps/web/src/lib/components/modules/ModuleDetailHeader.svelte index 29f8c0f..721a791 100644 --- a/web/src/lib/components/modules/ModuleDetailHeader.svelte +++ b/apps/web/src/lib/components/modules/ModuleDetailHeader.svelte @@ -2,8 +2,8 @@ import { resolve } from '$app/paths'; import type { ModuleRow } from '$lib/api/types.js'; import { moduleEnabledRu, moduleEnabledBadgeVariant, moduleTypeRu } from '$lib/ui-labels.js'; - import { Badge } from '$lib/ui/core/badge/index.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Badge } from '@evobgp/ui/components/badge/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import PageHeader from '$lib/ui/app/page-header/page-header.svelte'; import ArrowLeft from '@lucide/svelte/icons/arrow-left'; import Pencil from '@lucide/svelte/icons/pencil'; diff --git a/web/src/lib/components/modules/ModuleDomainEntriesCard.svelte b/apps/web/src/lib/components/modules/ModuleDomainEntriesCard.svelte similarity index 95% rename from web/src/lib/components/modules/ModuleDomainEntriesCard.svelte rename to apps/web/src/lib/components/modules/ModuleDomainEntriesCard.svelte index f8b7853..bcaba71 100644 --- a/web/src/lib/components/modules/ModuleDomainEntriesCard.svelte +++ b/apps/web/src/lib/components/modules/ModuleDomainEntriesCard.svelte @@ -6,17 +6,17 @@ sanitizeFilenamePart, supportsCsvIO } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Checkbox } from '$lib/ui/core/checkbox/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Checkbox } from '@evobgp/ui/components/checkbox/index.js'; import { Card, CardContent, CardHeader, CardTitle, CardDescription - } from '$lib/ui/core/card/index.js'; - import AppDataTable from '$lib/ui/patterns/data-table/app-data-table.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import AppDataTable from '$lib/components/patterns/data-table/app-data-table.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import ModuleDomainEntryDialog from '$lib/components/modules/ModuleDomainEntryDialog.svelte'; import Plus from '@lucide/svelte/icons/plus'; diff --git a/web/src/lib/components/modules/ModuleDomainEntryDialog.svelte b/apps/web/src/lib/components/modules/ModuleDomainEntryDialog.svelte similarity index 89% rename from web/src/lib/components/modules/ModuleDomainEntryDialog.svelte rename to apps/web/src/lib/components/modules/ModuleDomainEntryDialog.svelte index 8b7ad28..82fc9c0 100644 --- a/web/src/lib/components/modules/ModuleDomainEntryDialog.svelte +++ b/apps/web/src/lib/components/modules/ModuleDomainEntryDialog.svelte @@ -8,17 +8,22 @@ NONE_OPTION, nullableSelectValue } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter - } from '$lib/ui/core/dialog/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; + } from '@evobgp/ui/components/dialog/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; type Props = { diff --git a/web/src/lib/components/modules/ModuleEditDialog.svelte b/apps/web/src/lib/components/modules/ModuleEditDialog.svelte similarity index 94% rename from web/src/lib/components/modules/ModuleEditDialog.svelte rename to apps/web/src/lib/components/modules/ModuleEditDialog.svelte index 44a62a5..6371aaa 100644 --- a/web/src/lib/components/modules/ModuleEditDialog.svelte +++ b/apps/web/src/lib/components/modules/ModuleEditDialog.svelte @@ -16,19 +16,24 @@ NONE_OPTION, nullableSelectValue } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; - import { Checkbox } from '$lib/ui/core/checkbox/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; + import { Checkbox } from '@evobgp/ui/components/checkbox/index.js'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter - } from '$lib/ui/core/dialog/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; - import { Switch } from '$lib/ui/core/switch/index.js'; + } from '@evobgp/ui/components/dialog/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; + import { Switch } from '@evobgp/ui/components/switch/index.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; const dohPolicyOptions: { value: DohResolverPolicy; label: string; hint: string }[] = [ diff --git a/web/src/lib/components/modules/ModuleIpRangeEntryDialog.svelte b/apps/web/src/lib/components/modules/ModuleIpRangeEntryDialog.svelte similarity index 89% rename from web/src/lib/components/modules/ModuleIpRangeEntryDialog.svelte rename to apps/web/src/lib/components/modules/ModuleIpRangeEntryDialog.svelte index bf54d79..f2cabf3 100644 --- a/web/src/lib/components/modules/ModuleIpRangeEntryDialog.svelte +++ b/apps/web/src/lib/components/modules/ModuleIpRangeEntryDialog.svelte @@ -2,17 +2,22 @@ import { apiMutate } from '$lib/api/client.js'; import type { BgpCommunity, IpRangeEntry, IpRangeEntryCreate } from '$lib/api/types.js'; import { communityLabel, communityOptionLabel } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter - } from '$lib/ui/core/dialog/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; + } from '@evobgp/ui/components/dialog/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; type Props = { diff --git a/web/src/lib/components/modules/ModuleIpRangesCard.svelte b/apps/web/src/lib/components/modules/ModuleIpRangesCard.svelte similarity index 96% rename from web/src/lib/components/modules/ModuleIpRangesCard.svelte rename to apps/web/src/lib/components/modules/ModuleIpRangesCard.svelte index 3e9ac94..772ee0a 100644 --- a/web/src/lib/components/modules/ModuleIpRangesCard.svelte +++ b/apps/web/src/lib/components/modules/ModuleIpRangesCard.svelte @@ -6,17 +6,17 @@ sanitizeFilenamePart, supportsCsvIO } from '$lib/components/modules/module-helpers.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Checkbox } from '$lib/ui/core/checkbox/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Checkbox } from '@evobgp/ui/components/checkbox/index.js'; import { Card, CardContent, CardHeader, CardTitle, CardDescription - } from '$lib/ui/core/card/index.js'; - import AppDataTable from '$lib/ui/patterns/data-table/app-data-table.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import AppDataTable from '$lib/components/patterns/data-table/app-data-table.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import ModuleIpRangeEntryDialog from '$lib/components/modules/ModuleIpRangeEntryDialog.svelte'; import Plus from '@lucide/svelte/icons/plus'; diff --git a/web/src/lib/components/modules/ModuleKpiCards.svelte b/apps/web/src/lib/components/modules/ModuleKpiCards.svelte similarity index 95% rename from web/src/lib/components/modules/ModuleKpiCards.svelte rename to apps/web/src/lib/components/modules/ModuleKpiCards.svelte index e3e3a66..0c32d5b 100644 --- a/web/src/lib/components/modules/ModuleKpiCards.svelte +++ b/apps/web/src/lib/components/modules/ModuleKpiCards.svelte @@ -7,8 +7,8 @@ dohProfileLabel, moduleDohProfileIds } from '$lib/components/modules/module-helpers.js'; - import { Card, CardContent, CardHeader, CardTitle } from '$lib/ui/core/card/index.js'; - import CardSkeleton from '$lib/ui/patterns/feedback/card-skeleton.svelte'; + import { Card, CardContent, CardHeader, CardTitle } from '@evobgp/ui/components/card/index.js'; + import CardSkeleton from '$lib/components/patterns/feedback/card-skeleton.svelte'; import { cn } from '$lib/utils.js'; import ArrowDownUp from '@lucide/svelte/icons/arrow-down-up'; import Timer from '@lucide/svelte/icons/timer'; diff --git a/web/src/lib/components/modules/ModuleSummaryCard.svelte b/apps/web/src/lib/components/modules/ModuleSummaryCard.svelte similarity index 97% rename from web/src/lib/components/modules/ModuleSummaryCard.svelte rename to apps/web/src/lib/components/modules/ModuleSummaryCard.svelte index a0b920e..07c920a 100644 --- a/web/src/lib/components/modules/ModuleSummaryCard.svelte +++ b/apps/web/src/lib/components/modules/ModuleSummaryCard.svelte @@ -1,14 +1,14 @@ + +
+ {@render children()} +
diff --git a/web/src/lib/ui/patterns/confirm/confirm-dialog.svelte b/apps/web/src/lib/components/patterns/confirm/confirm-dialog.svelte similarity index 96% rename from web/src/lib/ui/patterns/confirm/confirm-dialog.svelte rename to apps/web/src/lib/components/patterns/confirm/confirm-dialog.svelte index 3935180..57af643 100644 --- a/web/src/lib/ui/patterns/confirm/confirm-dialog.svelte +++ b/apps/web/src/lib/components/patterns/confirm/confirm-dialog.svelte @@ -8,7 +8,7 @@ AlertDialogFooter, AlertDialogHeader, AlertDialogTitle - } from '$lib/ui/core/alert-dialog/index.js'; + } from '@evobgp/ui/components/alert-dialog/index.js'; import { closeConfirm, confirmState } from './confirm-state.svelte.js'; const state = $derived(confirmState.current); diff --git a/web/src/lib/ui/patterns/confirm/confirm-state.svelte.ts b/apps/web/src/lib/components/patterns/confirm/confirm-state.svelte.ts similarity index 100% rename from web/src/lib/ui/patterns/confirm/confirm-state.svelte.ts rename to apps/web/src/lib/components/patterns/confirm/confirm-state.svelte.ts diff --git a/web/src/lib/ui/patterns/data-table/app-data-table.svelte b/apps/web/src/lib/components/patterns/data-table/app-data-table.svelte similarity index 70% rename from web/src/lib/ui/patterns/data-table/app-data-table.svelte rename to apps/web/src/lib/components/patterns/data-table/app-data-table.svelte index fc1268b..561508f 100644 --- a/web/src/lib/ui/patterns/data-table/app-data-table.svelte +++ b/apps/web/src/lib/components/patterns/data-table/app-data-table.svelte @@ -1,14 +1,14 @@ + +
+ {#if Icon} + + {/if} +

{title}

+ {#if description} +

{description}

+ {/if} + {#if action} +
{@render action()}
+ {/if} +
diff --git a/web/src/lib/ui/patterns/feedback/card-skeleton.svelte b/apps/web/src/lib/components/patterns/feedback/card-skeleton.svelte similarity index 64% rename from web/src/lib/ui/patterns/feedback/card-skeleton.svelte rename to apps/web/src/lib/components/patterns/feedback/card-skeleton.svelte index a4844b8..6d27c44 100644 --- a/web/src/lib/ui/patterns/feedback/card-skeleton.svelte +++ b/apps/web/src/lib/components/patterns/feedback/card-skeleton.svelte @@ -1,6 +1,6 @@ diff --git a/web/src/lib/ui/patterns/feedback/table-skeleton.svelte b/apps/web/src/lib/components/patterns/feedback/table-skeleton.svelte similarity index 77% rename from web/src/lib/ui/patterns/feedback/table-skeleton.svelte rename to apps/web/src/lib/components/patterns/feedback/table-skeleton.svelte index f2cc84f..7cf7c44 100644 --- a/web/src/lib/ui/patterns/feedback/table-skeleton.svelte +++ b/apps/web/src/lib/components/patterns/feedback/table-skeleton.svelte @@ -1,6 +1,6 @@ + +{#if isLoading} + {#if skeleton} + {@render skeleton()} + {:else} +
+ + +
+ {/if} +{:else if isError} + +{:else if empty || data == null} + +{:else} + {@render children(data)} +{/if} + +{#snippet retryAction()} + +{/snippet} diff --git a/apps/web/src/lib/components/section-cards-skeleton.svelte b/apps/web/src/lib/components/section-cards-skeleton.svelte new file mode 100644 index 0000000..e9e8b26 --- /dev/null +++ b/apps/web/src/lib/components/section-cards-skeleton.svelte @@ -0,0 +1,26 @@ + + +
+ {#each Array.from({ length: count }) as _, i (i)} + + + +
+ + +
+
+
+ {/each} +
diff --git a/apps/web/src/lib/components/section-cards.svelte b/apps/web/src/lib/components/section-cards.svelte new file mode 100644 index 0000000..161d125 --- /dev/null +++ b/apps/web/src/lib/components/section-cards.svelte @@ -0,0 +1,111 @@ + + +
+ {#each items as item, idx (typeof item.label === 'string' ? item.label : idx)} + {@const clickable = Boolean(item.onClick)} + { + if (clickable && (e.key === 'Enter' || e.key === ' ')) { + e.preventDefault(); + item.onClick?.(); + } + }} + > + + {#if item.icon} + + + + {/if} +
+
+ {#if typeof item.label === 'string'} + {item.label} + {:else} + {@render item.label()} + {/if} + {#if item.badge} + {@render item.badge()} + {/if} +
+
+ + {#if typeof item.value === 'string' || typeof item.value === 'number'} + {item.value} + {:else} + {@render item.value()} + {/if} + + {#if item.hint} + {#if typeof item.hint === 'string'} + · {item.hint} + {:else} + · {@render item.hint()} + {/if} + {/if} +
+
+
+
+ {/each} +
diff --git a/apps/web/src/lib/components/status-badge.svelte b/apps/web/src/lib/components/status-badge.svelte new file mode 100644 index 0000000..7fd4a92 --- /dev/null +++ b/apps/web/src/lib/components/status-badge.svelte @@ -0,0 +1,30 @@ + + +{text} diff --git a/apps/web/src/lib/components/table-skeleton.svelte b/apps/web/src/lib/components/table-skeleton.svelte new file mode 100644 index 0000000..7cf7c44 --- /dev/null +++ b/apps/web/src/lib/components/table-skeleton.svelte @@ -0,0 +1,27 @@ + + +
+ + + {#each Array(rows) as _, ri (ri)} + + {#each Array(columns) as _, ci (ci)} + + + + {/each} + + {/each} + + +
diff --git a/web/src/lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte b/apps/web/src/lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte similarity index 92% rename from web/src/lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte rename to apps/web/src/lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte index c9487b7..cc19fac 100644 --- a/web/src/lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte +++ b/apps/web/src/lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte @@ -6,17 +6,17 @@ patchSettings, type AdditionalSettingEntry } from '$lib/settings/settings-api.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardDescription, CardHeader, CardTitle - } from '$lib/ui/core/card/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import EmptyState from '$lib/ui/patterns/empty-state/empty-state.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import EmptyState from '$lib/components/patterns/empty-state/empty-state.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import Save from '@lucide/svelte/icons/save'; import Plus from '@lucide/svelte/icons/plus'; diff --git a/web/src/lib/components/tenant-settings/TenantBirdSettingsCard.svelte b/apps/web/src/lib/components/tenant-settings/TenantBirdSettingsCard.svelte similarity index 94% rename from web/src/lib/components/tenant-settings/TenantBirdSettingsCard.svelte rename to apps/web/src/lib/components/tenant-settings/TenantBirdSettingsCard.svelte index f622908..3351f07 100644 --- a/web/src/lib/components/tenant-settings/TenantBirdSettingsCard.svelte +++ b/apps/web/src/lib/components/tenant-settings/TenantBirdSettingsCard.svelte @@ -14,17 +14,17 @@ patchSettings } from '$lib/settings/settings-api.js'; import { BIRD_SETTING_KEYS } from '$lib/settings/settings-known-keys.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardDescription, CardHeader, CardTitle - } from '$lib/ui/core/card/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Alert, AlertDescription, AlertTitle } from '$lib/ui/core/alert/index.js'; - import FormField from '$lib/ui/patterns/form/form-field.svelte'; + } from '@evobgp/ui/components/card/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert/index.js'; + import FormField from '$lib/components/patterns/form/form-field.svelte'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import Save from '@lucide/svelte/icons/save'; import Info from '@lucide/svelte/icons/info'; diff --git a/web/src/lib/components/tenant-settings/TenantRevisionSettingsCard.svelte b/apps/web/src/lib/components/tenant-settings/TenantRevisionSettingsCard.svelte similarity index 96% rename from web/src/lib/components/tenant-settings/TenantRevisionSettingsCard.svelte rename to apps/web/src/lib/components/tenant-settings/TenantRevisionSettingsCard.svelte index 0ecf925..4fd3d88 100644 --- a/web/src/lib/components/tenant-settings/TenantRevisionSettingsCard.svelte +++ b/apps/web/src/lib/components/tenant-settings/TenantRevisionSettingsCard.svelte @@ -21,17 +21,17 @@ } from '$lib/settings/settings-api.js'; import { REVISION_SETTING_KEYS } from '$lib/settings/settings-known-keys.js'; import { formatBytes } from '$lib/monitoring/postgres.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardDescription, CardHeader, CardTitle - } from '$lib/ui/core/card/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import FormField from '$lib/ui/patterns/form/form-field.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import FormField from '$lib/components/patterns/form/form-field.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import Save from '@lucide/svelte/icons/save'; import Trash2 from '@lucide/svelte/icons/trash-2'; diff --git a/web/src/lib/components/tenant-settings/TenantRuntimeLogsSettingsCard.svelte b/apps/web/src/lib/components/tenant-settings/TenantRuntimeLogsSettingsCard.svelte similarity index 95% rename from web/src/lib/components/tenant-settings/TenantRuntimeLogsSettingsCard.svelte rename to apps/web/src/lib/components/tenant-settings/TenantRuntimeLogsSettingsCard.svelte index c3be090..a9db6b1 100644 --- a/web/src/lib/components/tenant-settings/TenantRuntimeLogsSettingsCard.svelte +++ b/apps/web/src/lib/components/tenant-settings/TenantRuntimeLogsSettingsCard.svelte @@ -22,19 +22,24 @@ } from '$lib/runtime-logs/runtime-logs-auto-api.js'; import { isRuntimeLogsUnavailable } from '$lib/runtime-logs/runtime-logs-api.js'; import { formatBytes } from '$lib/monitoring/postgres.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardDescription, CardHeader, CardTitle - } from '$lib/ui/core/card/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Switch } from '$lib/ui/core/switch/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; - import FormField from '$lib/ui/patterns/form/form-field.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + } from '@evobgp/ui/components/card/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Switch } from '@evobgp/ui/components/switch/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; + import FormField from '$lib/components/patterns/form/form-field.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import Save from '@lucide/svelte/icons/save'; import Play from '@lucide/svelte/icons/play'; diff --git a/web/src/lib/components/tenant-settings/TenantSettingsPage.svelte b/apps/web/src/lib/components/tenant-settings/TenantSettingsPage.svelte similarity index 90% rename from web/src/lib/components/tenant-settings/TenantSettingsPage.svelte rename to apps/web/src/lib/components/tenant-settings/TenantSettingsPage.svelte index be0290f..cf3adee 100644 --- a/web/src/lib/components/tenant-settings/TenantSettingsPage.svelte +++ b/apps/web/src/lib/components/tenant-settings/TenantSettingsPage.svelte @@ -2,9 +2,10 @@ import { onMount } from 'svelte'; import { goto } from '$app/navigation'; import { page } from '$app/state'; + import PageShell from '$lib/components/page-shell.svelte'; import PageHeader from '$lib/ui/app/page-header/page-header.svelte'; - import { Alert, AlertDescription, AlertTitle } from '$lib/ui/core/alert/index.js'; - import { Tabs, TabsContent, TabsList, TabsTrigger } from '$lib/ui/core/tabs/index.js'; + import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert/index.js'; + import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs/index.js'; import TenantBirdSettingsCard from '$lib/components/tenant-settings/TenantBirdSettingsCard.svelte'; import TenantRevisionSettingsCard from '$lib/components/tenant-settings/TenantRevisionSettingsCard.svelte'; import TenantAdditionalSettingsCard from '$lib/components/tenant-settings/TenantAdditionalSettingsCard.svelte'; @@ -44,7 +45,7 @@ }); -
+ -
+
BIRD Ревизии @@ -87,4 +88,4 @@ -
+ diff --git a/web/src/lib/dialog-layout.ts b/apps/web/src/lib/dialog-layout.ts similarity index 100% rename from web/src/lib/dialog-layout.ts rename to apps/web/src/lib/dialog-layout.ts diff --git a/web/src/lib/index.ts b/apps/web/src/lib/index.ts similarity index 100% rename from web/src/lib/index.ts rename to apps/web/src/lib/index.ts diff --git a/web/src/lib/maintenance/policy-api.ts b/apps/web/src/lib/maintenance/policy-api.ts similarity index 100% rename from web/src/lib/maintenance/policy-api.ts rename to apps/web/src/lib/maintenance/policy-api.ts diff --git a/web/src/lib/maintenance/policy-presets.ts b/apps/web/src/lib/maintenance/policy-presets.ts similarity index 100% rename from web/src/lib/maintenance/policy-presets.ts rename to apps/web/src/lib/maintenance/policy-presets.ts diff --git a/web/src/lib/maintenance/policy-schedule.ts b/apps/web/src/lib/maintenance/policy-schedule.ts similarity index 100% rename from web/src/lib/maintenance/policy-schedule.ts rename to apps/web/src/lib/maintenance/policy-schedule.ts diff --git a/web/src/lib/maintenance/policy.schema.ts b/apps/web/src/lib/maintenance/policy.schema.ts similarity index 100% rename from web/src/lib/maintenance/policy.schema.ts rename to apps/web/src/lib/maintenance/policy.schema.ts diff --git a/web/src/lib/modules/display.ts b/apps/web/src/lib/modules/display.ts similarity index 100% rename from web/src/lib/modules/display.ts rename to apps/web/src/lib/modules/display.ts diff --git a/web/src/lib/monitoring/postgres.ts b/apps/web/src/lib/monitoring/postgres.ts similarity index 100% rename from web/src/lib/monitoring/postgres.ts rename to apps/web/src/lib/monitoring/postgres.ts diff --git a/web/src/lib/monitoring/status.ts b/apps/web/src/lib/monitoring/status.ts similarity index 100% rename from web/src/lib/monitoring/status.ts rename to apps/web/src/lib/monitoring/status.ts diff --git a/web/src/lib/network/network-metrics.ts b/apps/web/src/lib/network/network-metrics.ts similarity index 100% rename from web/src/lib/network/network-metrics.ts rename to apps/web/src/lib/network/network-metrics.ts diff --git a/web/src/lib/operations/job-kind-label.ts b/apps/web/src/lib/operations/job-kind-label.ts similarity index 100% rename from web/src/lib/operations/job-kind-label.ts rename to apps/web/src/lib/operations/job-kind-label.ts diff --git a/apps/web/src/lib/queries/api-keys.ts b/apps/web/src/lib/queries/api-keys.ts new file mode 100644 index 0000000..dd01de7 --- /dev/null +++ b/apps/web/src/lib/queries/api-keys.ts @@ -0,0 +1,14 @@ +import { apiJSON } from '$lib/api/client.js'; +import type { ApiKeysResponse } from '$lib/api/types.js'; + +export const apiKeysKeys = { + all: ['api-keys'] as const, + list: (limit?: number) => [...apiKeysKeys.all, 'list', limit] as const +}; + +export function apiKeysQueryOptions(limit = 500) { + return { + queryKey: apiKeysKeys.list(limit), + queryFn: () => apiJSON(`/v1/api-keys?limit=${limit}`) + }; +} diff --git a/apps/web/src/lib/queries/auth.ts b/apps/web/src/lib/queries/auth.ts new file mode 100644 index 0000000..b2ff68c --- /dev/null +++ b/apps/web/src/lib/queries/auth.ts @@ -0,0 +1,15 @@ +import { apiJSON } from '$lib/api/client.js'; +import type { AuthSession } from '$lib/api/types.js'; + +export const authKeys = { + all: ['auth'] as const, + session: () => [...authKeys.all, 'session'] as const +}; + +export function authSessionQueryOptions() { + return { + queryKey: authKeys.session(), + queryFn: () => apiJSON('/v1/auth/session'), + retry: false + }; +} diff --git a/apps/web/src/lib/queries/dashboard.ts b/apps/web/src/lib/queries/dashboard.ts new file mode 100644 index 0000000..b067495 --- /dev/null +++ b/apps/web/src/lib/queries/dashboard.ts @@ -0,0 +1,44 @@ +import { healthQueryOptions } from './health.js'; +import { modulesQueryOptions } from './modules.js'; +import { jobsQueryOptions, revisionsQueryOptions } from './operations.js'; +import { peersQueryOptions, speakersQueryOptions } from './network.js'; + +export type DashboardSnapshot = { + healthy: boolean; + modulesCount: number; + revisionsCount: number; + peersCount: number; + speakersCount: number; + runningJobs: number; +}; + +export const dashboardKeys = { + all: ['dashboard'] as const, + stats: ['dashboard', 'stats'] as const +}; + +export function dashboardStatsQueryOptions() { + return { + queryKey: dashboardKeys.stats, + queryFn: async (): Promise => { + const [health, modules, revisions, peers, speakers, jobs] = await Promise.all([ + healthQueryOptions().queryFn(), + modulesQueryOptions(200).queryFn(), + revisionsQueryOptions(50).queryFn(), + peersQueryOptions(200).queryFn(), + speakersQueryOptions(200).queryFn(), + jobsQueryOptions(50).queryFn() + ]); + const runningJobs = jobs.items.filter((j) => j.status === 'running').length; + return { + healthy: health.ok, + modulesCount: modules.items.length, + revisionsCount: revisions.items.length, + peersCount: peers.items.length, + speakersCount: speakers.items.length, + runningJobs + }; + }, + staleTime: 15_000 + }; +} diff --git a/apps/web/src/lib/queries/health.ts b/apps/web/src/lib/queries/health.ts new file mode 100644 index 0000000..79530e0 --- /dev/null +++ b/apps/web/src/lib/queries/health.ts @@ -0,0 +1,16 @@ +import { apiFetch } from '$lib/api/client.js'; + +export const healthKeys = { + all: ['health'] as const +}; + +export function healthQueryOptions() { + return { + queryKey: healthKeys.all, + queryFn: async () => { + const res = await apiFetch('/v1/health'); + if (!res.ok) throw new Error(`GET /v1/health: HTTP ${res.status}`); + return { ok: true as const }; + } + }; +} diff --git a/apps/web/src/lib/queries/modules.ts b/apps/web/src/lib/queries/modules.ts new file mode 100644 index 0000000..d755a16 --- /dev/null +++ b/apps/web/src/lib/queries/modules.ts @@ -0,0 +1,14 @@ +import { apiJSON } from '$lib/api/client.js'; +import type { ModulesResponse } from '$lib/api/types.js'; + +export const modulesKeys = { + all: ['modules'] as const, + list: (limit?: number) => [...modulesKeys.all, 'list', limit] as const +}; + +export function modulesQueryOptions(limit = 50) { + return { + queryKey: modulesKeys.list(limit), + queryFn: () => apiJSON(`/v1/modules?limit=${limit}`) + }; +} diff --git a/apps/web/src/lib/queries/network.ts b/apps/web/src/lib/queries/network.ts new file mode 100644 index 0000000..3fb39a9 --- /dev/null +++ b/apps/web/src/lib/queries/network.ts @@ -0,0 +1,22 @@ +import { apiJSON } from '$lib/api/client.js'; +import type { PeersResponse, SpeakersResponse } from '$lib/api/types.js'; + +export const networkKeys = { + all: ['network'] as const, + peers: (limit?: number) => [...networkKeys.all, 'peers', limit] as const, + speakers: (limit?: number) => [...networkKeys.all, 'speakers', limit] as const +}; + +export function peersQueryOptions(limit = 50) { + return { + queryKey: networkKeys.peers(limit), + queryFn: () => apiJSON(`/v1/peers?limit=${limit}`) + }; +} + +export function speakersQueryOptions(limit = 50) { + return { + queryKey: networkKeys.speakers(limit), + queryFn: () => apiJSON(`/v1/speakers?limit=${limit}`) + }; +} diff --git a/apps/web/src/lib/queries/operations.ts b/apps/web/src/lib/queries/operations.ts new file mode 100644 index 0000000..78a122e --- /dev/null +++ b/apps/web/src/lib/queries/operations.ts @@ -0,0 +1,26 @@ +import { apiJSON } from '$lib/api/client.js'; +import type { JobsResponse, RevisionsResponse } from '$lib/api/types.js'; + +export const revisionsKeys = { + all: ['revisions'] as const, + list: (limit?: number) => [...revisionsKeys.all, 'list', limit] as const +}; + +export const jobsKeys = { + all: ['jobs'] as const, + list: (limit?: number) => [...jobsKeys.all, 'list', limit] as const +}; + +export function revisionsQueryOptions(limit = 10) { + return { + queryKey: revisionsKeys.list(limit), + queryFn: () => apiJSON(`/v1/revisions?limit=${limit}`) + }; +} + +export function jobsQueryOptions(limit = 10) { + return { + queryKey: jobsKeys.list(limit), + queryFn: () => apiJSON(`/v1/jobs?limit=${limit}`) + }; +} diff --git a/apps/web/src/lib/query-client.ts b/apps/web/src/lib/query-client.ts new file mode 100644 index 0000000..8737cc0 --- /dev/null +++ b/apps/web/src/lib/query-client.ts @@ -0,0 +1,10 @@ +import { QueryClient } from '@tanstack/svelte-query'; + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 30_000, + retry: 1 + } + } +}); diff --git a/web/src/lib/runtime-logs/runtime-logs-api.ts b/apps/web/src/lib/runtime-logs/runtime-logs-api.ts similarity index 100% rename from web/src/lib/runtime-logs/runtime-logs-api.ts rename to apps/web/src/lib/runtime-logs/runtime-logs-api.ts diff --git a/web/src/lib/runtime-logs/runtime-logs-auto-api.ts b/apps/web/src/lib/runtime-logs/runtime-logs-auto-api.ts similarity index 100% rename from web/src/lib/runtime-logs/runtime-logs-auto-api.ts rename to apps/web/src/lib/runtime-logs/runtime-logs-auto-api.ts diff --git a/web/src/lib/settings/bird-settings.schema.ts b/apps/web/src/lib/settings/bird-settings.schema.ts similarity index 100% rename from web/src/lib/settings/bird-settings.schema.ts rename to apps/web/src/lib/settings/bird-settings.schema.ts diff --git a/web/src/lib/settings/ip-validation.ts b/apps/web/src/lib/settings/ip-validation.ts similarity index 100% rename from web/src/lib/settings/ip-validation.ts rename to apps/web/src/lib/settings/ip-validation.ts diff --git a/web/src/lib/settings/revision-prune-api.ts b/apps/web/src/lib/settings/revision-prune-api.ts similarity index 100% rename from web/src/lib/settings/revision-prune-api.ts rename to apps/web/src/lib/settings/revision-prune-api.ts diff --git a/web/src/lib/settings/revision-settings.schema.ts b/apps/web/src/lib/settings/revision-settings.schema.ts similarity index 100% rename from web/src/lib/settings/revision-settings.schema.ts rename to apps/web/src/lib/settings/revision-settings.schema.ts diff --git a/web/src/lib/settings/runtime-logs-settings.schema.ts b/apps/web/src/lib/settings/runtime-logs-settings.schema.ts similarity index 100% rename from web/src/lib/settings/runtime-logs-settings.schema.ts rename to apps/web/src/lib/settings/runtime-logs-settings.schema.ts diff --git a/web/src/lib/settings/settings-api.ts b/apps/web/src/lib/settings/settings-api.ts similarity index 100% rename from web/src/lib/settings/settings-api.ts rename to apps/web/src/lib/settings/settings-api.ts diff --git a/web/src/lib/settings/settings-known-keys.ts b/apps/web/src/lib/settings/settings-known-keys.ts similarity index 100% rename from web/src/lib/settings/settings-known-keys.ts rename to apps/web/src/lib/settings/settings-known-keys.ts diff --git a/web/src/lib/settings/settings-known.schema.ts b/apps/web/src/lib/settings/settings-known.schema.ts similarity index 100% rename from web/src/lib/settings/settings-known.schema.ts rename to apps/web/src/lib/settings/settings-known.schema.ts diff --git a/web/src/lib/sort-prefixes.ts b/apps/web/src/lib/sort-prefixes.ts similarity index 100% rename from web/src/lib/sort-prefixes.ts rename to apps/web/src/lib/sort-prefixes.ts diff --git a/web/src/lib/theme-preferences.svelte.ts b/apps/web/src/lib/theme-preferences.svelte.ts similarity index 100% rename from web/src/lib/theme-preferences.svelte.ts rename to apps/web/src/lib/theme-preferences.svelte.ts diff --git a/web/src/lib/theme.ts b/apps/web/src/lib/theme.ts similarity index 100% rename from web/src/lib/theme.ts rename to apps/web/src/lib/theme.ts diff --git a/web/src/lib/ui-labels.ts b/apps/web/src/lib/ui-labels.ts similarity index 100% rename from web/src/lib/ui-labels.ts rename to apps/web/src/lib/ui-labels.ts diff --git a/web/src/lib/ui/app/layout/app-mobile-nav.svelte b/apps/web/src/lib/ui/app/layout/app-mobile-nav.svelte similarity index 92% rename from web/src/lib/ui/app/layout/app-mobile-nav.svelte rename to apps/web/src/lib/ui/app/layout/app-mobile-nav.svelte index 7758a87..36276b3 100644 --- a/web/src/lib/ui/app/layout/app-mobile-nav.svelte +++ b/apps/web/src/lib/ui/app/layout/app-mobile-nav.svelte @@ -6,8 +6,8 @@ SheetHeader, SheetTitle, SheetTrigger - } from '$lib/ui/core/sheet/index.js'; - import { Button } from '$lib/ui/core/button/index.js'; + } from '@evobgp/ui/components/sheet/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import type { ThemePreference } from '$lib/theme.js'; import Menu from '@lucide/svelte/icons/menu'; import AppNavLinks from './app-nav-links.svelte'; diff --git a/web/src/lib/ui/app/layout/app-nav-links.svelte b/apps/web/src/lib/ui/app/layout/app-nav-links.svelte similarity index 91% rename from web/src/lib/ui/app/layout/app-nav-links.svelte rename to apps/web/src/lib/ui/app/layout/app-nav-links.svelte index 4b7aaa4..f1ddb92 100644 --- a/web/src/lib/ui/app/layout/app-nav-links.svelte +++ b/apps/web/src/lib/ui/app/layout/app-nav-links.svelte @@ -2,9 +2,9 @@ import { resolve } from '$app/paths'; import { page } from '$app/state'; import { cn } from '$lib/utils.js'; - import { buttonVariants } from '$lib/ui/core/button/index.js'; - import { Separator } from '$lib/ui/core/separator/index.js'; - import { Tooltip, TooltipContent, TooltipTrigger } from '$lib/ui/core/tooltip/index.js'; + import { buttonVariants } from '@evobgp/ui/components/button/index.js'; + import { Separator } from '@evobgp/ui/components/separator/index.js'; + import { Tooltip, TooltipContent, TooltipTrigger } from '@evobgp/ui/components/tooltip/index.js'; import { bottomNav, mainNav } from './nav.js'; type Props = { diff --git a/web/src/lib/ui/app/layout/app-topbar.svelte b/apps/web/src/lib/ui/app/layout/app-topbar.svelte similarity index 100% rename from web/src/lib/ui/app/layout/app-topbar.svelte rename to apps/web/src/lib/ui/app/layout/app-topbar.svelte diff --git a/web/src/lib/ui/app/layout/app-version.svelte b/apps/web/src/lib/ui/app/layout/app-version.svelte similarity index 100% rename from web/src/lib/ui/app/layout/app-version.svelte rename to apps/web/src/lib/ui/app/layout/app-version.svelte diff --git a/web/src/lib/ui/app/layout/nav.ts b/apps/web/src/lib/ui/app/layout/nav.ts similarity index 100% rename from web/src/lib/ui/app/layout/nav.ts rename to apps/web/src/lib/ui/app/layout/nav.ts diff --git a/web/src/lib/ui/app/layout/theme-menu.svelte b/apps/web/src/lib/ui/app/layout/theme-menu.svelte similarity index 92% rename from web/src/lib/ui/app/layout/theme-menu.svelte rename to apps/web/src/lib/ui/app/layout/theme-menu.svelte index 7bf6d55..570c601 100644 --- a/web/src/lib/ui/app/layout/theme-menu.svelte +++ b/apps/web/src/lib/ui/app/layout/theme-menu.svelte @@ -1,6 +1,6 @@ -
+ {#snippet actions()} - {/snippet} @@ -284,7 +208,7 @@ - {#if healthy === null} + {#if $healthQuery.isLoading} Проверка API… @@ -318,32 +242,31 @@ {/if} - + {#if isLoading} + + {:else} + + {/if}
@@ -380,4 +303,4 @@ -
+ diff --git a/web/src/routes/access/+page.svelte b/apps/web/src/routes/access/+page.svelte similarity index 64% rename from web/src/routes/access/+page.svelte rename to apps/web/src/routes/access/+page.svelte index 388efd0..8190630 100644 --- a/web/src/routes/access/+page.svelte +++ b/apps/web/src/routes/access/+page.svelte @@ -1,62 +1,48 @@ -
+ {:else if session} @@ -99,7 +85,7 @@ или создайте ключ через API / переменную EVOBGP_API_KEYS. - {:else} + {:else if !$sessionQuery.isLoading} Не удалось определить сессию. Укажите Bearer-токен в @@ -110,4 +96,4 @@ {/if} -
+ diff --git a/web/src/routes/directories/+page.svelte b/apps/web/src/routes/directories/+page.svelte similarity index 90% rename from web/src/routes/directories/+page.svelte rename to apps/web/src/routes/directories/+page.svelte index 5d92abc..18dbd9f 100644 --- a/web/src/routes/directories/+page.svelte +++ b/apps/web/src/routes/directories/+page.svelte @@ -8,19 +8,20 @@ DohProfile, DohProfilesResponse } from '$lib/api/types.js'; - import { Badge } from '$lib/ui/core/badge/index.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Badge } from '@evobgp/ui/components/badge/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardHeader, CardTitle, CardDescription - } from '$lib/ui/core/card/index.js'; - import { Alert, AlertDescription, AlertTitle } from '$lib/ui/core/alert/index.js'; - import { Tabs, TabsContent, TabsList, TabsTrigger } from '$lib/ui/core/tabs/index.js'; - import CardSkeleton from '$lib/ui/patterns/feedback/card-skeleton.svelte'; - import KpiMetricsGrid from '$lib/ui/patterns/kpi/kpi-metrics-grid.svelte'; + } from '@evobgp/ui/components/card/index.js'; + import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert/index.js'; + import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs/index.js'; + import CardSkeleton from '$lib/components/patterns/feedback/card-skeleton.svelte'; + import KpiMetricsGrid from '$lib/components/patterns/kpi/kpi-metrics-grid.svelte'; + import PageShell from '$lib/components/page-shell.svelte'; import PageHeader from '$lib/ui/app/page-header/page-header.svelte'; import { notifyApiError } from '$lib/ui/app/toast.js'; import DirectoriesCommunitiesCard from '$lib/components/directories/DirectoriesCommunitiesCard.svelte'; @@ -143,7 +144,7 @@ onMount(load); -
+ -
+ diff --git a/apps/web/src/routes/layout.css b/apps/web/src/routes/layout.css new file mode 100644 index 0000000..1aa66c5 --- /dev/null +++ b/apps/web/src/routes/layout.css @@ -0,0 +1 @@ +@import '@evobgp/ui/styles/globals.css'; diff --git a/web/src/routes/modules/+page.svelte b/apps/web/src/routes/modules/+page.svelte similarity index 75% rename from web/src/routes/modules/+page.svelte rename to apps/web/src/routes/modules/+page.svelte index c18a951..a29dc58 100644 --- a/web/src/routes/modules/+page.svelte +++ b/apps/web/src/routes/modules/+page.svelte @@ -1,32 +1,35 @@ -
+ {#snippet actions()} - @@ -218,12 +188,11 @@ - + {#if initialLoading} + + {:else} + + {/if} @@ -304,6 +273,6 @@ -
+ - {}} onCreated={load} /> + {}} onCreated={refetchModules} /> diff --git a/web/src/routes/modules/[moduleId]/+page.svelte b/apps/web/src/routes/modules/[moduleId]/+page.svelte similarity index 94% rename from web/src/routes/modules/[moduleId]/+page.svelte rename to apps/web/src/routes/modules/[moduleId]/+page.svelte index 6d87bec..5aa8c1d 100644 --- a/web/src/routes/modules/[moduleId]/+page.svelte +++ b/apps/web/src/routes/modules/[moduleId]/+page.svelte @@ -20,10 +20,11 @@ DohProfilesResponse } from '$lib/api/types.js'; import { moduleTypeRu } from '$lib/ui-labels.js'; - import { Alert, AlertDescription, AlertTitle } from '$lib/ui/core/alert/index.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import EmptyState from '$lib/ui/patterns/empty-state/empty-state.svelte'; - import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js'; + import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import PageShell from '$lib/components/page-shell.svelte'; + import EmptyState from '$lib/components/patterns/empty-state/empty-state.svelte'; + import { confirm } from '$lib/components/patterns/confirm/confirm-state.svelte.js'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import ModuleDetailHeader from '$lib/components/modules/ModuleDetailHeader.svelte'; import ModuleKpiCards from '$lib/components/modules/ModuleKpiCards.svelte'; @@ -172,11 +173,11 @@ {#if loadingMod} -
+ -
+ {:else if mod} -
+ {/if} -
+ {#if moduleId} -
+ -
+ diff --git a/web/src/routes/network/+page.svelte b/apps/web/src/routes/network/+page.svelte similarity index 95% rename from web/src/routes/network/+page.svelte rename to apps/web/src/routes/network/+page.svelte index 6ac0a99..4c7e849 100644 --- a/web/src/routes/network/+page.svelte +++ b/apps/web/src/routes/network/+page.svelte @@ -12,9 +12,10 @@ SpeakersResponse } from '$lib/api/types.js'; import { NETWORK_AUTO_REFRESH_MS } from '$lib/network/network-metrics.js'; - import { Button } from '$lib/ui/core/button/index.js'; - import { Alert, AlertDescription, AlertTitle } from '$lib/ui/core/alert/index.js'; - import { Tabs, TabsContent, TabsList, TabsTrigger } from '$lib/ui/core/tabs/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; + import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert/index.js'; + import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs/index.js'; + import PageShell from '$lib/components/page-shell.svelte'; import PageHeader from '$lib/ui/app/page-header/page-header.svelte'; import { notifyApiError } from '$lib/ui/app/toast.js'; import NetworkPeersCard from '$lib/components/network/NetworkPeersCard.svelte'; @@ -165,7 +166,7 @@ }); -
+ -
+ -
+ -
+
Ревизии ({revisions.length}) Сравнение @@ -1030,7 +1036,7 @@ /> -
+ diff --git a/web/src/routes/peers/+page.svelte b/apps/web/src/routes/peers/+page.svelte similarity index 100% rename from web/src/routes/peers/+page.svelte rename to apps/web/src/routes/peers/+page.svelte diff --git a/web/src/routes/revisions/+page.svelte b/apps/web/src/routes/revisions/+page.svelte similarity index 100% rename from web/src/routes/revisions/+page.svelte rename to apps/web/src/routes/revisions/+page.svelte diff --git a/web/src/routes/schedule/+page.svelte b/apps/web/src/routes/schedule/+page.svelte similarity index 94% rename from web/src/routes/schedule/+page.svelte rename to apps/web/src/routes/schedule/+page.svelte index 138b64f..ce6630d 100644 --- a/web/src/routes/schedule/+page.svelte +++ b/apps/web/src/routes/schedule/+page.svelte @@ -10,20 +10,21 @@ } from '$lib/modules/display.js'; import { jobKindTitle } from '$lib/operations/job-kind-label.js'; import { jobStatusRu, jobStatusBadgeVariant, moduleTypeRu } from '$lib/ui-labels.js'; - import { Badge } from '$lib/ui/core/badge/index.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Badge } from '@evobgp/ui/components/badge/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardHeader, CardTitle, CardDescription - } from '$lib/ui/core/card/index.js'; - import { Alert, AlertDescription, AlertTitle } from '$lib/ui/core/alert/index.js'; - import { Tabs, TabsContent, TabsList, TabsTrigger } from '$lib/ui/core/tabs/index.js'; - import AppDataTable from '$lib/ui/patterns/data-table/app-data-table.svelte'; - import CardSkeleton from '$lib/ui/patterns/feedback/card-skeleton.svelte'; - import KpiMetricsGrid from '$lib/ui/patterns/kpi/kpi-metrics-grid.svelte'; + } from '@evobgp/ui/components/card/index.js'; + import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert/index.js'; + import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs/index.js'; + import AppDataTable from '$lib/components/patterns/data-table/app-data-table.svelte'; + import CardSkeleton from '$lib/components/patterns/feedback/card-skeleton.svelte'; + import KpiMetricsGrid from '$lib/components/patterns/kpi/kpi-metrics-grid.svelte'; + import PageShell from '$lib/components/page-shell.svelte'; import PageHeader from '$lib/ui/app/page-header/page-header.svelte'; import { notify, notifyApiError } from '$lib/ui/app/toast.js'; import { cn } from '$lib/utils.js'; @@ -204,7 +205,7 @@ onMount(load); -
+ -
+ diff --git a/web/src/routes/settings/+page.svelte b/apps/web/src/routes/settings/+page.svelte similarity index 87% rename from web/src/routes/settings/+page.svelte rename to apps/web/src/routes/settings/+page.svelte index 27198c9..ee8bb21 100644 --- a/web/src/routes/settings/+page.svelte +++ b/apps/web/src/routes/settings/+page.svelte @@ -5,20 +5,26 @@ import { TOKEN_STORAGE_KEY } from '$lib/api/client.js'; import { themeState } from '$lib/theme-preferences.svelte.js'; import type { ThemePreference } from '$lib/theme.js'; - import { Button } from '$lib/ui/core/button/index.js'; + import { Button } from '@evobgp/ui/components/button/index.js'; import { Card, CardContent, CardDescription, CardHeader, CardTitle - } from '$lib/ui/core/card/index.js'; - import { Input } from '$lib/ui/core/input/index.js'; - import { Label } from '$lib/ui/core/label/index.js'; - import { Select, SelectContent, SelectItem, SelectTrigger } from '$lib/ui/core/select/index.js'; + } from '@evobgp/ui/components/card/index.js'; + import { Input } from '@evobgp/ui/components/input/index.js'; + import { Label } from '@evobgp/ui/components/label/index.js'; + import { + Select, + SelectContent, + SelectItem, + SelectTrigger + } from '@evobgp/ui/components/select/index.js'; import { notify } from '$lib/ui/app/toast.js'; import Save from '@lucide/svelte/icons/save'; import SettingsIcon from '@lucide/svelte/icons/settings'; + import PageShell from '$lib/components/page-shell.svelte'; import PageHeader from '$lib/ui/app/page-header/page-header.svelte'; let token = $state(''); @@ -51,7 +57,7 @@ } -
+ -
+ diff --git a/web/src/routes/tenant-settings/+page.svelte b/apps/web/src/routes/tenant-settings/+page.svelte similarity index 100% rename from web/src/routes/tenant-settings/+page.svelte rename to apps/web/src/routes/tenant-settings/+page.svelte diff --git a/web/static/robots.txt b/apps/web/static/robots.txt similarity index 100% rename from web/static/robots.txt rename to apps/web/static/robots.txt diff --git a/web/svelte.config.js b/apps/web/svelte.config.js similarity index 75% rename from web/svelte.config.js rename to apps/web/svelte.config.js index 6469714..115a20e 100644 --- a/web/svelte.config.js +++ b/apps/web/svelte.config.js @@ -1,5 +1,6 @@ import adapter from '@sveltejs/adapter-static'; import { relative, sep } from 'node:path'; +import path from 'node:path'; /** @type {import('@sveltejs/kit').Config} */ const config = { @@ -17,7 +18,11 @@ const config = { adapter: adapter({ fallback: 'index.html', strict: false - }) + }), + alias: { + '@evobgp/ui': path.resolve(import.meta.dirname, '../../packages/ui/src'), + '@evobgp/shared': path.resolve(import.meta.dirname, '../../packages/shared/src') + } } }; diff --git a/web/tsconfig.json b/apps/web/tsconfig.json similarity index 50% rename from web/tsconfig.json rename to apps/web/tsconfig.json index 2c2ed3c..feea18b 100644 --- a/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -12,9 +12,4 @@ "strict": true, "moduleResolution": "bundler" } - // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias - // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files - // - // To make changes to top-level options such as include and exclude, we recommend extending - // the generated config; see https://svelte.dev/docs/kit/configuration#typescript } diff --git a/web/vite.config.ts b/apps/web/vite.config.ts similarity index 52% rename from web/vite.config.ts rename to apps/web/vite.config.ts index 37d23cb..45aaaaa 100644 --- a/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -1,9 +1,20 @@ import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; +import path from 'node:path'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [tailwindcss(), sveltekit()], + resolve: { + alias: { + '@evobgp/ui': path.resolve(import.meta.dirname, '../../packages/ui/src'), + '@evobgp/shared': path.resolve(import.meta.dirname, '../../packages/shared/src'), + '@evobgp/ui/globals.css': path.resolve( + import.meta.dirname, + '../../packages/ui/src/styles/globals.css' + ) + } + }, server: { proxy: { '/v1': { target: 'http://127.0.0.1:8080', changeOrigin: true }, diff --git a/deploy/docker/evobgp-web/Dockerfile b/deploy/docker/evobgp-web/Dockerfile index 8988ccf..b69f12e 100644 --- a/deploy/docker/evobgp-web/Dockerfile +++ b/deploy/docker/evobgp-web/Dockerfile @@ -2,18 +2,24 @@ # Статическая панель EvoBGP (SvelteKit) + nginx. # Финальный stage `web` ожидает bake-контекст web-artifacts (= target:web-build). FROM public.ecr.aws/docker/library/node:22-alpine AS deps -WORKDIR /web -COPY web/package.json web/package-lock.json ./ -RUN --mount=type=cache,target=/root/.npm,sharing=locked \ - npm ci +WORKDIR /app +RUN corepack enable && corepack prepare pnpm@10.33.2 --activate +COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ +COPY apps/web/package.json apps/web/ +COPY packages/ui/package.json packages/ui/ +COPY packages/shared/package.json packages/shared/ +RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ + pnpm install --frozen-lockfile FROM deps AS build -COPY web/ ./ -RUN npm run build +COPY tsconfig.base.json ./ +COPY packages/ packages/ +COPY apps/web/ apps/web/ +RUN pnpm --filter @evobgp/web build FROM public.ecr.aws/docker/library/nginx:1.27-alpine AS web ARG EVOBGP_UPSTREAM=evobgp-api COPY deploy/docker/evobgp-web/nginx.conf /tmp/nginx-default.conf RUN sed -e "s/evobgp-api/${EVOBGP_UPSTREAM}/g" /tmp/nginx-default.conf > /etc/nginx/conf.d/default.conf \ && rm -f /tmp/nginx-default.conf -COPY --from=web-artifacts /web/build /usr/share/nginx/html +COPY --from=web-artifacts /app/apps/web/build /usr/share/nginx/html diff --git a/package.json b/package.json index 124d12e..2b8776e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,18 @@ { - "name": "evobgp-release", + "name": "evobgp", "private": true, + "type": "module", + "scripts": { + "dev": "pnpm --filter @evobgp/web dev", + "build": "pnpm --filter @evobgp/web build", + "check": "pnpm --filter @evobgp/web check", + "lint": "pnpm --filter @evobgp/web lint", + "format": "pnpm --filter @evobgp/web format" + }, + "packageManager": "pnpm@10.33.2", + "engines": { + "node": ">=22" + }, "devDependencies": { "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..30a36b1 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,14 @@ +{ + "name": "@evobgp/shared", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts", + "./types/*": "./src/types/*", + "./contracts/*": "./src/contracts/*" + }, + "dependencies": { + "zod": "^4.4.3" + } +} diff --git a/packages/shared/src/contracts/common.ts b/packages/shared/src/contracts/common.ts new file mode 100644 index 0000000..9f0b88a --- /dev/null +++ b/packages/shared/src/contracts/common.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +export const pageSchema = (itemSchema: T) => + z.object({ + items: z.array(itemSchema), + next_cursor: z.string().nullable(), + has_more: z.boolean() + }); diff --git a/packages/shared/src/contracts/modules.ts b/packages/shared/src/contracts/modules.ts new file mode 100644 index 0000000..9966cd1 --- /dev/null +++ b/packages/shared/src/contracts/modules.ts @@ -0,0 +1,38 @@ +import { z } from 'zod'; + +export const moduleTypeSchema = z.enum(['AS_PREFIXES', 'CDN_CIDRS', 'DOMAINS', 'IP_RANGES']); + +export const dohResolverPolicySchema = z.enum(['primary_only', 'failover', 'union']); + +export const moduleRowSchema = z.object({ + id: z.string(), + type: moduleTypeSchema, + name: z.string().min(1), + enabled: z.boolean(), + priority: z.number().int(), + refresh_interval_sec: z.number().int().nullable(), + cron_expr: z.string().nullable(), + default_community_id: z.string().nullable(), + doh_profile_id: z.string().nullable(), + doh_profile_ids: z.array(z.string()), + doh_resolver_policy: dohResolverPolicySchema, + last_refreshed_at: z.string().nullable() +}); + +export const moduleCreateSchema = z.object({ + type: moduleTypeSchema, + name: z.string().min(1, 'Укажите название'), + enabled: z.boolean().optional(), + priority: z.number().int().optional(), + doh_profile_id: z.string().nullable().optional(), + doh_profile_ids: z.array(z.string()).optional(), + doh_resolver_policy: dohResolverPolicySchema.optional(), + refresh_interval_sec: z.number().int().nullable().optional(), + cron_expr: z.string().nullable().optional(), + default_community_id: z.string().nullable().optional() +}); + +export const modulePatchSchema = moduleCreateSchema.omit({ type: true }).partial(); + +export type ModuleCreateInput = z.infer; +export type ModulePatchInput = z.infer; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..6b6f592 --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,2 @@ +export * from './types/api.js'; +export * from './contracts/modules.js'; diff --git a/web/src/lib/api/types.ts b/packages/shared/src/types/api.ts similarity index 100% rename from web/src/lib/api/types.ts rename to packages/shared/src/types/api.ts diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..bf5a36d --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"] +} diff --git a/packages/ui/components.json b/packages/ui/components.json new file mode 100644 index 0000000..d611eb7 --- /dev/null +++ b/packages/ui/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://www.shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/styles/globals.css", + "baseColor": "neutral" + }, + "aliases": { + "components": "src/components", + "utils": "src/lib/utils", + "ui": "src/components", + "hooks": "src/hooks", + "lib": "src/lib" + }, + "typescript": true, + "iconLibrary": "lucide" +} diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..5caac0e --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,30 @@ +{ + "name": "@evobgp/ui", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + "./components/*": "./src/components/*", + "./hooks/*": "./src/hooks/*", + "./lib/utils": "./src/lib/utils.ts", + "./globals.css": "./src/styles/globals.css", + "./styles/globals.css": "./src/styles/globals.css" + }, + "dependencies": { + "@lucide/svelte": "^1.16.0", + "@tanstack/table-core": "^8.21.3", + "bits-ui": "^2.17.2", + "clsx": "^2.1.1", + "formsnap": "^2.0.1", + "sveltekit-superforms": "^2.30.1", + "tailwind-merge": "^3.5.0", + "tailwind-variants": "^3.2.2", + "tw-animate-css": "^1.4.0" + }, + "peerDependencies": { + "svelte": "^5.54.0" + }, + "devDependencies": { + "tailwindcss": "^4.1.18" + } +} diff --git a/web/src/lib/ui/core/alert-dialog/alert-dialog-action.svelte b/packages/ui/src/components/alert-dialog/alert-dialog-action.svelte similarity index 85% rename from web/src/lib/ui/core/alert-dialog/alert-dialog-action.svelte rename to packages/ui/src/components/alert-dialog/alert-dialog-action.svelte index 8393d03..ea9064c 100644 --- a/web/src/lib/ui/core/alert-dialog/alert-dialog-action.svelte +++ b/packages/ui/src/components/alert-dialog/alert-dialog-action.svelte @@ -4,8 +4,8 @@ buttonVariants, type ButtonVariant, type ButtonSize - } from '$lib/ui/core/button/index.js'; - import { cn } from '$lib/utils.js'; + } from '@evobgp/ui/components/button/index.js'; + import { cn } from '@evobgp/ui/lib/utils'; let { ref = $bindable(null), diff --git a/web/src/lib/ui/core/alert-dialog/alert-dialog-cancel.svelte b/packages/ui/src/components/alert-dialog/alert-dialog-cancel.svelte similarity index 85% rename from web/src/lib/ui/core/alert-dialog/alert-dialog-cancel.svelte rename to packages/ui/src/components/alert-dialog/alert-dialog-cancel.svelte index b2ed1ba..b7aedf9 100644 --- a/web/src/lib/ui/core/alert-dialog/alert-dialog-cancel.svelte +++ b/packages/ui/src/components/alert-dialog/alert-dialog-cancel.svelte @@ -4,8 +4,8 @@ buttonVariants, type ButtonVariant, type ButtonSize - } from '$lib/ui/core/button/index.js'; - import { cn } from '$lib/utils.js'; + } from '@evobgp/ui/components/button/index.js'; + import { cn } from '@evobgp/ui/lib/utils'; let { ref = $bindable(null), diff --git a/web/src/lib/ui/core/alert-dialog/alert-dialog-content.svelte b/packages/ui/src/components/alert-dialog/alert-dialog-content.svelte similarity index 97% rename from web/src/lib/ui/core/alert-dialog/alert-dialog-content.svelte rename to packages/ui/src/components/alert-dialog/alert-dialog-content.svelte index 67e082c..d9bdcb0 100644 --- a/web/src/lib/ui/core/alert-dialog/alert-dialog-content.svelte +++ b/packages/ui/src/components/alert-dialog/alert-dialog-content.svelte @@ -2,7 +2,7 @@ import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'; import AlertDialogPortal from './alert-dialog-portal.svelte'; import AlertDialogOverlay from './alert-dialog-overlay.svelte'; - import { cn, type WithoutChild, type WithoutChildrenOrChild } from '$lib/utils.js'; + import { cn, type WithoutChild, type WithoutChildrenOrChild } from '@evobgp/ui/lib/utils'; import type { ComponentProps } from 'svelte'; let { diff --git a/web/src/lib/ui/core/alert-dialog/alert-dialog-description.svelte b/packages/ui/src/components/alert-dialog/alert-dialog-description.svelte similarity index 91% rename from web/src/lib/ui/core/alert-dialog/alert-dialog-description.svelte rename to packages/ui/src/components/alert-dialog/alert-dialog-description.svelte index 9b2dd92..bf3deab 100644 --- a/web/src/lib/ui/core/alert-dialog/alert-dialog-description.svelte +++ b/packages/ui/src/components/alert-dialog/alert-dialog-description.svelte @@ -1,6 +1,6 @@ + + diff --git a/packages/ui/src/components/breadcrumb/breadcrumb-item.svelte b/packages/ui/src/components/breadcrumb/breadcrumb-item.svelte new file mode 100644 index 0000000..5e1db48 --- /dev/null +++ b/packages/ui/src/components/breadcrumb/breadcrumb-item.svelte @@ -0,0 +1,20 @@ + + +
  • + {@render children?.()} +
  • diff --git a/packages/ui/src/components/breadcrumb/breadcrumb-link.svelte b/packages/ui/src/components/breadcrumb/breadcrumb-link.svelte new file mode 100644 index 0000000..1504f02 --- /dev/null +++ b/packages/ui/src/components/breadcrumb/breadcrumb-link.svelte @@ -0,0 +1,31 @@ + + +{#if child} + {@render child({ props: attrs })} +{:else} + + {@render children?.()} + +{/if} diff --git a/packages/ui/src/components/breadcrumb/breadcrumb-list.svelte b/packages/ui/src/components/breadcrumb/breadcrumb-list.svelte new file mode 100644 index 0000000..72eb4aa --- /dev/null +++ b/packages/ui/src/components/breadcrumb/breadcrumb-list.svelte @@ -0,0 +1,20 @@ + + +
      + {@render children?.()} +
    diff --git a/packages/ui/src/components/breadcrumb/breadcrumb-page.svelte b/packages/ui/src/components/breadcrumb/breadcrumb-page.svelte new file mode 100644 index 0000000..58d47f2 --- /dev/null +++ b/packages/ui/src/components/breadcrumb/breadcrumb-page.svelte @@ -0,0 +1,23 @@ + + + + {@render children?.()} + diff --git a/packages/ui/src/components/breadcrumb/breadcrumb-separator.svelte b/packages/ui/src/components/breadcrumb/breadcrumb-separator.svelte new file mode 100644 index 0000000..45e4713 --- /dev/null +++ b/packages/ui/src/components/breadcrumb/breadcrumb-separator.svelte @@ -0,0 +1,27 @@ + + + diff --git a/packages/ui/src/components/breadcrumb/breadcrumb.svelte b/packages/ui/src/components/breadcrumb/breadcrumb.svelte new file mode 100644 index 0000000..dea3622 --- /dev/null +++ b/packages/ui/src/components/breadcrumb/breadcrumb.svelte @@ -0,0 +1,22 @@ + + + diff --git a/packages/ui/src/components/breadcrumb/index.ts b/packages/ui/src/components/breadcrumb/index.ts new file mode 100644 index 0000000..dc914ec --- /dev/null +++ b/packages/ui/src/components/breadcrumb/index.ts @@ -0,0 +1,25 @@ +import Root from "./breadcrumb.svelte"; +import Ellipsis from "./breadcrumb-ellipsis.svelte"; +import Item from "./breadcrumb-item.svelte"; +import Separator from "./breadcrumb-separator.svelte"; +import Link from "./breadcrumb-link.svelte"; +import List from "./breadcrumb-list.svelte"; +import Page from "./breadcrumb-page.svelte"; + +export { + Root, + Ellipsis, + Item, + Separator, + Link, + List, + Page, + // + Root as Breadcrumb, + Ellipsis as BreadcrumbEllipsis, + Item as BreadcrumbItem, + Separator as BreadcrumbSeparator, + Link as BreadcrumbLink, + List as BreadcrumbList, + Page as BreadcrumbPage, +}; diff --git a/web/src/lib/ui/core/button/button.svelte b/packages/ui/src/components/button/button.svelte similarity index 98% rename from web/src/lib/ui/core/button/button.svelte rename to packages/ui/src/components/button/button.svelte index 9c97ac4..947252e 100644 --- a/web/src/lib/ui/core/button/button.svelte +++ b/packages/ui/src/components/button/button.svelte @@ -1,5 +1,5 @@ diff --git a/web/src/lib/ui/core/form/form-description.svelte b/packages/ui/src/components/form/form-description.svelte similarity index 84% rename from web/src/lib/ui/core/form/form-description.svelte rename to packages/ui/src/components/form/form-description.svelte index 034270a..a1b2c5a 100644 --- a/web/src/lib/ui/core/form/form-description.svelte +++ b/packages/ui/src/components/form/form-description.svelte @@ -1,6 +1,6 @@ + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-footer.svelte b/packages/ui/src/components/sidebar/sidebar-footer.svelte new file mode 100644 index 0000000..9df87ea --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-footer.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-group-action.svelte b/packages/ui/src/components/sidebar/sidebar-group-action.svelte new file mode 100644 index 0000000..1de5162 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-group-action.svelte @@ -0,0 +1,33 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + +{/if} diff --git a/packages/ui/src/components/sidebar/sidebar-group-content.svelte b/packages/ui/src/components/sidebar/sidebar-group-content.svelte new file mode 100644 index 0000000..8d46072 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-group-content.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-group-label.svelte b/packages/ui/src/components/sidebar/sidebar-group-label.svelte new file mode 100644 index 0000000..e50bf9d --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-group-label.svelte @@ -0,0 +1,33 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} +
    + {@render children?.()} +
    +{/if} diff --git a/packages/ui/src/components/sidebar/sidebar-group.svelte b/packages/ui/src/components/sidebar/sidebar-group.svelte new file mode 100644 index 0000000..1d87deb --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-group.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-header.svelte b/packages/ui/src/components/sidebar/sidebar-header.svelte new file mode 100644 index 0000000..ef81e7f --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-header.svelte @@ -0,0 +1,21 @@ + + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-input.svelte b/packages/ui/src/components/sidebar/sidebar-input.svelte new file mode 100644 index 0000000..cd78af7 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-input.svelte @@ -0,0 +1,21 @@ + + + diff --git a/packages/ui/src/components/sidebar/sidebar-inset.svelte b/packages/ui/src/components/sidebar/sidebar-inset.svelte new file mode 100644 index 0000000..762bcd2 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-inset.svelte @@ -0,0 +1,20 @@ + + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-menu-action.svelte b/packages/ui/src/components/sidebar/sidebar-menu-action.svelte new file mode 100644 index 0000000..6bc53b1 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-action.svelte @@ -0,0 +1,37 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + +{/if} diff --git a/packages/ui/src/components/sidebar/sidebar-menu-badge.svelte b/packages/ui/src/components/sidebar/sidebar-menu-badge.svelte new file mode 100644 index 0000000..afbec7e --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-badge.svelte @@ -0,0 +1,24 @@ + + +
    + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-menu-button.svelte b/packages/ui/src/components/sidebar/sidebar-menu-button.svelte new file mode 100644 index 0000000..16809b0 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-button.svelte @@ -0,0 +1,102 @@ + + + + +{#snippet Button({ props }: { props?: Record })} + {@const mergedProps = mergeProps(buttonProps, props)} + {#if child} + {@render child({ props: mergedProps })} + {:else} + + {/if} +{/snippet} + +{#if !tooltipContent} + {@render Button({})} +{:else} + + + {#snippet child({ props })} + {@render Button({ props })} + {/snippet} + + + +{/if} diff --git a/packages/ui/src/components/sidebar/sidebar-menu-item.svelte b/packages/ui/src/components/sidebar/sidebar-menu-item.svelte new file mode 100644 index 0000000..141a940 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-item.svelte @@ -0,0 +1,21 @@ + + +
  • + {@render children?.()} +
  • diff --git a/packages/ui/src/components/sidebar/sidebar-menu-skeleton.svelte b/packages/ui/src/components/sidebar/sidebar-menu-skeleton.svelte new file mode 100644 index 0000000..eefbc5a --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-skeleton.svelte @@ -0,0 +1,36 @@ + + +
    + {#if showIcon} + + {/if} + + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-menu-sub-button.svelte b/packages/ui/src/components/sidebar/sidebar-menu-sub-button.svelte new file mode 100644 index 0000000..6f0edc0 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-sub-button.svelte @@ -0,0 +1,39 @@ + + +{#if child} + {@render child({ props: mergedProps })} +{:else} + + {@render children?.()} + +{/if} diff --git a/packages/ui/src/components/sidebar/sidebar-menu-sub-item.svelte b/packages/ui/src/components/sidebar/sidebar-menu-sub-item.svelte new file mode 100644 index 0000000..402f389 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-sub-item.svelte @@ -0,0 +1,21 @@ + + +
  • + {@render children?.()} +
  • diff --git a/packages/ui/src/components/sidebar/sidebar-menu-sub.svelte b/packages/ui/src/components/sidebar/sidebar-menu-sub.svelte new file mode 100644 index 0000000..c58a994 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu-sub.svelte @@ -0,0 +1,21 @@ + + +
      + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-menu.svelte b/packages/ui/src/components/sidebar/sidebar-menu.svelte new file mode 100644 index 0000000..5b82a5c --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-menu.svelte @@ -0,0 +1,21 @@ + + +
      + {@render children?.()} +
    diff --git a/packages/ui/src/components/sidebar/sidebar-provider.svelte b/packages/ui/src/components/sidebar/sidebar-provider.svelte new file mode 100644 index 0000000..ec41be9 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-provider.svelte @@ -0,0 +1,53 @@ + + + + + +
    + {@render children?.()} +
    +
    diff --git a/packages/ui/src/components/sidebar/sidebar-rail.svelte b/packages/ui/src/components/sidebar/sidebar-rail.svelte new file mode 100644 index 0000000..56f8692 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-rail.svelte @@ -0,0 +1,36 @@ + + + diff --git a/packages/ui/src/components/sidebar/sidebar-separator.svelte b/packages/ui/src/components/sidebar/sidebar-separator.svelte new file mode 100644 index 0000000..368f261 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-separator.svelte @@ -0,0 +1,19 @@ + + + diff --git a/packages/ui/src/components/sidebar/sidebar-trigger.svelte b/packages/ui/src/components/sidebar/sidebar-trigger.svelte new file mode 100644 index 0000000..48d68df --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar-trigger.svelte @@ -0,0 +1,36 @@ + + + diff --git a/packages/ui/src/components/sidebar/sidebar.svelte b/packages/ui/src/components/sidebar/sidebar.svelte new file mode 100644 index 0000000..0c3dcc2 --- /dev/null +++ b/packages/ui/src/components/sidebar/sidebar.svelte @@ -0,0 +1,108 @@ + + +{#if collapsible === "none"} +
    + {@render children?.()} +
    +{:else if sidebar.isMobile} + sidebar.openMobile, (v) => sidebar.setOpenMobile(v)} + {...restProps} + > + button]:hidden", + className + )} + style="--sidebar-width: {SIDEBAR_WIDTH_MOBILE};" + {side} + > + + Sidebar + Displays the mobile sidebar. + +
    + {@render children?.()} +
    +
    +
    +{:else} + +{/if} diff --git a/web/src/lib/ui/core/skeleton/index.ts b/packages/ui/src/components/skeleton/index.ts similarity index 100% rename from web/src/lib/ui/core/skeleton/index.ts rename to packages/ui/src/components/skeleton/index.ts diff --git a/web/src/lib/ui/core/skeleton/skeleton.svelte b/packages/ui/src/components/skeleton/skeleton.svelte similarity index 94% rename from web/src/lib/ui/core/skeleton/skeleton.svelte rename to packages/ui/src/components/skeleton/skeleton.svelte index e2b514b..2106845 100644 --- a/web/src/lib/ui/core/skeleton/skeleton.svelte +++ b/packages/ui/src/components/skeleton/skeleton.svelte @@ -1,5 +1,5 @@ - - -
    - -
    -
    - - EvoBGP -
    -
    - {@render children()} -
    -
    -
    -
    diff --git a/web/src/lib/ui/app/layout/app-sidebar.svelte b/web/src/lib/ui/app/layout/app-sidebar.svelte deleted file mode 100644 index d3ca165..0000000 --- a/web/src/lib/ui/app/layout/app-sidebar.svelte +++ /dev/null @@ -1,59 +0,0 @@ - - -