diff --git a/AGENTS.md b/AGENTS.md index 55f5018..ecee99e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,7 @@ - Vue files use ` - + {{ t('Current Password') }} - + {{ errors.current_secret_key }} @@ -126,12 +116,7 @@ async function submitForm() { {{ t('New Password') }} - + {{ errors.new_secret_key }} @@ -141,13 +126,7 @@ async function submitForm() { {{ t('Confirm New Password') }} - + {{ errors.re_new_secret_key }} diff --git a/components/access-keys/edit-item.vue b/components/access-keys/edit-item.vue index 86955e3..df20b8d 100644 --- a/components/access-keys/edit-item.vue +++ b/components/access-keys/edit-item.vue @@ -1,16 +1,16 @@ - + {{ t('Access Key') }} @@ -131,11 +126,11 @@ async function submitForm() { {{ t('Description') }} - + - + {{ t('Status') }} diff --git a/components/access-keys/new-item.vue b/components/access-keys/new-item.vue index 017e91d..db6fdb4 100644 --- a/components/access-keys/new-item.vue +++ b/components/access-keys/new-item.vue @@ -1,10 +1,5 @@ - + {{ t('Access Key') }} @@ -29,12 +24,7 @@ {{ t('Expiry') }} - + {{ errors.expiry }} @@ -91,17 +81,17 @@ - + diff --git a/components/users/tabs/user.vue b/components/users/tabs/user.vue deleted file mode 100644 index 02ea501..0000000 --- a/components/users/tabs/user.vue +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - - {{ t('Delete Selected') }} - - - - {{ t('Add to Group') }} - - - - {{ t('Add User') }} - - - - - - - - - - {{ t('Name') }} - {{ t('Actions') }} - - - - - - toggleRow(rowKey(row), value)" - /> - - {{ row.accessKey }} - - - - - {{ t('Edit') }} - - - - - - {{ t('Delete') }} - - - - - {{ t('Confirm Delete') }} - - - {{ t('Cancel') }} - deleteItem(row)"> - {{ t('Delete') }} - - - - - - - - - - - - {{ t('No Data') }} - - - - - - - - - - - - diff --git a/config/navs.ts b/config/navs.ts index 3cbe274..9ad609b 100644 --- a/config/navs.ts +++ b/config/navs.ts @@ -22,6 +22,11 @@ export default [ to: '/users', icon: 'ri:file-user-line', }, + { + label: 'User Groups', + to: '/user-groups', + icon: 'ri:group-line', + }, { label: 'Import/Export', to: '/import-export', diff --git a/docs/shadcn-migration-plan.md b/docs/shadcn-migration-plan.md deleted file mode 100644 index a7c3094..0000000 --- a/docs/shadcn-migration-plan.md +++ /dev/null @@ -1,75 +0,0 @@ -# Shadcn-Vue Migration Plan - -This document tracks the Naive UI → shadcn-vue migration for the RustFS console. The migration focuses on a visual refresh without altering business logic or flows. - -## Phase Overview - -1. **Foundation** – remove Naive UI plugins, wire up shadcn providers, and ensure global theming, i18n, and layout tokens remain intact. -2. **Layout & Navigation** – rebuild the default layout and sidebar based on Sidebar07 while keeping Pinia-driven collapse behaviour. -3. **Shared Primitives** – introduce `app-*` components that wrap shadcn primitives to cover buttons, cards, dialogs, drawers, forms, tables, and feedback mechanisms used across screens. -4. **Feature Screens** – replace Naive UI usage page-by-page, preserving interactions and API calls; migrate bespoke styles only when still needed. -5. **Cleanup & Validation** – remove Naive UI dependencies, stale utilities, and update docs/tests before a final regression pass. - -Each phase will be committed independently on `refactor/shadcn-vue` to keep rollback simple. - -## Component Mapping - -| Category | Naive UI usage | shadcn-vue / plan | -| --- | --- | --- | -| Providers | `NConfigProvider`, `NMessageProvider`, `NDialogProvider`, `NNotificationProvider`, `useMessage`, `useDialog`, `useNotification` | Replace with a root `AppUiProvider` that wraps `ThemeProvider`, `TooltipProvider`, and `Toaster` (`components/ui/sonner`). Provide composables for toast/dialog using shadcn Dialog + AlertDialog. | -| Layout | `NLayout`, `NLayoutSider`, `NSpace`, `NFlex` | Adopt Sidebar07 structure using `SidebarProvider`, `AppSidebar`, and Tailwind flex utilities; remove redundant spacing classes. | -| Navigation | `NMenu`, `NDropdown` | Build `AppSidebarMenu` using shadcn Sidebar primitives; for dropdowns use `components/ui/dropdown-menu`. | -| Form shell | `NForm`, `NFormItem`, `NFormItemGi`, `NFormItemGridItem`, `NGrid`, `NGi` | Use `vee-validate` (already installed) with `components/ui/form`. Create wrappers (`app-form`, `app-form-field`, helpers) that match current inline/grid layout via Tailwind grid/flex utilities. | -| Inputs | `NInput`, `NInputNumber`, `NSelect`, `NDatePicker`, `NCheckbox`, `NCheckboxGroup`, `NRadio`, `NRadioGroup`, `NSwitch`, `NDynamicInput`, `NUpload`, `NUploadDragger`, `NInputGroup`, `NInputGroupLabel`, `NP`, `NText` | Map to shadcn `Input`, `NumberField`, `Select`, `Calendar/Popover`, `Checkbox`, `RadioGroup`, `Switch`, `TagsInput`. Implement custom wrappers for dynamic list fields and upload (likely using existing uploader logic + `Dropzone`). Replace `NP/NText` with semantic HTML + `Typography` utilities. | -| Buttons | `NButton`, `NButtonGroup` | Use shadcn `Button` / `ButtonGroup` directly; add Tailwind utilities or inline spinner when needed for loading states. | -| Feedback | `NModal`, `NDrawer`, `NAlert`, `NTooltip`, `NEmpty`, `NProgress`, `NSpin`, `NStatistic`, `NBreadcrumb` | Use shadcn `Dialog`, `Drawer`, `Alert`, `Tooltip`, `Empty` block, `Progress`, `Spinner` etc. Create `app-stat`/`app-empty` wrappers where gaps exist. | -| Data display | `NCard`, `NList`, `NThing`, `NBadge`, `NTag`, `NDescriptions`, `NDescriptionsItem`, `NCarousel`, `NCollapse`, `NCollapseItem` | Replace with shadcn `Card`, `Badge`, `Accordion`, `Tabs` etc. Implement `AppDescriptionList` (simple definition list). Use `Carousel` block already present. | -| Tables | `NDataTable`, `NVirtualList` | Implement `DataTable` powered by `@tanstack/vue-table` + `ScrollArea`. Support selection, inline actions, slot-based cell rendering. Provide compatible props for datasets currently using render functions. | -| Misc | `NDrawerContent`, `NScrollbar`, `NPageHeader`, `NPopover`, `NPopconfirm` | Use `DrawerContent`, `ScrollArea`, `PageHeader` replaced by `div` + `Breadcrumb`, shadcn `Popover`, and build `AppConfirmDialog` on top of AlertDialog. | - -### Additional Dependencies - -- `@tanstack/vue-table`, `@tanstack/match-sorter-utils`, and `@tanstack/vue-virtual` for the shared table abstraction. -- Possibly `@headlessui/vue` or lightweight packages are **not** required because shadcn already provides primitives through Reka/ Radix. - -## Shared Components To Introduce - -- `components/providers/AppUiProvider.vue` – wraps shadcn theme providers and exports composables for toasts/dialogs. -- `components/app-sidebar.vue` + related items (menu, user dropdown host) following Sidebar07 markup. -- Form/layout helpers in `components/app-*.vue` (card, form, field grid) for consistent structure. -- Shared overlays like `components/modal.vue`, `drawer.vue`, and confirmation dialogs. -- `components/DataTable` module with column definitions, toolbar templates, and selection helpers. -- Presentation helpers such as `components/app-description-list.vue`, `empty-state.vue`, `spinner.vue`, and related statistic cards. - -These wrappers allow feature screens to migrate with minimal churn and keep visual consistency. - -## Page Migration Order - -1. **Access Keys / Users / Policies** – heavy data table usage; verify DataTable handles selection and actions. -2. **Objects & Buckets** – exercise drawers/modals, dynamic input, and descriptions. -3. **Lifecycle / Replication / Events** – validate complex forms, tabs, accordions. -4. **Performance Dashboard** – ensure stats, badges, carousel translations remain coherent. -5. **Auth / Settings / License** – simpler forms and cards to finish the sweep. - -After each page group migrates, commit & push to `refactor/shadcn-vue`. - -## Cleanup Checklist - -- [x] Remove `nuxtjs-naive-ui`, `naive-ui`, `auto-imports` config, and generated component declarations. -- [x] Drop obsolete CSS tied to Naive overrides once equivalents exist. -- [x] Update README to reflect the new UI stack. -- [x] Run lint/tests plus a targeted manual regression of menus, forms, uploads, and table interactions. - -## Remaining TODO (2025-10-27) - -- [x] Object task management: `components/object/task-stats.vue`, `components/object/upload/task/item.vue`, `components/object/delete/task/item.vue`, and finish `components/object/upload/picker.vue`. -- [x] Bucket dialogs: migrate `components/buckets/new-form.vue` and `components/buckets/info.vue`. -- [x] User management suite: group/user edit & policy components under `components/users/**` (forms, tables, modals). -- [x] Performance dashboard: refactor `pages/performance/index.vue` to shadcn primitives. -- [x] License center: migrate `pages/license/index.vue` & `pages/license/components/license-article.vue`. -- [x] SSE monitor & metrics: convert `pages/sse/index.vue`. -- [x] System configuration tooling: update `pages/config.vue`. -- [x] KMS test utilities: migrate `pages/kms-test.vue`. -- [x] Final pass: remove Naive UI resolver/module, scrub `` usage (`rg " {{ t('Access Keys') }} - + {{ t('Change Password') }} - + {{ t('Delete Selected') }} - + {{ t('Add Access Key') }} @@ -25,10 +25,10 @@ - - - - + + + + @@ -36,7 +36,6 @@ import { Button } from '@/components/ui/button' import { Icon } from '#components' -import { ChangePassword, EditItem, NewItem } from '@/components/access-keys' import DataTablePagination from '@/components/data-table/data-table-pagination.vue' import DataTable from '@/components/data-table/data-table.vue' import { useDataTable } from '@/components/data-table/useDataTable' @@ -64,8 +63,6 @@ const data = ref([]) const loading = ref(false) const searchTerm = ref('') -const TableButton = Button as unknown as any - const openEditItem = (row: RowData) => { editItemRef.value?.openDialog(row) } @@ -90,13 +87,13 @@ const columns: ColumnDef[] = [ checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(), 'onUpdate:checked': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value), - class: 'translate-y-[2px]' + class: 'translate-y-0.5' }), cell: ({ row }) => h(Checkbox, { checked: row.getIsSelected(), 'onUpdate:checked': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), - class: 'translate-y-[2px]' + class: 'translate-y-0.5' }), size: 48, }, @@ -139,12 +136,12 @@ const columns: ColumnDef[] = [ enableHiding: false, cell: ({ row }) => h('div', { class: 'flex justify-center gap-2' }, [ - h(TableButton, { + h(Button, { variant: 'outline', size: 'sm', onClick: () => openEditItem(row.original), }, () => [h(Icon, { name: 'ri:edit-2-line', class: 'size-4' }), h('span', t('Edit'))]), - h(TableButton, { + h(Button, { variant: 'outline', size: 'sm', onClick: () => confirmDeleteSingle(row.original), diff --git a/pages/browser/index.vue b/pages/browser/index.vue index b2d2091..6a9d075 100644 --- a/pages/browser/index.vue +++ b/pages/browser/index.vue @@ -4,7 +4,7 @@ {{ t('Buckets') }} - + {{ t('Create Bucket') }} diff --git a/pages/events-target/index.vue b/pages/events-target/index.vue index f90af3f..f122b02 100644 --- a/pages/events-target/index.vue +++ b/pages/events-target/index.vue @@ -6,7 +6,7 @@ - + {{ t('Add Event Destination') }} @@ -19,7 +19,7 @@ - refresh()" /> + refresh()" /> diff --git a/pages/events/index.vue b/pages/events/index.vue index 29ad3ed..39aeaac 100644 --- a/pages/events/index.vue +++ b/pages/events/index.vue @@ -3,29 +3,27 @@ {{ t('Events') }} - - {{ t('Bucket') }} - - - - - - - - {{ bucket.label }} - - - - - - - {{ t('Add Event Subscription') }} - - - - {{ t('Refresh') }} - - + {{ t('Bucket') }} + + + + + + + + {{ bucket.label }} + + + + + + + {{ t('Add Event Subscription') }} + + + + {{ t('Refresh') }} + @@ -68,7 +66,7 @@ {{ row.suffix || '-' }} - handleRowDelete(row, event)"> + handleRowDelete(row, event)"> {{ t('Delete') }} diff --git a/pages/kms-test.vue b/pages/kms-test.vue deleted file mode 100644 index 8b5bd15..0000000 --- a/pages/kms-test.vue +++ /dev/null @@ -1,166 +0,0 @@ - - - - KMS API Test Page - - Test page for KMS functionality after API updates - - - - - - - KMS API Test Suite - - - - Test Service Status - Test Configuration - Test Key List - Test Clear Cache - - - - - Test Results - - - - - - {{ result.test }} - {{ result.message }} - - - View Response Data - {{ JSON.stringify(result.data, null, 2) }} - - - - - {{ result.success ? 'PASS' : 'FAIL' }} - - - - - - - - - API Documentation - - - - Updated KMS API documentation is available in the project's docs folder. - - - View API Documentation - - - - - - - - - - diff --git a/pages/lifecycle/index.vue b/pages/lifecycle/index.vue index 9e078c5..407d684 100644 --- a/pages/lifecycle/index.vue +++ b/pages/lifecycle/index.vue @@ -4,14 +4,8 @@ {{ t('Lifecycle') }} - - + + {{ t('Add Lifecycle Rule') }} @@ -35,12 +29,12 @@ import { Button } from '@/components/ui/button' import { Icon } from '#components' import DataTable from '@/components/data-table/data-table.vue' import { useDataTable } from '@/components/data-table/useDataTable' -import type { SelectOption } from '@/components/selector.vue' import { Badge } from '@/components/ui/badge' import type { Bucket } from '@aws-sdk/client-s3' import type { ColumnDef } from '@tanstack/vue-table' import { computed, h, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' +import type { SelectOption } from '~/components/selector.vue' const { t } = useI18n() const message = useMessage() diff --git a/pages/policies/index.vue b/pages/policies/index.vue index 30b0519..a9c93a9 100644 --- a/pages/policies/index.vue +++ b/pages/policies/index.vue @@ -4,7 +4,7 @@ {{ t('IAM Policies') }} - + {{ t('New Policy') }} diff --git a/pages/replication/index.vue b/pages/replication/index.vue index 4d7ddd0..97d05b6 100644 --- a/pages/replication/index.vue +++ b/pages/replication/index.vue @@ -4,14 +4,8 @@ {{ t('Bucket Replication') }} - - + + {{ t('Add Replication Rule') }} @@ -35,13 +29,13 @@ import { Button } from '@/components/ui/button' import { Icon } from '#components' import DataTable from '@/components/data-table/data-table.vue' import { useDataTable } from '@/components/data-table/useDataTable' -import type { SelectOption } from '@/components/selector.vue' import { Badge } from '@/components/ui/badge' import { useBucket } from '@/composables/useBucket' import type { Bucket } from '@aws-sdk/client-s3' import type { ColumnDef } from '@tanstack/vue-table' import { computed, h, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' +import type { SelectOption } from '~/components/selector.vue' const { t } = useI18n() const message = useMessage() diff --git a/pages/site-replication/index.vue b/pages/site-replication/index.vue index 8b1fce9..ffdbaa4 100644 --- a/pages/site-replication/index.vue +++ b/pages/site-replication/index.vue @@ -5,7 +5,7 @@ - + {{ t('Add Site') }} diff --git a/pages/sse/index.vue b/pages/sse/index.vue index 75eed20..9992c82 100644 --- a/pages/sse/index.vue +++ b/pages/sse/index.vue @@ -60,23 +60,12 @@ {{ t('Details') }} - + {{ t('Start KMS') }} - + {{ t('Stop KMS') }} @@ -171,165 +160,161 @@ - - {{ t('KMS Type') }} - - {{ t('HashiCorp Vault Transit Engine') }} - - + + {{ t('KMS Type') }} + + {{ t('HashiCorp Vault Transit Engine') }} + + - - {{ t('Vault Server Address') }} - - - - + + {{ t('Vault Server Address') }} + + + + - - {{ t('Authentication Method') }} - - - - - - {{ option.label }} - - {{ option.description }} + + {{ t('Authentication Method') }} + + + + + + {{ option.label }} + + {{ option.description }} + - - - - - + + + + - - {{ t('Vault Token') }} - - - - - {{ t('Required: Vault authentication token') }} - - - - - - {{ t('Role ID') }} + + {{ t('Vault Token') }} - + - {{ t('AppRole Role ID from Vault') }} + {{ t('Required: Vault authentication token') }} + + + + {{ t('Role ID') }} + + + + + {{ t('AppRole Role ID from Vault') }} + + + + {{ t('Secret ID') }} + + + + + {{ t('AppRole Secret ID from Vault') }} + + + + - {{ t('Secret ID') }} + {{ t('Transit Mount Path') }} - + - {{ t('AppRole Secret ID from Vault') }} + {{ t('Transit engine mount path, default: transit') }} - - - {{ t('Transit Mount Path') }} - - - - - {{ t('Transit engine mount path, default: transit') }} - - - - - {{ t('KV Mount Path') }} - - - - - {{ t('KV storage mount path, default: secret') }} - - - - - {{ t('Key Path Prefix') }} - - - - - {{ t('Key storage path prefix in KV store') }} - - - - - {{ t('Timeout (seconds)') }} + {{ t('KV Mount Path') }} - + - {{ t('Request timeout in seconds, default: 30') }} + {{ t('KV storage mount path, default: secret') }} + - {{ t('Retry Attempts') }} + {{ t('Key Path Prefix') }} - + - {{ t('Number of retry attempts, default: 3') }} + {{ t('Key storage path prefix in KV store') }} - - - {{ t('Default Key ID') }} - - - - - {{ t('Default master key ID for SSE-KMS') }} - - + + + {{ t('Timeout (seconds)') }} + + + + + {{ t('Request timeout in seconds, default: 30') }} + + + + {{ t('Retry Attempts') }} + + + + + {{ t('Number of retry attempts, default: 3') }} + + + - - {{ t('Enable Cache') }} - - - - - - - {{ t('Enable caching for better performance, default: true') }} - - + + {{ t('Default Key ID') }} + + + + + {{ t('Default master key ID for SSE-KMS') }} + + - - {{ t('Cache TTL (seconds)') }} - - - - - {{ t('Cache time-to-live in seconds, default: 600') }} - - + + {{ t('Enable Cache') }} + + + + + + + {{ t('Enable caching for better performance, default: true') }} + + - - - {{ t('Cancel') }} - - - {{ t('Save Configuration') }} - - - - + + {{ t('Cache TTL (seconds)') }} + + + + + {{ t('Cache time-to-live in seconds, default: 600') }} + + + + + + {{ t('Cancel') }} + + + {{ t('Save Configuration') }} + + + + @@ -407,7 +392,7 @@ {{ t('Master Key (CMK)') - }} + }} {{ getKeyName(key) }} @@ -473,7 +458,7 @@ {{ t('Data Key (DEK)') - }} + }} {{ getKeyName(key) }} @@ -503,13 +488,7 @@ - + {{ t('Create First Key') }} @@ -531,27 +510,13 @@ - - + + - + @@ -586,13 +551,8 @@ {{ t('Configure') }} - + {{ t('Remove') }} @@ -601,10 +561,7 @@ - + {{ t('Algorithm') }}: {{ bucket.encryptionAlgorithm }} @@ -616,18 +573,12 @@ - + {{ t('No buckets found') }} {{ t('Create your first bucket to configure encryption') }} - + {{ t('No buckets match your search') }} {{ t('Try adjusting your search terms') }} @@ -879,15 +830,15 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import EmptyState from '@/components/empty-state.vue' -import Selector from '@/components/selector.vue' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Field, FieldContent, FieldDescription, FieldLabel } from '@/components/ui/field' import { Spinner } from '@/components/ui/spinner' import { Switch } from '@/components/ui/switch' -import { Field, FieldContent, FieldDescription, FieldLabel } from '@/components/ui/field' import { useMessage } from '@/composables/ui' import { computed, onMounted, reactive, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' +import Selector from '~/components/selector.vue' const { getKMSStatus, getConfiguration, diff --git a/pages/tiers/index.vue b/pages/tiers/index.vue index dfb9e18..2aa61ea 100644 --- a/pages/tiers/index.vue +++ b/pages/tiers/index.vue @@ -3,7 +3,7 @@ {{ t('Tiers') }} - + {{ t('Add Tier') }} diff --git a/pages/user-groups/index.vue b/pages/user-groups/index.vue new file mode 100644 index 0000000..5056b1b --- /dev/null +++ b/pages/user-groups/index.vue @@ -0,0 +1,259 @@ + + + + {{ t('User Groups') }} + + + + + {{ t('Assign Policy') }} + + + + {{ t('Add User Group') }} + + + + + + + + + + + + + + diff --git a/pages/users/index.vue b/pages/users/index.vue index 260723b..177249d 100644 --- a/pages/users/index.vue +++ b/pages/users/index.vue @@ -2,42 +2,234 @@ {{ t('Users') }} + + + + + {{ t('Delete Selected') }} + + + + {{ t('Add to Group') }} + + + + {{ t('Add User') }} + + - - - - - {{ tab.label }} - - - - - - - - - + + + + + + + - - -
{{ t('No Data') }}
Test page for KMS functionality after API updates
{{ result.message }}
{{ JSON.stringify(result.data, null, 2) }}
- Updated KMS API documentation is available in the project's docs folder. -
{{ t('KMS Type') }}
- {{ t('HashiCorp Vault Transit Engine') }} -
+ {{ t('HashiCorp Vault Transit Engine') }} +