@goplusvn/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/assets/erp_wallpaper.png +0 -0
- package/src/assets/goeat_logo.png +0 -0
- package/src/audit/audit-manager.ts +139 -0
- package/src/audit/index.ts +11 -0
- package/src/audit/memory-audit-logger.ts +86 -0
- package/src/audit/types.ts +50 -0
- package/src/auth/auth-service.ts +97 -0
- package/src/auth/index.ts +266 -0
- package/src/code-generation/index.ts +69 -0
- package/src/configs/auth-routes.ts +17 -0
- package/src/configs/crud.ts +136 -0
- package/src/configs/data/navigations.ts +781 -0
- package/src/configs/data/oauth-links.ts +10 -0
- package/src/configs/entities/material-categories.config.ts +125 -0
- package/src/configs/i18n.ts +12 -0
- package/src/configs/index.ts +26 -0
- package/src/configs/status.ts +25 -0
- package/src/configs/themes.ts +100 -0
- package/src/crud/components/crud-bulk-actions.tsx +91 -0
- package/src/crud/components/crud-card-view.tsx +241 -0
- package/src/crud/components/crud-context.tsx +122 -0
- package/src/crud/components/crud-delete-dialog.tsx +145 -0
- package/src/crud/components/crud-dialog.tsx +406 -0
- package/src/crud/components/crud-empty-state.tsx +104 -0
- package/src/crud/components/crud-export-button.tsx +170 -0
- package/src/crud/components/crud-field-renderer.tsx +653 -0
- package/src/crud/components/crud-filter-chips.tsx +102 -0
- package/src/crud/components/crud-filters/checkbox-filter.tsx +97 -0
- package/src/crud/components/crud-filters/datetime-filter.tsx +83 -0
- package/src/crud/components/crud-filters/filter-builder.tsx +66 -0
- package/src/crud/components/crud-filters/index.tsx +76 -0
- package/src/crud/components/crud-filters/radio-filter.tsx +86 -0
- package/src/crud/components/crud-filters/select-filter.tsx +141 -0
- package/src/crud/components/crud-filters/text-filter.tsx +86 -0
- package/src/crud/components/crud-form.tsx +642 -0
- package/src/crud/components/crud-import-dialog.tsx +440 -0
- package/src/crud/components/crud-infinite-scroll.tsx +116 -0
- package/src/crud/components/crud-page.tsx +1017 -0
- package/src/crud/components/crud-provider.tsx +277 -0
- package/src/crud/components/crud-row-actions.tsx +189 -0
- package/src/crud/components/crud-search.tsx +82 -0
- package/src/crud/components/crud-sheet.tsx +336 -0
- package/src/crud/components/crud-table-skeleton.tsx +26 -0
- package/src/crud/components/crud-table-toolbar.tsx +91 -0
- package/src/crud/components/crud-table.tsx +352 -0
- package/src/crud/components/crud-virtual-table.tsx +55 -0
- package/src/crud/components/index.tsx +20 -0
- package/src/crud/crud-filters/checkbox-filter.tsx +87 -0
- package/src/crud/crud-filters/datetime-filter.tsx +82 -0
- package/src/crud/crud-filters/filter-builder.tsx +64 -0
- package/src/crud/crud-filters/index.tsx +78 -0
- package/src/crud/crud-filters/radio-filter.tsx +79 -0
- package/src/crud/crud-filters/select-filter.tsx +148 -0
- package/src/crud/crud-filters/text-filter.tsx +81 -0
- package/src/crud/index.ts +43 -0
- package/src/crud/lib/crud-service.test.ts +334 -0
- package/src/crud/lib/crud-service.ts +358 -0
- package/src/crud/lib/crud-utils.test.ts +354 -0
- package/src/crud/lib/crud-utils.ts +299 -0
- package/src/crud/lib/crud-validator.ts +247 -0
- package/src/crud/lib/data-loader.ts +234 -0
- package/src/crud/lib/field-calculator.ts +241 -0
- package/src/crud/lib/field-formatter.ts +240 -0
- package/src/crud/lib/import-export-service.test.ts +290 -0
- package/src/crud/lib/import-export-service.ts +352 -0
- package/src/crud/lib/import-server-utils.ts +109 -0
- package/src/crud/lib/lazy-loader.ts +241 -0
- package/src/crud/lib/parse-filters.ts +85 -0
- package/src/crud/lib/permissions.ts +52 -0
- package/src/crud/lib/serialize-config.ts +60 -0
- package/src/crud/lib/stream-loader.ts +145 -0
- package/src/crud/lib/translate-config.ts +335 -0
- package/src/crud/lib/types.ts +11 -0
- package/src/crud/pages/entity-crud-page.tsx +144 -0
- package/src/crud/server.ts +8 -0
- package/src/home/constants.tsx +142 -0
- package/src/home/feature-showcase.tsx +171 -0
- package/src/home/home-page.tsx +191 -0
- package/src/home/hooks/index.ts +1 -0
- package/src/home/hooks/useWidgetPreferences.ts +167 -0
- package/src/home/index.ts +33 -0
- package/src/home/quick-access-dialog.tsx +271 -0
- package/src/home/quick-access-menu.tsx +267 -0
- package/src/home/types.ts +140 -0
- package/src/home/welcome-card.tsx +92 -0
- package/src/home/widget-container.tsx +258 -0
- package/src/home/widgets/base-widget.tsx +200 -0
- package/src/home/widgets/customers-widget.tsx +74 -0
- package/src/home/widgets/index.ts +6 -0
- package/src/home/widgets/orders-widget.tsx +87 -0
- package/src/home/widgets/revenue-widget.tsx +71 -0
- package/src/home/widgets/stock-widget.tsx +109 -0
- package/src/hooks/index.tsx +598 -0
- package/src/hooks/use-tenant.test.tsx +30 -0
- package/src/hooks/use-tenant.ts +5 -0
- package/src/index.ts +17 -0
- package/src/infrastructure/__tests__/architecture-verification.spec.ts +103 -0
- package/src/infrastructure/api-service.ts +317 -0
- package/src/infrastructure/cache/cache-manager.ts +107 -0
- package/src/infrastructure/cache/cache.ts +120 -0
- package/src/infrastructure/cache/index.ts +8 -0
- package/src/infrastructure/cache/types.ts +48 -0
- package/src/infrastructure/cron/cron-manager.ts +239 -0
- package/src/infrastructure/cron/index.ts +6 -0
- package/src/infrastructure/cron/types.ts +41 -0
- package/src/infrastructure/event-bus/event-bus.ts +145 -0
- package/src/infrastructure/event-bus/index.ts +2 -0
- package/src/infrastructure/event-bus/types.ts +22 -0
- package/src/infrastructure/index.ts +32 -0
- package/src/infrastructure/lock/decorators.ts +67 -0
- package/src/infrastructure/lock/index.ts +2 -0
- package/src/infrastructure/lock/lock-manager.ts +33 -0
- package/src/infrastructure/logger/index.ts +2 -0
- package/src/infrastructure/logger/logger.ts +96 -0
- package/src/infrastructure/logger/types.ts +25 -0
- package/src/layout/index.tsx +185 -0
- package/src/navigation/index.ts +91 -0
- package/src/notification/index.ts +14 -0
- package/src/notification/notification-service.ts +120 -0
- package/src/notification/storage/in-memory.ts +56 -0
- package/src/notification/storage/index.ts +1 -0
- package/src/notification/types.ts +51 -0
- package/src/organization/branch-service.ts +299 -0
- package/src/organization/branches.config.ts +154 -0
- package/src/organization/index.ts +5 -0
- package/src/plugin/apps-registry.ts +97 -0
- package/src/plugin/index.ts +5 -0
- package/src/plugin/types.ts +41 -0
- package/src/providers/index.tsx +109 -0
- package/src/providers/tenant-provider.tsx +45 -0
- package/src/rbac/components/roles/role-card.tsx +158 -0
- package/src/rbac/components/roles/role-stats-cards.tsx +29 -0
- package/src/rbac/components/roles/role-toolbar.tsx +123 -0
- package/src/rbac/hooks/use-role-operations.ts +159 -0
- package/src/rbac/hooks/use-roles-data.ts +59 -0
- package/src/rbac/index.ts +297 -0
- package/src/rbac/lib/permission-helpers.ts +63 -0
- package/src/rbac/pages/action-list-page.tsx +25 -0
- package/src/rbac/pages/resource-list-page.tsx +25 -0
- package/src/rbac/pages/role-list-page.tsx +378 -0
- package/src/rbac/permission-service.ts +140 -0
- package/src/rbac/permissions.ts +135 -0
- package/src/rbac/resource-service.ts +115 -0
- package/src/rbac/resource-validator.ts +119 -0
- package/src/rbac/role-service.ts +165 -0
- package/src/rbac/server.ts +16 -0
- package/src/rbac/types.ts +38 -0
- package/src/schemas/action.schema.ts +66 -0
- package/src/schemas/branch.schema.ts +52 -0
- package/src/schemas/coming-soon-schema.ts +9 -0
- package/src/schemas/company.schema.ts +44 -0
- package/src/schemas/forgot-passward-schema.ts +9 -0
- package/src/schemas/index.ts +30 -0
- package/src/schemas/material-category.schema.ts +43 -0
- package/src/schemas/material-pricing.schema.ts +74 -0
- package/src/schemas/material.schema.ts +76 -0
- package/src/schemas/materials.ts +52 -0
- package/src/schemas/new-passward-schema.ts +15 -0
- package/src/schemas/partner-company.schema.ts +149 -0
- package/src/schemas/register-schema.ts +36 -0
- package/src/schemas/resource.schema.ts +133 -0
- package/src/schemas/role.schema.ts +11 -0
- package/src/schemas/sign-in-schema.ts +24 -0
- package/src/schemas/supplier-pricing.schema.ts +15 -0
- package/src/schemas/supplier.schema.ts +120 -0
- package/src/schemas/system-category-group.schema.ts +67 -0
- package/src/schemas/system-category.schema.ts +77 -0
- package/src/schemas/system-config.schema.ts +118 -0
- package/src/schemas/uom.schema.ts +75 -0
- package/src/schemas/user-supplier.schema.ts +179 -0
- package/src/schemas/user.schema.ts +18 -0
- package/src/schemas/verify-email-schema.ts +9 -0
- package/src/schemas/warehouse.schema.ts +49 -0
- package/src/system/components/categories/category-list.tsx +529 -0
- package/src/system/components/categories/category-manager.tsx +89 -0
- package/src/system/components/categories/group-sidebar.tsx +308 -0
- package/src/system/components/settings/setting-dialogs.tsx +197 -0
- package/src/system/components/settings/setting-field.tsx +291 -0
- package/src/system/components/settings/setting-form-dialog.tsx +308 -0
- package/src/system/components/settings/settings-groups.ts +80 -0
- package/src/system/components/settings/settings-search.tsx +71 -0
- package/src/system/components/settings/settings-section.tsx +74 -0
- package/src/system/components/settings/settings-sidebar.tsx +81 -0
- package/src/system/constants.ts +3 -0
- package/src/system/index.ts +150 -0
- package/src/system/job-manager.ts +176 -0
- package/src/system/pages/components/categories/category-list.tsx +537 -0
- package/src/system/pages/components/categories/category-manager.tsx +90 -0
- package/src/system/pages/components/categories/group-sidebar.tsx +311 -0
- package/src/system/pages/components/settings/sales-rules-settings.tsx +222 -0
- package/src/system/pages/components/settings/setting-dialogs.tsx +197 -0
- package/src/system/pages/components/settings/setting-field.tsx +292 -0
- package/src/system/pages/components/settings/setting-form-dialog.tsx +308 -0
- package/src/system/pages/components/settings/settings-groups.ts +87 -0
- package/src/system/pages/components/settings/settings-page.tsx +372 -0
- package/src/system/pages/components/settings/settings-search.tsx +71 -0
- package/src/system/pages/components/settings/settings-section.tsx +74 -0
- package/src/system/pages/components/settings/settings-sidebar.tsx +81 -0
- package/src/system/pages/components/settings/system-settings.tsx +244 -0
- package/src/system/pages/system-category-page.tsx +15 -0
- package/src/system/pages/system-settings-page.tsx +380 -0
- package/src/system/schemas/system-category-group.schema.ts +46 -0
- package/src/system/schemas/system-category.schema.ts +56 -0
- package/src/system/services/settings-service.ts +127 -0
- package/src/system/services/system-category-service.ts +63 -0
- package/src/system/types.ts +45 -0
- package/src/types/index.ts +703 -0
- package/src/ui/auth/auth-layout.tsx +135 -0
- package/src/ui/auth/forgot-password-form.tsx +98 -0
- package/src/ui/auth/index.tsx +7 -0
- package/src/ui/auth/new-password-form.tsx +107 -0
- package/src/ui/auth/oauth-links.tsx +30 -0
- package/src/ui/auth/register-form.tsx +202 -0
- package/src/ui/auth/sign-in-form.tsx +238 -0
- package/src/ui/auth/verify-email-form.tsx +104 -0
- package/src/ui/crud/index.tsx +10 -0
- package/src/ui/data-display/accordion.tsx +65 -0
- package/src/ui/data-display/aspect-ratio.tsx +11 -0
- package/src/ui/data-display/avatar.tsx +163 -0
- package/src/ui/data-display/bento-grid.tsx +77 -0
- package/src/ui/data-display/carousel.tsx +249 -0
- package/src/ui/data-display/chart.tsx +363 -0
- package/src/ui/data-display/code-block-highlight.tsx +54 -0
- package/src/ui/data-display/collapsible.tsx +42 -0
- package/src/ui/data-display/compact-stat-bar.tsx +149 -0
- package/src/ui/data-display/data-table/data-table-context.tsx +255 -0
- package/src/ui/data-display/data-table/data-table-empty-state.tsx +133 -0
- package/src/ui/data-display/data-table/data-table-skeleton.tsx +145 -0
- package/src/ui/data-display/data-table/data-table-toolbar.tsx +353 -0
- package/src/ui/data-display/data-table/data-table.tsx +597 -0
- package/src/ui/data-display/data-table/index.ts +44 -0
- package/src/ui/data-display/data-table-column-header.tsx +75 -0
- package/src/ui/data-display/data-table-pagination.tsx +130 -0
- package/src/ui/data-display/data-table-view-options.tsx +59 -0
- package/src/ui/data-display/formatted-number-input.tsx +210 -0
- package/src/ui/data-display/highlight.tsx +20 -0
- package/src/ui/data-display/hover-card.tsx +48 -0
- package/src/ui/data-display/index.tsx +50 -0
- package/src/ui/data-display/iphone-15-pro.tsx +114 -0
- package/src/ui/data-display/kanban/index.ts +4 -0
- package/src/ui/data-display/kanban/kanban-board.tsx +192 -0
- package/src/ui/data-display/kanban/kanban-column.tsx +74 -0
- package/src/ui/data-display/kanban/kanban-item.tsx +50 -0
- package/src/ui/data-display/kanban/kanban-types.ts +21 -0
- package/src/ui/data-display/kpi-card.tsx +68 -0
- package/src/ui/data-display/media-grid.tsx +110 -0
- package/src/ui/data-display/safari.tsx +175 -0
- package/src/ui/data-display/show-more-text.tsx +55 -0
- package/src/ui/data-display/tabs.tsx +68 -0
- package/src/ui/data-display/timeline.tsx +256 -0
- package/src/ui/feedback/alert.tsx +60 -0
- package/src/ui/feedback/context-menu.tsx +245 -0
- package/src/ui/feedback/drawer.tsx +132 -0
- package/src/ui/feedback/error-dialog.tsx +273 -0
- package/src/ui/feedback/index.tsx +183 -0
- package/src/ui/feedback/progress.tsx +32 -0
- package/src/ui/feedback/sheet.tsx +148 -0
- package/src/ui/feedback/sonner.tsx +36 -0
- package/src/ui/forms/command.tsx +157 -0
- package/src/ui/forms/date-picker.tsx +73 -0
- package/src/ui/forms/date-range-picker.tsx +76 -0
- package/src/ui/forms/date-time-picker.tsx +109 -0
- package/src/ui/forms/editor/editor-menu-bar.tsx +394 -0
- package/src/ui/forms/editor/index.tsx +130 -0
- package/src/ui/forms/editor/multi-select-example.tsx +1234 -0
- package/src/ui/forms/emoji-picker.tsx +109 -0
- package/src/ui/forms/file-dropzone.tsx +169 -0
- package/src/ui/forms/file-thumbnail.tsx +29 -0
- package/src/ui/forms/index.tsx +201 -0
- package/src/ui/forms/input-file.tsx +99 -0
- package/src/ui/forms/input-group.tsx +46 -0
- package/src/ui/forms/input-otp.tsx +81 -0
- package/src/ui/forms/input-phone.tsx +172 -0
- package/src/ui/forms/input-spin.tsx +116 -0
- package/src/ui/forms/input-tags.tsx +219 -0
- package/src/ui/forms/input-time.tsx +42 -0
- package/src/ui/forms/multi-select.tsx +629 -0
- package/src/ui/forms/multiple-date-picker.tsx +74 -0
- package/src/ui/forms/radio-group.tsx +42 -0
- package/src/ui/forms/rating.tsx +158 -0
- package/src/ui/forms/time-picker.tsx +57 -0
- package/src/ui/index.tsx +17 -0
- package/src/ui/layout/animated-list.tsx +77 -0
- package/src/ui/layout/animated-sidebar.tsx +294 -0
- package/src/ui/layout/command-menu.tsx +355 -0
- package/src/ui/layout/customizer.tsx +324 -0
- package/src/ui/layout/footer.tsx +43 -0
- package/src/ui/layout/full-screen-toggle.tsx +52 -0
- package/src/ui/layout/header-breadcrumb.tsx +77 -0
- package/src/ui/layout/horizontal-layout-header.tsx +83 -0
- package/src/ui/layout/horizontal-layout.tsx +50 -0
- package/src/ui/layout/index.tsx +25 -0
- package/src/ui/layout/language-dropdown.tsx +103 -0
- package/src/ui/layout/logo.tsx +63 -0
- package/src/ui/layout/main-layout.tsx +57 -0
- package/src/ui/layout/mode-dropdown.tsx +58 -0
- package/src/ui/layout/notification-dropdown.tsx +127 -0
- package/src/ui/layout/page-tabs.tsx +306 -0
- package/src/ui/layout/route-cache.tsx +214 -0
- package/src/ui/layout/sidebar-group-icon-menu.tsx +195 -0
- package/src/ui/layout/sidebar.tsx +279 -0
- package/src/ui/layout/tab-content-cache.tsx +201 -0
- package/src/ui/layout/tab-navigation-provider.tsx +536 -0
- package/src/ui/layout/toggle-mobile-sidebar.tsx +33 -0
- package/src/ui/layout/top-bar-header-menubar.tsx +412 -0
- package/src/ui/layout/user-dropdown.tsx +188 -0
- package/src/ui/layout/vertical-layout-header.tsx +65 -0
- package/src/ui/layout/vertical-layout.tsx +47 -0
- package/src/ui/management/audit-log-page.tsx +209 -0
- package/src/ui/management/cache-management.tsx +349 -0
- package/src/ui/management/index.ts +3 -0
- package/src/ui/management/job-management.tsx +308 -0
- package/src/ui/pages/not-found.tsx +30 -0
- package/src/ui/primitives/badge.tsx +66 -0
- package/src/ui/primitives/breadcrumb.tsx +103 -0
- package/src/ui/primitives/button.tsx +129 -0
- package/src/ui/primitives/calendar.tsx +74 -0
- package/src/ui/primitives/card.tsx +86 -0
- package/src/ui/primitives/checkbox.tsx +31 -0
- package/src/ui/primitives/client.ts +30 -0
- package/src/ui/primitives/combobox.tsx +290 -0
- package/src/ui/primitives/dialog.tsx +121 -0
- package/src/ui/primitives/dropdown-menu.tsx +239 -0
- package/src/ui/primitives/dynamic-icon.tsx +24 -0
- package/src/ui/primitives/index.tsx +134 -0
- package/src/ui/primitives/input-number.tsx +131 -0
- package/src/ui/primitives/input.tsx +22 -0
- package/src/ui/primitives/keyboard.tsx +23 -0
- package/src/ui/primitives/label.tsx +24 -0
- package/src/ui/primitives/menubar.tsx +262 -0
- package/src/ui/primitives/navigation-menu.tsx +157 -0
- package/src/ui/primitives/pagination.tsx +118 -0
- package/src/ui/primitives/popover.tsx +56 -0
- package/src/ui/primitives/prefetch-link.tsx +60 -0
- package/src/ui/primitives/resizable.tsx +59 -0
- package/src/ui/primitives/scroll-area.tsx +63 -0
- package/src/ui/primitives/select.tsx +172 -0
- package/src/ui/primitives/separator.tsx +51 -0
- package/src/ui/primitives/sidebar.tsx +844 -0
- package/src/ui/primitives/slider.tsx +27 -0
- package/src/ui/primitives/status-badge.tsx +47 -0
- package/src/ui/primitives/sticky-layout.tsx +50 -0
- package/src/ui/primitives/switch.tsx +29 -0
- package/src/ui/primitives/table.tsx +116 -0
- package/src/ui/primitives/tabs.tsx +55 -0
- package/src/ui/primitives/toggle-group.tsx +70 -0
- package/src/ui/primitives/toggle.tsx +47 -0
- package/src/ui/primitives/tooltip.tsx +59 -0
- package/src/user/components/dangerous-zone.tsx +34 -0
- package/src/user/components/delete-account-form.tsx +40 -0
- package/src/user/components/index.ts +4 -0
- package/src/user/components/profile-info-form.tsx +390 -0
- package/src/user/components/profile-info.tsx +32 -0
- package/src/user/components/unified-profile-dialog.tsx +1019 -0
- package/src/user/components/user-stats.tsx +27 -0
- package/src/user/components/user-toolbar.tsx +137 -0
- package/src/user/components/users-card-view.tsx +253 -0
- package/src/user/index.ts +11 -0
- package/src/user/pages/user-list-page.tsx +234 -0
- package/src/user/pages/users-client-page.tsx +385 -0
- package/src/user/profile-page.tsx +19 -0
- package/src/user/schemas.ts +68 -0
- package/src/user/types.ts +34 -0
- package/src/user/user-service.ts +538 -0
- package/src/utils/index.ts +906 -0
- package/src/workflow/activity-timeline.tsx +412 -0
- package/src/workflow/approval-workflow.tsx +31 -0
- package/src/workflow/index.ts +2 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { DictionaryType } from "../../hooks";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
import type { NavigationType } from "../../types";
|
|
6
|
+
|
|
7
|
+
import { SidebarProvider, SidebarInset } from "../primitives/sidebar";
|
|
8
|
+
import { Footer } from "./footer";
|
|
9
|
+
import { AppSidebar } from "./sidebar"; // Use renamed AppSidebar
|
|
10
|
+
import { VerticalLayoutHeader } from "./vertical-layout-header";
|
|
11
|
+
|
|
12
|
+
interface VerticalLayoutProps {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
dictionary: DictionaryType;
|
|
15
|
+
navigation?: NavigationType[];
|
|
16
|
+
onGlobalSearch?: (query: string) => void;
|
|
17
|
+
searchResults?: any[];
|
|
18
|
+
searchLoading?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function VerticalLayout({
|
|
22
|
+
children,
|
|
23
|
+
dictionary,
|
|
24
|
+
navigation,
|
|
25
|
+
onGlobalSearch,
|
|
26
|
+
searchResults,
|
|
27
|
+
searchLoading,
|
|
28
|
+
}: VerticalLayoutProps) {
|
|
29
|
+
return (
|
|
30
|
+
<SidebarProvider>
|
|
31
|
+
<AppSidebar
|
|
32
|
+
dictionary={dictionary}
|
|
33
|
+
navigation={navigation ?? []}
|
|
34
|
+
onGlobalSearch={onGlobalSearch}
|
|
35
|
+
searchResults={searchResults}
|
|
36
|
+
searchLoading={searchLoading}
|
|
37
|
+
/>
|
|
38
|
+
<SidebarInset className="w-full min-w-0 overflow-hidden h-screen flex flex-col">
|
|
39
|
+
<VerticalLayoutHeader dictionary={dictionary} />
|
|
40
|
+
<main className="w-full mx-auto flex-1 min-h-0 px-4 md:px-8 py-4 overflow-auto">
|
|
41
|
+
{children}
|
|
42
|
+
</main>
|
|
43
|
+
<Footer />
|
|
44
|
+
</SidebarInset>
|
|
45
|
+
</SidebarProvider>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from "react";
|
|
4
|
+
import { auditManager } from "../../audit/audit-manager";
|
|
5
|
+
import type { AuditLog } from "../../audit/types";
|
|
6
|
+
|
|
7
|
+
// Relative imports to core UI components
|
|
8
|
+
import { Badge } from "../primitives/badge";
|
|
9
|
+
import { Button } from "../primitives/button";
|
|
10
|
+
import { Card, CardHeader, CardTitle, CardContent } from "../primitives/card";
|
|
11
|
+
import { Input } from "../primitives/input";
|
|
12
|
+
import { Label } from "../primitives/label";
|
|
13
|
+
import {
|
|
14
|
+
Table,
|
|
15
|
+
TableBody,
|
|
16
|
+
TableCell,
|
|
17
|
+
TableHead,
|
|
18
|
+
TableHeader,
|
|
19
|
+
TableRow,
|
|
20
|
+
} from "../primitives/table";
|
|
21
|
+
import {
|
|
22
|
+
Select,
|
|
23
|
+
SelectContent,
|
|
24
|
+
SelectItem,
|
|
25
|
+
SelectTrigger,
|
|
26
|
+
SelectValue,
|
|
27
|
+
} from "../primitives/select";
|
|
28
|
+
|
|
29
|
+
export const AuditLogPage = () => {
|
|
30
|
+
const [logs, setLogs] = useState<AuditLog[]>([]);
|
|
31
|
+
const [userIdFilter, setUserIdFilter] = useState("");
|
|
32
|
+
const [actionFilter, setActionFilter] = useState("");
|
|
33
|
+
const [resourceFilter, setResourceFilter] = useState("");
|
|
34
|
+
const [typeFilter, setTypeFilter] = useState<string>("all");
|
|
35
|
+
|
|
36
|
+
const fetchLogs = () => {
|
|
37
|
+
// Safe cast as we know we added getLogs
|
|
38
|
+
// In real app, we would have proper interface
|
|
39
|
+
const manager = auditManager as any;
|
|
40
|
+
if (typeof manager.getLogs === "function") {
|
|
41
|
+
const filter: any = {};
|
|
42
|
+
if (userIdFilter) filter.userId = userIdFilter;
|
|
43
|
+
if (actionFilter) filter.action = actionFilter;
|
|
44
|
+
if (resourceFilter) filter.resource = resourceFilter;
|
|
45
|
+
if (typeFilter && typeFilter !== "all") filter.type = typeFilter;
|
|
46
|
+
|
|
47
|
+
setLogs(manager.getLogs(filter));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
fetchLogs();
|
|
53
|
+
// Poll for updates in demo
|
|
54
|
+
const interval = setInterval(fetchLogs, 5000);
|
|
55
|
+
return () => clearInterval(interval);
|
|
56
|
+
}, [userIdFilter, actionFilter, resourceFilter, typeFilter]);
|
|
57
|
+
|
|
58
|
+
// Format changes object for display
|
|
59
|
+
const renderChanges = (changes: any) => {
|
|
60
|
+
if (!changes) return <span className="text-muted-foreground">-</span>;
|
|
61
|
+
return (
|
|
62
|
+
<code
|
|
63
|
+
className="text-xs bg-slate-100 dark:bg-slate-800 p-1 rounded block max-w-[300px] overflow-hidden text-ellipsis whitespace-nowrap"
|
|
64
|
+
title={JSON.stringify(changes, null, 2)}
|
|
65
|
+
>
|
|
66
|
+
{Object.keys(changes).join(", ")}
|
|
67
|
+
</code>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className="p-6 space-y-6 h-full flex flex-col">
|
|
73
|
+
<div className="flex justify-between items-center mb-4">
|
|
74
|
+
<div>
|
|
75
|
+
<h1 className="text-2xl font-bold tracking-tight">Audit Logs</h1>
|
|
76
|
+
<p className="text-muted-foreground text-sm">
|
|
77
|
+
Track system changes and user actions.
|
|
78
|
+
</p>
|
|
79
|
+
</div>
|
|
80
|
+
<Button onClick={fetchLogs} variant="outline">
|
|
81
|
+
Refresh
|
|
82
|
+
</Button>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<Card>
|
|
86
|
+
<CardHeader>
|
|
87
|
+
<CardTitle className="text-base font-medium">Filters</CardTitle>
|
|
88
|
+
</CardHeader>
|
|
89
|
+
<CardContent>
|
|
90
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
|
|
91
|
+
<div className="space-y-1">
|
|
92
|
+
<Label htmlFor="user-filter">User ID</Label>
|
|
93
|
+
<Input
|
|
94
|
+
id="user-filter"
|
|
95
|
+
placeholder="Search by user..."
|
|
96
|
+
value={userIdFilter}
|
|
97
|
+
onChange={(e) => setUserIdFilter(e.target.value)}
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
<div className="space-y-1">
|
|
101
|
+
<Label htmlFor="action-filter">Action</Label>
|
|
102
|
+
<Input
|
|
103
|
+
id="action-filter"
|
|
104
|
+
placeholder="e.g. create, update"
|
|
105
|
+
value={actionFilter}
|
|
106
|
+
onChange={(e) => setActionFilter(e.target.value)}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="space-y-1">
|
|
110
|
+
<Label htmlFor="resource-filter">Resource</Label>
|
|
111
|
+
<Input
|
|
112
|
+
id="resource-filter"
|
|
113
|
+
placeholder="e.g. product, order"
|
|
114
|
+
value={resourceFilter}
|
|
115
|
+
onChange={(e) => setResourceFilter(e.target.value)}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
<div className="space-y-1">
|
|
119
|
+
<Label htmlFor="type-filter">Type</Label>
|
|
120
|
+
<Select value={typeFilter} onValueChange={setTypeFilter}>
|
|
121
|
+
<SelectTrigger>
|
|
122
|
+
<SelectValue placeholder="All Types" />
|
|
123
|
+
</SelectTrigger>
|
|
124
|
+
<SelectContent>
|
|
125
|
+
<SelectItem value="all">All Types</SelectItem>
|
|
126
|
+
<SelectItem value="info">Info</SelectItem>
|
|
127
|
+
<SelectItem value="warning">Warning</SelectItem>
|
|
128
|
+
<SelectItem value="error">Error</SelectItem>
|
|
129
|
+
</SelectContent>
|
|
130
|
+
</Select>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</CardContent>
|
|
134
|
+
</Card>
|
|
135
|
+
|
|
136
|
+
<Card className="flex-1 flex flex-col min-h-0">
|
|
137
|
+
<CardContent className="flex-1 overflow-auto p-0">
|
|
138
|
+
<Table>
|
|
139
|
+
<TableHeader>
|
|
140
|
+
<TableRow>
|
|
141
|
+
<TableHead className="w-[180px]">Timestamp</TableHead>
|
|
142
|
+
<TableHead className="w-[150px]">Action</TableHead>
|
|
143
|
+
<TableHead className="w-[150px]">Resource</TableHead>
|
|
144
|
+
<TableHead className="w-[150px]">User</TableHead>
|
|
145
|
+
<TableHead>Changes / Metadata</TableHead>
|
|
146
|
+
<TableHead className="w-[100px] text-right">Status</TableHead>
|
|
147
|
+
</TableRow>
|
|
148
|
+
</TableHeader>
|
|
149
|
+
<TableBody>
|
|
150
|
+
{logs.length === 0 && (
|
|
151
|
+
<TableRow>
|
|
152
|
+
<TableCell
|
|
153
|
+
colSpan={6}
|
|
154
|
+
className="text-center h-24 text-muted-foreground"
|
|
155
|
+
>
|
|
156
|
+
No logs found matching filters.
|
|
157
|
+
</TableCell>
|
|
158
|
+
</TableRow>
|
|
159
|
+
)}
|
|
160
|
+
{logs.map((log) => (
|
|
161
|
+
<TableRow key={log.id}>
|
|
162
|
+
<TableCell className="font-mono text-xs">
|
|
163
|
+
{new Date(log.createdAt).toLocaleString()}
|
|
164
|
+
</TableCell>
|
|
165
|
+
<TableCell>
|
|
166
|
+
<Badge variant="outline" className="font-normal">
|
|
167
|
+
{log.action}
|
|
168
|
+
</Badge>
|
|
169
|
+
</TableCell>
|
|
170
|
+
<TableCell>{log.resource}</TableCell>
|
|
171
|
+
<TableCell>
|
|
172
|
+
<div className="flex flex-col">
|
|
173
|
+
<span>{log.userId || "System"}</span>
|
|
174
|
+
{log.roleName && (
|
|
175
|
+
<span className="text-[10px] text-muted-foreground">
|
|
176
|
+
{log.roleName}
|
|
177
|
+
</span>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
</TableCell>
|
|
181
|
+
<TableCell>
|
|
182
|
+
{renderChanges(log.changes)}
|
|
183
|
+
{log.metadata && Object.keys(log.metadata).length > 0 && (
|
|
184
|
+
<div className="text-[10px] text-muted-foreground mt-1">
|
|
185
|
+
MD: {JSON.stringify(log.metadata).substring(0, 50)}...
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
188
|
+
</TableCell>
|
|
189
|
+
<TableCell className="text-right">
|
|
190
|
+
{(log.status || 200) >= 400 ? (
|
|
191
|
+
<Badge variant="destructive">{log.status}</Badge>
|
|
192
|
+
) : (
|
|
193
|
+
<Badge
|
|
194
|
+
variant="secondary"
|
|
195
|
+
className="bg-emerald-100 text-emerald-700 hover:bg-emerald-100"
|
|
196
|
+
>
|
|
197
|
+
{log.status || 200}
|
|
198
|
+
</Badge>
|
|
199
|
+
)}
|
|
200
|
+
</TableCell>
|
|
201
|
+
</TableRow>
|
|
202
|
+
))}
|
|
203
|
+
</TableBody>
|
|
204
|
+
</Table>
|
|
205
|
+
</CardContent>
|
|
206
|
+
</Card>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from "react";
|
|
4
|
+
// Remove direct import of server-side cacheManager
|
|
5
|
+
// import { cacheManager } from "../../infrastructure/cache/cache-manager";
|
|
6
|
+
|
|
7
|
+
// Relative imports to core UI components
|
|
8
|
+
import { Badge } from "../primitives/badge";
|
|
9
|
+
import { Button } from "../primitives/button";
|
|
10
|
+
import { Card, CardHeader, CardTitle, CardContent } from "../primitives/card";
|
|
11
|
+
import { Input } from "../primitives/input";
|
|
12
|
+
import {
|
|
13
|
+
Table,
|
|
14
|
+
TableBody,
|
|
15
|
+
TableCell,
|
|
16
|
+
TableHead,
|
|
17
|
+
TableHeader,
|
|
18
|
+
TableRow,
|
|
19
|
+
} from "../primitives/table";
|
|
20
|
+
import {
|
|
21
|
+
Dialog,
|
|
22
|
+
DialogContent,
|
|
23
|
+
DialogHeader,
|
|
24
|
+
DialogTitle,
|
|
25
|
+
DialogDescription,
|
|
26
|
+
} from "../primitives/dialog";
|
|
27
|
+
import { ScrollArea } from "../primitives/scroll-area"; // Assume scroll-area exists or use div
|
|
28
|
+
|
|
29
|
+
interface CacheStat {
|
|
30
|
+
name: string;
|
|
31
|
+
itemCount: number;
|
|
32
|
+
ttl: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface CacheManagementPageProps {
|
|
36
|
+
/** Server action to fetch stats */
|
|
37
|
+
fetchStats?: () => Promise<CacheStat[]>;
|
|
38
|
+
/** Server action to clear specific cache */
|
|
39
|
+
clearCache?: (name: string) => Promise<boolean>;
|
|
40
|
+
/** Server action to clear all caches */
|
|
41
|
+
clearAll?: () => Promise<void>;
|
|
42
|
+
/** Server action to get keys of a cache */
|
|
43
|
+
getCacheKeys?: (name: string) => Promise<string[]>;
|
|
44
|
+
/** Server action to get value of a cache key */
|
|
45
|
+
getCacheValue?: (name: string, key: string) => Promise<any>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const CacheManagementPage = ({
|
|
49
|
+
fetchStats,
|
|
50
|
+
clearCache,
|
|
51
|
+
clearAll,
|
|
52
|
+
getCacheKeys,
|
|
53
|
+
getCacheValue,
|
|
54
|
+
}: CacheManagementPageProps) => {
|
|
55
|
+
const [caches, setCaches] = useState<CacheStat[]>([]);
|
|
56
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
57
|
+
const [refresh, setRefresh] = useState(0);
|
|
58
|
+
const [loading, setLoading] = useState(false);
|
|
59
|
+
|
|
60
|
+
// Inspection State
|
|
61
|
+
const [inspectCacheName, setInspectCacheName] = useState<string | null>(null);
|
|
62
|
+
const [cacheKeys, setCacheKeys] = useState<string[]>([]);
|
|
63
|
+
const [inspectKey, setInspectKey] = useState<string | null>(null);
|
|
64
|
+
const [keyValue, setKeyValue] = useState<any>(null);
|
|
65
|
+
const [inspecting, setInspecting] = useState(false);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
loadStats();
|
|
69
|
+
}, [refresh]);
|
|
70
|
+
|
|
71
|
+
const loadStats = async () => {
|
|
72
|
+
if (!fetchStats) return;
|
|
73
|
+
try {
|
|
74
|
+
setLoading(true);
|
|
75
|
+
const stats = await fetchStats();
|
|
76
|
+
setCaches(stats);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("Failed to load cache stats:", error);
|
|
79
|
+
} finally {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleClearAll = async () => {
|
|
85
|
+
if (!clearAll) return;
|
|
86
|
+
if (
|
|
87
|
+
confirm(
|
|
88
|
+
"Are you sure you want to clear ALL caches? Performance may be impacted.",
|
|
89
|
+
)
|
|
90
|
+
) {
|
|
91
|
+
try {
|
|
92
|
+
setLoading(true);
|
|
93
|
+
await clearAll();
|
|
94
|
+
setRefresh((r) => r + 1); // Trigger reload
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("Failed to clear all caches:", error);
|
|
97
|
+
} finally {
|
|
98
|
+
setLoading(false);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const handleClearCache = async (name: string) => {
|
|
104
|
+
if (!clearCache) return;
|
|
105
|
+
try {
|
|
106
|
+
setLoading(true);
|
|
107
|
+
await clearCache(name);
|
|
108
|
+
setRefresh((r) => r + 1); // Trigger reload
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(`Failed to clear cache ${name}:`, error);
|
|
111
|
+
} finally {
|
|
112
|
+
setLoading(false);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleInspect = async (name: string) => {
|
|
117
|
+
if (!getCacheKeys) return;
|
|
118
|
+
try {
|
|
119
|
+
setInspecting(true);
|
|
120
|
+
setInspectCacheName(name);
|
|
121
|
+
setCacheKeys([]);
|
|
122
|
+
setInspectKey(null);
|
|
123
|
+
|
|
124
|
+
const keys = await getCacheKeys(name);
|
|
125
|
+
setCacheKeys(keys);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(`Failed to inspect cache ${name}:`, error);
|
|
128
|
+
} finally {
|
|
129
|
+
setInspecting(false);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleViewValue = async (key: string) => {
|
|
134
|
+
if (!getCacheValue || !inspectCacheName) return;
|
|
135
|
+
try {
|
|
136
|
+
setInspecting(true);
|
|
137
|
+
const value = await getCacheValue(inspectCacheName, key);
|
|
138
|
+
setKeyValue(value);
|
|
139
|
+
setInspectKey(key);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`Failed to get value for ${key}:`, error);
|
|
142
|
+
} finally {
|
|
143
|
+
setInspecting(false);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const filteredCaches = caches.filter((c) =>
|
|
148
|
+
c.name.toLowerCase().includes(searchTerm.toLowerCase()),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const totalItems = caches.reduce((acc, curr) => acc + curr.itemCount, 0);
|
|
152
|
+
|
|
153
|
+
if (!fetchStats) {
|
|
154
|
+
return (
|
|
155
|
+
<div className="p-6 text-red-500">
|
|
156
|
+
Error: fetchStats server action not provided to CacheManagementPage.
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div className="p-6 space-y-6 h-full flex flex-col">
|
|
163
|
+
<div className="flex justify-between items-center mb-4">
|
|
164
|
+
<div>
|
|
165
|
+
<h1 className="text-2xl font-bold tracking-tight">
|
|
166
|
+
Cache Management
|
|
167
|
+
</h1>
|
|
168
|
+
<p className="text-muted-foreground text-sm">
|
|
169
|
+
Monitor cache usage and clear cached data.
|
|
170
|
+
</p>
|
|
171
|
+
</div>
|
|
172
|
+
<div className="flex gap-2">
|
|
173
|
+
<Button
|
|
174
|
+
onClick={() => setRefresh((r) => r + 1)}
|
|
175
|
+
variant="outline"
|
|
176
|
+
disabled={loading}
|
|
177
|
+
>
|
|
178
|
+
{loading ? "Loading..." : "Refresh"}
|
|
179
|
+
</Button>
|
|
180
|
+
<Button
|
|
181
|
+
onClick={handleClearAll}
|
|
182
|
+
variant="destructive"
|
|
183
|
+
disabled={!clearAll || loading}
|
|
184
|
+
>
|
|
185
|
+
Clear All Caches
|
|
186
|
+
</Button>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
|
191
|
+
<Card>
|
|
192
|
+
<CardHeader className="pb-2">
|
|
193
|
+
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
194
|
+
Total Caches
|
|
195
|
+
</CardTitle>
|
|
196
|
+
</CardHeader>
|
|
197
|
+
<CardContent>
|
|
198
|
+
<div className="text-2xl font-bold">{caches.length}</div>
|
|
199
|
+
</CardContent>
|
|
200
|
+
</Card>
|
|
201
|
+
<Card>
|
|
202
|
+
<CardHeader className="pb-2">
|
|
203
|
+
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
204
|
+
Total Cached Items
|
|
205
|
+
</CardTitle>
|
|
206
|
+
</CardHeader>
|
|
207
|
+
<CardContent>
|
|
208
|
+
<div className="text-2xl font-bold">{totalItems}</div>
|
|
209
|
+
</CardContent>
|
|
210
|
+
</Card>
|
|
211
|
+
<Card>
|
|
212
|
+
<CardHeader className="pb-2">
|
|
213
|
+
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
214
|
+
System Status
|
|
215
|
+
</CardTitle>
|
|
216
|
+
</CardHeader>
|
|
217
|
+
<CardContent>
|
|
218
|
+
<div className="flex items-center gap-2">
|
|
219
|
+
<span className="relative flex h-3 w-3">
|
|
220
|
+
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
|
221
|
+
<span className="relative inline-flex rounded-full h-3 w-3 bg-emerald-500"></span>
|
|
222
|
+
</span>
|
|
223
|
+
<span className="text-sm font-medium text-emerald-600">
|
|
224
|
+
Active
|
|
225
|
+
</span>
|
|
226
|
+
</div>
|
|
227
|
+
</CardContent>
|
|
228
|
+
</Card>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<Card className="flex-1 flex flex-col min-h-0">
|
|
232
|
+
<CardHeader>
|
|
233
|
+
<div className="flex items-center justify-between">
|
|
234
|
+
<CardTitle>Cache Instances</CardTitle>
|
|
235
|
+
<div className="w-[250px]">
|
|
236
|
+
<Input
|
|
237
|
+
placeholder="Filter by name..."
|
|
238
|
+
value={searchTerm}
|
|
239
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
240
|
+
/>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
</CardHeader>
|
|
244
|
+
<CardContent className="flex-1 overflow-auto p-0">
|
|
245
|
+
<Table>
|
|
246
|
+
<TableHeader>
|
|
247
|
+
<TableRow>
|
|
248
|
+
<TableHead>Cache Name</TableHead>
|
|
249
|
+
<TableHead>Items</TableHead>
|
|
250
|
+
<TableHead>TTL (Seconds)</TableHead>
|
|
251
|
+
<TableHead className="text-right">Actions</TableHead>
|
|
252
|
+
</TableRow>
|
|
253
|
+
</TableHeader>
|
|
254
|
+
<TableBody>
|
|
255
|
+
{filteredCaches.length === 0 && (
|
|
256
|
+
<TableRow>
|
|
257
|
+
<TableCell
|
|
258
|
+
colSpan={4}
|
|
259
|
+
className="text-center h-24 text-muted-foreground"
|
|
260
|
+
>
|
|
261
|
+
{loading ? "Loading caches..." : "No caches found."}
|
|
262
|
+
</TableCell>
|
|
263
|
+
</TableRow>
|
|
264
|
+
)}
|
|
265
|
+
{filteredCaches.map((cache) => (
|
|
266
|
+
<TableRow key={cache.name}>
|
|
267
|
+
<TableCell className="font-medium">{cache.name}</TableCell>
|
|
268
|
+
<TableCell>{cache.itemCount}</TableCell>
|
|
269
|
+
<TableCell>{cache.ttl}</TableCell>
|
|
270
|
+
<TableCell className="text-right">
|
|
271
|
+
<div className="flex items-center justify-end gap-2">
|
|
272
|
+
<Button
|
|
273
|
+
size="sm"
|
|
274
|
+
variant="outline"
|
|
275
|
+
onClick={() => handleInspect(cache.name)}
|
|
276
|
+
disabled={!getCacheKeys || loading}
|
|
277
|
+
>
|
|
278
|
+
Inspect
|
|
279
|
+
</Button>
|
|
280
|
+
<Button
|
|
281
|
+
size="sm"
|
|
282
|
+
variant="secondary"
|
|
283
|
+
onClick={() => handleClearCache(cache.name)}
|
|
284
|
+
disabled={!clearCache || loading}
|
|
285
|
+
>
|
|
286
|
+
Clear
|
|
287
|
+
</Button>
|
|
288
|
+
</div>
|
|
289
|
+
</TableCell>
|
|
290
|
+
</TableRow>
|
|
291
|
+
))}
|
|
292
|
+
</TableBody>
|
|
293
|
+
</Table>
|
|
294
|
+
</CardContent>
|
|
295
|
+
</Card>
|
|
296
|
+
|
|
297
|
+
{/* Inspect Dialog */}
|
|
298
|
+
<Dialog open={!!inspectCacheName} onOpenChange={(open) => !open && setInspectCacheName(null)}>
|
|
299
|
+
<DialogContent className="max-w-2xl max-h-[80vh] flex flex-col">
|
|
300
|
+
<DialogHeader>
|
|
301
|
+
<DialogTitle>Inspect Cache: {inspectCacheName}</DialogTitle>
|
|
302
|
+
<DialogDescription>
|
|
303
|
+
{inspecting ? "Loading..." : `${cacheKeys.length} items found`}
|
|
304
|
+
</DialogDescription>
|
|
305
|
+
</DialogHeader>
|
|
306
|
+
|
|
307
|
+
<div className="flex-1 overflow-hidden flex gap-4 min-h-0">
|
|
308
|
+
{/* Keys List */}
|
|
309
|
+
<div className="w-1/3 border-r pr-4 overflow-y-auto">
|
|
310
|
+
{cacheKeys.length === 0 ? (
|
|
311
|
+
<div className="text-sm text-muted-foreground py-4 text-center">No keys found</div>
|
|
312
|
+
) : (
|
|
313
|
+
<div className="space-y-1">
|
|
314
|
+
{cacheKeys.map(key => (
|
|
315
|
+
<div
|
|
316
|
+
key={key}
|
|
317
|
+
className={`text-xs p-2 rounded cursor-pointer truncate ${inspectKey === key ? 'bg-primary/10 text-primary font-medium' : 'hover:bg-muted'}`}
|
|
318
|
+
onClick={() => handleViewValue(key)}
|
|
319
|
+
title={key}
|
|
320
|
+
>
|
|
321
|
+
{key}
|
|
322
|
+
</div>
|
|
323
|
+
))}
|
|
324
|
+
</div>
|
|
325
|
+
)}
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
{/* Value View */}
|
|
329
|
+
<div className="w-2/3 overflow-y-auto bg-muted/30 p-4 rounded-md font-mono text-xs">
|
|
330
|
+
{inspectKey ? (
|
|
331
|
+
inspecting && keyValue === null ? (
|
|
332
|
+
<div className="text-muted-foreground">Loading value...</div>
|
|
333
|
+
) : (
|
|
334
|
+
<pre className="whitespace-pre-wrap break-all">
|
|
335
|
+
{JSON.stringify(keyValue, null, 2)}
|
|
336
|
+
</pre>
|
|
337
|
+
)
|
|
338
|
+
) : (
|
|
339
|
+
<div className="text-muted-foreground flex items-center justify-center h-full">
|
|
340
|
+
Select a key to view details
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
</DialogContent>
|
|
346
|
+
</Dialog>
|
|
347
|
+
</div>
|
|
348
|
+
);
|
|
349
|
+
};
|