From 0918bdb49a5193a7e8cf03a0d17871ad0cdf9d24 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 11 Jul 2026 05:13:16 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(web):=20consolida?= =?UTF-8?q?te=20design-system=20primitives=20and=20responsive=20data=20vie?= =?UTF-8?q?ws?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/constants.go | 2 +- controller/channel.go | 2 +- web/bun.lock | 25 +- .../settings/ChannelSelectorModal.jsx | 2 +- .../channels/modals/MultiKeyManageModal.jsx | 4 +- .../users/modals/UserSubscriptionsModal.jsx | 2 +- .../topup/modals/TopupHistoryModal.jsx | 2 +- .../src/constants/channel.constants.js | 2 +- web/classic/src/constants/common.constant.js | 2 +- .../src/constants/dashboard.constants.js | 2 +- .../subscriptions/useSubscriptionsData.jsx | 2 +- .../src/pages/Setting/Chat/SettingsChats.jsx | 2 +- .../Setting/Dashboard/SettingsAPIInfo.jsx | 2 +- .../Dashboard/SettingsAnnouncements.jsx | 2 +- .../pages/Setting/Dashboard/SettingsFAQ.jsx | 2 +- .../Setting/Dashboard/SettingsUptimeKuma.jsx | 2 +- .../pages/Setting/Ratio/UpstreamRatioSync.jsx | 2 +- .../Ratio/hooks/useModelPricingEditorState.js | 2 +- web/default/.oxlintrc.json | 40 + web/default/AGENTS.md | 18 +- web/default/package.json | 5 +- .../src/components/ai-elements/actions.tsx | 6 +- .../src/components/ai-elements/artifact.tsx | 16 +- .../src/components/ai-elements/branch.tsx | 10 +- .../src/components/ai-elements/code-block.tsx | 12 +- .../components/ai-elements/confirmation.tsx | 4 +- .../src/components/ai-elements/context.tsx | 2 +- .../components/ai-elements/conversation.tsx | 2 +- .../ai-elements/inline-citation.tsx | 2 +- .../components/ai-elements/open-in-chat.tsx | 2 +- .../src/components/ai-elements/plan.tsx | 4 +- .../components/ai-elements/prompt-input.tsx | 31 +- .../src/components/ai-elements/queue.tsx | 4 +- .../src/components/ai-elements/suggestion.tsx | 2 +- .../components/ai-elements/web-preview.tsx | 10 +- web/default/src/components/command-menu.tsx | 2 +- web/default/src/components/config-drawer.tsx | 8 +- web/default/src/components/confirm-dialog.tsx | 4 +- web/default/src/components/copy-button.tsx | 4 +- .../data-table/core/column-header.tsx | 5 +- .../data-table/core/content-sized-columns.ts | 7 +- .../data-table/core/data-table-colgroup.tsx | 11 +- .../data-table/core/data-table-header.tsx | 19 +- .../data-table/core/data-table-row.tsx | 74 +- .../data-table/core/data-table-view.tsx | 32 +- .../components/data-table/core/pagination.tsx | 20 +- .../data-table/core/row-action-menu.tsx | 4 +- .../data-table/core/table-empty.tsx | 2 +- .../data-table/core/table-sizing.ts | 5 +- .../data-table/core/table-skeleton.tsx | 2 +- .../data-table/core/truncated-cell.tsx | 9 +- .../src/components/data-table/core/types.ts | 1 + .../src/components/data-table/index.ts | 16 +- .../data-table/layout/card-cell-utils.ts | 8 +- .../data-table/layout/card-field.tsx | 121 ++++ .../data-table/layout/card-grid.tsx | 4 +- .../data-table/layout/card-row-content.tsx | 264 +++---- .../data-table/layout/data-table-page.tsx | 38 +- .../data-table/layout/mobile-card-list.tsx | 18 +- .../data-table/static/static-data-table.tsx | 2 +- .../data-table/static/static-row-actions.tsx | 2 +- .../data-table/toolbar/bulk-actions.tsx | 5 +- .../data-table/toolbar/faceted-filter.tsx | 146 ++-- .../data-table/toolbar/filter-panel.tsx | 307 ++++++++ .../components/data-table/toolbar/toolbar.tsx | 200 ++--- .../data-table/toolbar/view-mode-toggle.tsx | 2 +- .../data-table/toolbar/view-options.tsx | 2 +- web/default/src/components/date-picker.tsx | 2 +- .../src/components/datetime-picker.tsx | 4 +- .../src/components/design-system/README.md | 42 ++ .../components/design-system/alert-dialog.tsx | 80 ++ .../src/components/design-system/button.tsx | 65 ++ .../design-system/combobox-input.tsx | 37 + .../src/components/design-system/combobox.tsx | 139 ++++ .../src/components/design-system/command.tsx | 77 ++ .../src/components/design-system/dialog.tsx | 74 ++ .../components/design-system/input-group.tsx | 79 ++ .../src/components/design-system/input.tsx | 37 + .../components/design-system/pagination.tsx | 55 ++ .../src/components/design-system/select.tsx | 64 ++ .../src/components/design-system/sidebar.tsx | 95 +++ .../src/components/design-system/table.tsx | 49 ++ .../src/components/design-system/tabs.tsx | 57 ++ .../components/design-system/toggle-group.tsx | 46 ++ .../src/components/design-system/toggle.tsx | 59 ++ web/default/src/components/dialog.tsx | 2 +- web/default/src/components/error-state.tsx | 4 +- web/default/src/components/group-badge.tsx | 52 +- .../src/components/json-code-editor.tsx | 5 +- web/default/src/components/json-editor.tsx | 10 +- .../src/components/language-switcher.tsx | 6 +- .../layout/components/app-sidebar.tsx | 14 +- .../components/authenticated-layout.tsx | 7 +- .../layout/components/chat-presets-item.tsx | 16 +- .../components/layout/components/header.tsx | 4 +- .../layout/components/mobile-drawer.tsx | 7 +- .../layout/components/nav-group.tsx | 22 +- .../layout/components/public-header.tsx | 6 +- .../layout/components/sidebar-view-header.tsx | 4 +- .../layout/components/system-brand.tsx | 2 +- .../components/layout/components/top-nav.tsx | 4 +- web/default/src/components/learn-more.tsx | 2 +- .../src/components/masked-value-display.tsx | 6 +- .../src/components/model-group-selector.tsx | 66 +- web/default/src/components/multi-select.tsx | 2 +- .../src/components/notification-popover.tsx | 15 +- web/default/src/components/password-input.tsx | 9 +- .../src/components/profile-dropdown.tsx | 4 +- web/default/src/components/provider-badge.tsx | 37 +- .../risk-acknowledgement-dialog.tsx | 4 +- web/default/src/components/search.tsx | 5 +- web/default/src/components/status-badge.tsx | 386 +++++----- web/default/src/components/tag-input.tsx | 2 +- .../src/components/theme-quick-switcher.tsx | 14 +- web/default/src/components/theme-switch.tsx | 6 +- web/default/src/components/ui/accordion.tsx | 4 +- .../src/components/ui/alert-dialog.tsx | 10 +- web/default/src/components/ui/alert.tsx | 4 +- web/default/src/components/ui/avatar.tsx | 10 +- web/default/src/components/ui/breadcrumb.tsx | 4 +- .../src/components/ui/button-group.tsx | 2 +- web/default/src/components/ui/button.tsx | 26 +- web/default/src/components/ui/calendar.tsx | 4 +- web/default/src/components/ui/card.tsx | 10 +- web/default/src/components/ui/carousel.tsx | 8 +- web/default/src/components/ui/chart.tsx | 14 +- web/default/src/components/ui/checkbox.tsx | 2 +- web/default/src/components/ui/combobox.tsx | 59 +- web/default/src/components/ui/command.tsx | 10 +- .../src/components/ui/context-menu.tsx | 16 +- web/default/src/components/ui/dialog.tsx | 8 +- web/default/src/components/ui/drawer.tsx | 168 ++++- .../src/components/ui/dropdown-menu-events.ts | 25 - .../src/components/ui/dropdown-menu.test.tsx | 51 -- .../src/components/ui/dropdown-menu.tsx | 36 +- web/default/src/components/ui/empty.tsx | 2 +- web/default/src/components/ui/field.tsx | 8 +- web/default/src/components/ui/hover-card.tsx | 2 +- web/default/src/components/ui/input-group.tsx | 11 +- web/default/src/components/ui/input-otp.tsx | 4 +- web/default/src/components/ui/input.tsx | 2 +- web/default/src/components/ui/item.tsx | 2 +- web/default/src/components/ui/kbd.tsx | 2 +- web/default/src/components/ui/menubar.tsx | 18 +- .../src/components/ui/native-select.tsx | 2 +- .../src/components/ui/navigation-menu.tsx | 6 +- web/default/src/components/ui/popover.tsx | 16 +- web/default/src/components/ui/progress.tsx | 6 +- web/default/src/components/ui/radio-group.tsx | 2 +- web/default/src/components/ui/resizable.tsx | 2 +- web/default/src/components/ui/select.tsx | 72 +- web/default/src/components/ui/separator.tsx | 2 +- web/default/src/components/ui/sheet.tsx | 19 +- web/default/src/components/ui/sidebar.tsx | 49 +- web/default/src/components/ui/skeleton.tsx | 2 +- web/default/src/components/ui/sonner.tsx | 33 +- web/default/src/components/ui/spinner.tsx | 8 +- web/default/src/components/ui/switch.tsx | 2 +- web/default/src/components/ui/table.tsx | 28 +- web/default/src/components/ui/tabs.tsx | 4 +- web/default/src/components/ui/textarea.tsx | 2 +- .../src/components/ui/toggle-group.tsx | 6 +- web/default/src/components/ui/toggle.tsx | 2 +- web/default/src/components/ui/tooltip.tsx | 2 +- .../auth/components/oauth-providers.tsx | 5 +- .../components/forgot-password-form.tsx | 5 +- .../features/auth/otp/components/otp-form.tsx | 11 +- .../auth/reset-password-confirm/index.tsx | 5 +- .../components/secure-verification-dialog.tsx | 11 +- .../sign-in/components/user-auth-form.tsx | 10 +- .../auth/sign-up/components/sign-up-form.tsx | 7 +- web/default/src/features/channels/api.ts | 2 +- .../channels/components/channel-card.tsx | 203 +++--- .../channels/components/channels-columns.tsx | 240 +++--- .../components/channels-primary-buttons.tsx | 5 +- .../channels/components/channels-table.tsx | 16 +- .../components/data-table-bulk-actions.tsx | 8 +- .../components/data-table-row-actions.tsx | 5 +- .../components/data-table-tag-row-actions.tsx | 2 +- .../dialogs/advanced-custom-editor-dialog.tsx | 30 +- .../dialogs/balance-query-dialog.tsx | 2 +- .../dialogs/channel-test-dialog.tsx | 52 +- .../components/dialogs/codex-usage-dialog.tsx | 93 +-- .../dialogs/copy-channel-dialog.tsx | 4 +- .../components/dialogs/edit-tag-dialog.tsx | 44 +- .../dialogs/fetch-models-dialog.tsx | 11 +- .../missing-models-confirmation-dialog.tsx | 4 +- .../dialogs/multi-key-manage-dialog.tsx | 65 +- .../dialogs/multi-key-table-row-actions.tsx | 2 +- .../dialogs/ollama-models-dialog.tsx | 17 +- .../dialogs/param-override-editor-dialog.tsx | 127 +--- .../dialogs/status-code-risk-dialog.tsx | 4 +- .../dialogs/tag-batch-edit-dialog.tsx | 4 +- .../dialogs/upstream-update-dialog.tsx | 23 +- .../drawers/channel-mutate-drawer.tsx | 50 +- .../components/model-mapping-editor.tsx | 16 +- .../src/features/channels/constants.ts | 6 +- .../features/channels/lib/channel-utils.ts | 4 +- .../dashboard/components/flow/flow-charts.tsx | 4 +- .../components/flow/flow-node-filter.tsx | 15 +- .../models/models-chart-preferences.tsx | 10 +- .../models/models-filter-dialog.tsx | 15 +- .../models/performance-overview.tsx | 45 +- .../components/overview/api-info-item.tsx | 51 +- .../overview/overview-dashboard.tsx | 682 +----------------- .../components/overview/summary-cards.tsx | 2 +- .../components/overview/uptime-panel.tsx | 5 +- .../components/users/user-charts.tsx | 2 +- web/default/src/features/dashboard/index.tsx | 8 +- .../dashboard/lib/flow-selection.test.ts | 43 -- .../features/dashboard/lib/flow-selection.ts | 14 +- .../src/features/dashboard/lib/flow.test.ts | 10 +- .../src/features/dashboard/lib/flow.ts | 22 - web/default/src/features/errors/forbidden.tsx | 2 +- .../src/features/errors/general-error.tsx | 2 +- .../src/features/errors/maintenance-error.tsx | 2 +- .../src/features/errors/not-found-error.tsx | 2 +- .../features/errors/unauthorized-error.tsx | 2 +- .../features/home/components/hero-buttons.tsx | 2 +- .../features/home/components/sections/cta.tsx | 2 +- .../home/components/sections/hero.tsx | 17 +- web/default/src/features/keys/api.ts | 2 +- .../features/keys/components/api-key-card.tsx | 189 +++++ .../components/api-key-group-combobox.tsx | 8 +- .../keys/components/api-keys-cells.tsx | 36 +- .../keys/components/api-keys-columns.tsx | 128 ++-- .../components/api-keys-delete-dialog.tsx | 2 +- .../components/api-keys-mutate-drawer.tsx | 12 +- .../components/api-keys-primary-buttons.tsx | 4 +- .../keys/components/api-keys-table.tsx | 162 +---- .../components/data-table-bulk-actions.tsx | 4 +- .../components/data-table-row-actions.tsx | 2 +- .../components/dialogs/cc-switch-dialog.tsx | 4 +- web/default/src/features/keys/constants.ts | 4 +- .../src/features/legal/legal-document.tsx | 2 +- .../components/data-table-bulk-actions.tsx | 6 +- .../components/data-table-row-actions.tsx | 2 +- .../components/deployment-access-guard.tsx | 2 +- .../models/components/deployments-columns.tsx | 94 ++- .../models/components/deployments-table.tsx | 8 +- .../models/components/description-cell.tsx | 6 +- .../dialogs/create-deployment-drawer.tsx | 20 +- .../dialogs/extend-deployment-dialog.tsx | 4 +- .../dialogs/missing-models-dialog.tsx | 25 +- .../prefill-group-management-dialog.tsx | 70 +- .../dialogs/rename-deployment-dialog.tsx | 4 +- .../components/dialogs/sync-wizard-dialog.tsx | 8 +- .../dialogs/update-config-dialog.tsx | 4 +- .../dialogs/upstream-conflict-dialog.tsx | 77 +- .../dialogs/vendor-mutate-dialog.tsx | 4 +- .../dialogs/view-details-dialog.tsx | 6 +- .../components/dialogs/view-logs-dialog.tsx | 8 +- .../drawers/model-mutate-drawer.tsx | 22 +- .../drawers/prefill-group-form-drawer.tsx | 100 ++- .../models/components/models-columns.tsx | 172 +++-- .../components/models-primary-buttons.tsx | 6 +- .../models/components/models-table.tsx | 6 +- .../models/components/prefill-group-shared.ts | 20 +- web/default/src/features/models/constants.ts | 42 +- web/default/src/features/models/index.tsx | 10 +- .../chat/playground-empty-state.tsx | 4 +- .../input/playground-parameter-panel.tsx | 13 +- .../message/message-action-button.tsx | 2 +- .../components/message/message-actions.tsx | 4 +- .../components/message/message-error.tsx | 3 +- .../message/playground-message-editor.tsx | 2 +- .../pricing/components/empty-state.tsx | 4 +- .../pricing/components/model-card-grid.tsx | 4 +- .../pricing/components/model-card.tsx | 9 +- .../pricing/components/model-details-api.tsx | 6 +- .../pricing/components/model-details.tsx | 18 +- .../pricing/components/pricing-columns.tsx | 37 +- .../pricing/components/pricing-sidebar.tsx | 3 +- .../pricing/components/pricing-toolbar.tsx | 12 +- .../pricing/components/search-bar.tsx | 6 +- .../components/checkin-calendar-card.tsx | 148 ++-- ...gs-tab.tsx => connected-accounts-card.tsx} | 250 +++---- .../dialogs/access-token-dialog.tsx | 5 +- .../dialogs/change-password-dialog.tsx | 4 +- .../dialogs/delete-account-dialog.tsx | 6 +- .../components/dialogs/email-bind-dialog.tsx | 19 +- .../dialogs/two-fa-backup-dialog.tsx | 48 +- .../dialogs/two-fa-disable-dialog.tsx | 6 +- .../dialogs/two-fa-setup-dialog.tsx | 20 +- .../components/language-preferences-card.tsx | 39 +- ...tab.tsx => notification-settings-card.tsx} | 186 ++--- .../{passkey-card.tsx => passkey-row.tsx} | 231 +++--- .../profile/components/profile-header.tsx | 89 +-- .../components/profile-security-card.tsx | 129 ++-- .../components/profile-settings-card.tsx | 106 --- .../components/sidebar-modules-card.tsx | 142 ++-- .../profile/components/two-fa-card.tsx | 186 ----- .../profile/components/two-fa-row.tsx | 122 ++++ web/default/src/features/profile/index.tsx | 88 ++- .../src/features/redemption-codes/api.ts | 4 +- .../components/data-table-bulk-actions.tsx | 1 - .../components/data-table-row-actions.tsx | 2 +- .../components/redemptions-columns.tsx | 90 ++- .../components/redemptions-delete-dialog.tsx | 2 +- .../components/redemptions-mutate-drawer.tsx | 8 +- .../redemptions-primary-buttons.tsx | 5 +- .../components/redemptions-table.tsx | 9 +- .../features/redemption-codes/constants.ts | 5 +- .../features/setup/components/admin-step.tsx | 2 +- .../setup/components/complete-step.tsx | 6 +- .../setup/components/database-step.tsx | 8 +- .../setup/components/step-navigation.tsx | 2 +- .../components/data-table-row-actions.tsx | 2 +- .../dialogs/subscription-purchase-dialog.tsx | 10 +- .../dialogs/user-subscriptions-dialog.tsx | 52 +- .../components/subscriptions-columns.tsx | 87 ++- .../subscriptions-mutate-drawer.tsx | 20 +- .../subscriptions-primary-buttons.tsx | 8 +- .../components/subscriptions-table.tsx | 1 + .../components/system-instances-panel.tsx | 79 +- .../components/system-tasks-panel.tsx | 214 +++--- .../src/features/system-info/index.tsx | 6 +- .../auth/bot-protection-section.tsx | 2 +- .../components/discovery-button.tsx | 3 +- .../components/preset-selector.tsx | 6 +- .../components/provider-form-dialog.tsx | 23 +- .../components/provider-table.tsx | 18 +- .../custom-oauth/custom-oauth-section.tsx | 3 +- .../system-settings/auth/oauth-section.tsx | 12 +- .../system-settings/auth/passkey-section.tsx | 18 +- .../components/form-dirty-indicator.tsx | 7 +- .../components/settings-page-context.tsx | 4 +- .../content/announcements-section.tsx | 55 +- .../content/api-info-section.tsx | 42 +- .../system-settings/content/chat-dialog.tsx | 4 +- .../content/chat-settings-section.tsx | 7 +- .../content/chat-settings-visual-editor.tsx | 4 +- .../content/dashboard-section.tsx | 18 +- .../system-settings/content/faq-section.tsx | 12 +- .../content/uptime-kuma-section.tsx | 12 +- .../general/channel-affinity/index.tsx | 44 +- .../channel-affinity/rule-editor-dialog.tsx | 21 +- .../general/checkin-settings-section.tsx | 2 +- .../general/pricing-section.tsx | 18 +- .../general/quota-settings-section.tsx | 2 +- .../general/system-info-section.tsx | 18 +- .../integrations/amount-discount-dialog.tsx | 4 +- .../amount-discount-visual-editor.tsx | 9 +- .../amount-options-visual-editor.tsx | 34 +- .../integrations/creem-product-dialog.tsx | 20 +- .../creem-products-visual-editor.tsx | 6 +- .../integrations/email-settings-section.tsx | 2 +- .../ionet-deployment-settings-section.tsx | 4 +- .../monitoring-settings-section.tsx | 18 +- .../integrations/payment-method-dialog.tsx | 6 +- .../payment-methods-visual-editor.tsx | 6 +- .../integrations/payment-settings-section.tsx | 16 +- .../waffo-pancake-settings-section.tsx | 8 +- .../integrations/waffo-settings-section.tsx | 6 +- .../integrations/worker-settings-section.tsx | 2 +- .../maintenance/log-settings-section.tsx | 25 +- .../maintenance/performance-section.tsx | 22 +- .../maintenance/update-checker-section.tsx | 2 +- .../models/channel-selector-dialog.tsx | 45 +- .../models/claude-settings-card.tsx | 2 +- .../models/conflict-confirm-dialog.tsx | 2 +- .../system-settings/models/constants.ts | 2 +- .../models/gemini-settings-card.tsx | 2 +- .../models/global-settings-card.tsx | 14 +- .../models/grok-settings-card.tsx | 2 +- .../models/group-ratio-form.tsx | 7 +- .../models/group-ratio-visual-editor.tsx | 41 +- .../models/group-special-usable-editor.tsx | 68 +- .../models/model-pricing-inputs.tsx | 2 +- .../models/model-pricing-sheet.tsx | 21 +- .../models/model-ratio-form.tsx | 12 +- .../models/model-ratio-table-columns.tsx | 27 +- .../models/model-ratio-visual-editor.tsx | 4 +- .../models/ratio-settings-card.tsx | 7 +- .../models/routing-reliability-section.tsx | 18 +- .../models/tiered-pricing-editor.tsx | 186 ++--- .../models/tool-price-settings.tsx | 14 +- .../models/upstream-ratio-sync-columns.tsx | 54 +- .../models/upstream-ratio-sync-table.tsx | 6 +- .../models/upstream-ratio-sync.tsx | 2 +- .../request-limits/rate-limit-dialog.tsx | 4 +- .../request-limits/rate-limit-section.tsx | 5 +- .../rate-limit-visual-editor.tsx | 4 +- .../request-limits/ssrf-section.tsx | 18 +- .../request-limits/token-limit-section.tsx | 2 +- .../components/columns/column-helpers.tsx | 72 +- .../columns/common-logs-columns.tsx | 288 ++++---- .../columns/drawing-logs-columns.tsx | 73 +- .../components/columns/task-logs-columns.tsx | 58 +- .../components/common-logs-filter-bar.tsx | 8 +- .../components/common-logs-header-actions.tsx | 6 +- .../components/common-logs-stats.tsx | 32 +- .../compact-date-time-range-picker.tsx | 27 +- .../dialogs/audio-preview-dialog.tsx | 15 +- .../components/dialogs/details-dialog.tsx | 154 ++-- .../components/dialogs/fail-reason-dialog.tsx | 6 +- .../components/dialogs/prompt-dialog.tsx | 10 +- .../components/logs-filter-toolbar.tsx | 233 +----- .../usage-logs/components/model-badge.tsx | 87 ++- .../components/usage-logs-mobile-card.tsx | 405 +++-------- .../components/usage-logs-table.tsx | 34 +- .../src/features/usage-logs/constants.ts | 110 +-- web/default/src/features/usage-logs/index.tsx | 4 +- .../src/features/usage-logs/lib/format.ts | 25 +- .../src/features/usage-logs/lib/status.ts | 8 +- web/default/src/features/users/api.ts | 4 +- .../components/data-table-row-actions.tsx | 2 +- .../dialogs/user-binding-dialog.tsx | 16 +- .../users/components/user-quota-dialog.tsx | 5 +- .../users/components/users-columns.tsx | 131 ++-- .../users/components/users-mutate-drawer.tsx | 20 +- .../components/users-primary-buttons.tsx | 4 +- .../features/users/components/users-table.tsx | 20 +- web/default/src/features/users/constants.ts | 2 +- .../components/affiliate-rewards-card.tsx | 97 ++- .../components/creem-products-section.tsx | 38 +- .../dialogs/billing-history-dialog.tsx | 65 +- .../dialogs/creem-confirm-dialog.tsx | 2 +- .../dialogs/payment-confirm-dialog.tsx | 2 +- .../components/dialogs/transfer-dialog.tsx | 4 +- .../wallet/components/recharge-form-card.tsx | 543 +++++++------- .../components/subscription-plans-card.tsx | 480 ++++++------ .../wallet/components/wallet-stats-card.tsx | 39 +- .../features/wallet/hooks/use-affiliate.ts | 2 +- .../wallet/hooks/use-billing-history.ts | 2 +- .../wallet/hooks/use-creem-payment.ts | 2 +- .../src/features/wallet/hooks/use-payment.ts | 21 +- .../features/wallet/hooks/use-redemption.ts | 2 +- .../wallet/hooks/use-waffo-pancake-payment.ts | 2 +- .../wallet/hooks/use-waffo-payment.ts | 2 +- web/default/src/features/wallet/index.tsx | 89 +-- .../src/features/wallet/lib/billing.ts | 6 +- .../src/features/wallet/lib/payment.ts | 4 +- web/default/src/features/wallet/lib/ui.tsx | 2 +- web/default/src/hooks/use-group-ratios.ts | 44 ++ web/default/src/hooks/use-table-url-state.ts | 35 +- web/default/src/i18n/locales/en.json | 36 +- web/default/src/i18n/locales/fr.json | 36 +- web/default/src/i18n/locales/ja.json | 36 +- web/default/src/i18n/locales/ru.json | 36 +- web/default/src/i18n/locales/vi.json | 36 +- web/default/src/i18n/locales/zh-TW.json | 36 +- web/default/src/i18n/locales/zh.json | 36 +- web/default/src/lib/avatar.ts | 27 +- web/default/src/lib/colors.ts | 46 +- web/default/src/lib/format.ts | 28 - .../routes/_authenticated/chat/$chatId.tsx | 2 +- .../routes/_authenticated/models/$section.tsx | 4 +- .../_authenticated/redemption-codes/index.tsx | 2 +- web/default/src/styles/theme.css | 93 +++ web/default/src/tanstack-table.d.ts | 19 +- 451 files changed, 7295 insertions(+), 7693 deletions(-) create mode 100644 web/default/src/components/data-table/layout/card-field.tsx create mode 100644 web/default/src/components/data-table/toolbar/filter-panel.tsx create mode 100644 web/default/src/components/design-system/README.md create mode 100644 web/default/src/components/design-system/alert-dialog.tsx create mode 100644 web/default/src/components/design-system/button.tsx create mode 100644 web/default/src/components/design-system/combobox-input.tsx create mode 100644 web/default/src/components/design-system/combobox.tsx create mode 100644 web/default/src/components/design-system/command.tsx create mode 100644 web/default/src/components/design-system/dialog.tsx create mode 100644 web/default/src/components/design-system/input-group.tsx create mode 100644 web/default/src/components/design-system/input.tsx create mode 100644 web/default/src/components/design-system/pagination.tsx create mode 100644 web/default/src/components/design-system/select.tsx create mode 100644 web/default/src/components/design-system/sidebar.tsx create mode 100644 web/default/src/components/design-system/table.tsx create mode 100644 web/default/src/components/design-system/tabs.tsx create mode 100644 web/default/src/components/design-system/toggle-group.tsx create mode 100644 web/default/src/components/design-system/toggle.tsx delete mode 100644 web/default/src/components/ui/dropdown-menu-events.ts delete mode 100644 web/default/src/components/ui/dropdown-menu.test.tsx create mode 100644 web/default/src/features/keys/components/api-key-card.tsx rename web/default/src/features/profile/components/{tabs/account-bindings-tab.tsx => connected-accounts-card.tsx} (65%) rename web/default/src/features/profile/components/{tabs/notification-tab.tsx => notification-settings-card.tsx} (71%) rename web/default/src/features/profile/components/{passkey-card.tsx => passkey-row.tsx} (52%) delete mode 100644 web/default/src/features/profile/components/profile-settings-card.tsx delete mode 100644 web/default/src/features/profile/components/two-fa-card.tsx create mode 100644 web/default/src/features/profile/components/two-fa-row.tsx create mode 100644 web/default/src/hooks/use-group-ratios.ts diff --git a/common/constants.go b/common/constants.go index 87d212f99..ebca3b108 100644 --- a/common/constants.go +++ b/common/constants.go @@ -80,7 +80,7 @@ var SessionCookieTrustedURLs []string var OptionMap map[string]string var OptionMapRWMutex sync.RWMutex -var ItemsPerPage = 10 +var ItemsPerPage = 20 var MaxRecentItems = 1000 var PasswordLoginEnabled = true diff --git a/controller/channel.go b/controller/channel.go index a2b5687ab..21e5f0f5d 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -1488,7 +1488,7 @@ func ManageMultiKeys(c *gin.Context) { page = 1 } if pageSize <= 0 { - pageSize = 50 // Default page size + pageSize = common.ItemsPerPage } // Statistics for all keys (unchanged by filtering) diff --git a/web/bun.lock b/web/bun.lock index d86f3a8b3..c702811db 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -83,7 +83,6 @@ "@tanstack/react-query": "^5.101.2", "@tanstack/react-router": "^1.170.17", "@tanstack/react-table": "^8.21.3", - "@tanstack/react-virtual": "^3.14.5", "@visactor/react-vchart": "^2.1.2", "@visactor/vchart": "^2.1.2", "ai": "^7.0.14", @@ -92,6 +91,7 @@ "class-variance-authority": "^0.7.1", "clsx": "catalog:", "cmdk": "^1.1.1", + "date-fns": "^4.4.0", "dayjs": "catalog:", "dompurify": "3.4.11", "i18next": "^26.3.4", @@ -112,7 +112,7 @@ "react-icons": "catalog:", "react-resizable-panels": "^4.12.0", "react-top-loading-bar": "^3.0.2", - "recharts": "3.9.1", + "recharts": "3.8.0", "shiki": "^4.3.0", "sonner": "^2.0.7", "sse.js": "catalog:", @@ -122,7 +122,6 @@ "tokenlens": "^1.3.1", "tw-animate-css": "^1.4.0", "use-stick-to-bottom": "^1.1.6", - "vaul": "^1.1.2", "zod": "^4.4.3", "zustand": "^5.0.14", }, @@ -958,8 +957,6 @@ "@tanstack/react-table": ["@tanstack/react-table@8.21.3", "", { "dependencies": { "@tanstack/table-core": "8.21.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww=="], - "@tanstack/react-virtual": ["@tanstack/react-virtual@3.14.5", "", { "dependencies": { "@tanstack/virtual-core": "3.17.3" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4EKRXh7zBLkbKbFmG3AUVkircuHd+7OdT1pocJSepxtfBd3qnrJgJ5rtPkRYyo9fmyVb2+pI2xPy5oYvMLQy6A=="], - "@tanstack/router-core": ["@tanstack/router-core@1.171.8", "", { "dependencies": { "@tanstack/history": "1.162.0", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-PbrTBbofFcacrH3RLgHYILRqTFnAGq+gXrXoA/vo7qUSkJpSO4GWfLtrtCahD4VayzRm19IPwcjPPLEugag6pw=="], "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.168.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.170.0", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg=="], @@ -974,8 +971,6 @@ "@tanstack/table-core": ["@tanstack/table-core@8.21.3", "", {}, "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg=="], - "@tanstack/virtual-core": ["@tanstack/virtual-core@3.17.3", "", {}, "sha512-8Np/TFELpI0ySuJoVmjvOrQYXH/8sTX0Biv9szhFhY39xOdAAY+smrMxjxOum/ux3eM8MUJQsEJ0/R0UpvC8dw=="], - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.162.0", "", {}, "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA=="], "@tiptap/core": ["@tiptap/core@3.24.0", "", { "peerDependencies": { "@tiptap/pm": "3.24.0" } }, "sha512-GTAsXAI32p4hEZgPzvUv2RPrObxamy9AFhmhG10fXSvN/cDUs8naEYVIqDV3Sh99jMwQEbTFKW1E1mcspsY6ow=="], @@ -1860,7 +1855,7 @@ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "immer": ["immer@11.1.8", "", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="], + "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], @@ -2538,7 +2533,7 @@ "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], - "recharts": ["recharts@3.9.1", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^11.1.8", "react-redux": "8.x.x || 9.x.x", "reselect": "5.2.0", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WMcwlXcB7l+BbxiEdyClkG+1sxrMHNZpzT577LEvU4+rXPd8oTAy1wXk72hnk2KOOmxuLvw3z5DtXT7HEAydtg=="], + "recharts": ["recharts@3.8.0", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ=="], "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], @@ -2590,7 +2585,7 @@ "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], "resize-observer-polyfill": ["resize-observer-polyfill@1.5.1", "", {}, "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="], @@ -2878,8 +2873,6 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="], - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], @@ -2926,6 +2919,8 @@ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "@base-ui/utils/reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], + "@codemirror/autocomplete/@codemirror/view": ["@codemirror/view@6.43.3", "", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-MwEwCAr/o0agJefhC2+reBv5kfOQpMcDRUNQrRYZgWlhH8IwQcerMZrpqWyUFSyO0ebgN2cnh/w87F7G4BGSng=="], "@codemirror/lang-html/@codemirror/view": ["@codemirror/view@6.43.3", "", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-MwEwCAr/o0agJefhC2+reBv5kfOQpMcDRUNQrRYZgWlhH8IwQcerMZrpqWyUFSyO0ebgN2cnh/w87F7G4BGSng=="], @@ -2974,6 +2969,8 @@ "@lobehub/ui/@shikijs/core": ["@shikijs/core@4.1.0", "", { "dependencies": { "@shikijs/primitive": "4.1.0", "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ=="], + "@lobehub/ui/immer": ["immer@11.1.8", "", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="], + "@lobehub/ui/katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="], "@lobehub/ui/lucide-react": ["lucide-react@1.17.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w=="], @@ -3016,7 +3013,7 @@ "@rc-component/trigger/@rc-component/portal": ["@rc-component/portal@2.2.0", "", { "dependencies": { "@rc-component/util": "^1.2.1", "clsx": "^2.1.1" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ=="], - "@reduxjs/toolkit/reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + "@reduxjs/toolkit/immer": ["immer@11.1.8", "", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="], "@rspack/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], @@ -3462,8 +3459,6 @@ "type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "@lobehub/ui/@base-ui/react/@base-ui/utils/reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], - "@lobehub/ui/motion/framer-motion/motion-dom": ["motion-dom@12.40.0", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg=="], "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], diff --git a/web/classic/src/components/settings/ChannelSelectorModal.jsx b/web/classic/src/components/settings/ChannelSelectorModal.jsx index 165aafafd..9879c392c 100644 --- a/web/classic/src/components/settings/ChannelSelectorModal.jsx +++ b/web/classic/src/components/settings/ChannelSelectorModal.jsx @@ -59,7 +59,7 @@ const ChannelSelectorModal = forwardRef( ) => { const [searchText, setSearchText] = useState(''); const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const isMobile = useIsMobile(); const [filteredData, setFilteredData] = useState([]); diff --git a/web/classic/src/components/table/channels/modals/MultiKeyManageModal.jsx b/web/classic/src/components/table/channels/modals/MultiKeyManageModal.jsx index fa7870dad..746ae5d4f 100644 --- a/web/classic/src/components/table/channels/modals/MultiKeyManageModal.jsx +++ b/web/classic/src/components/table/channels/modals/MultiKeyManageModal.jsx @@ -58,7 +58,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => { // Pagination states const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [total, setTotal] = useState(0); const [totalPages, setTotalPages] = useState(0); @@ -99,7 +99,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => { setKeyStatusList(data.keys || []); setTotal(data.total || 0); setCurrentPage(data.page || 1); - setPageSize(data.page_size || 10); + setPageSize(data.page_size || 20); setTotalPages(data.total_pages || 0); // Update statistics (these are always the overall statistics) diff --git a/web/classic/src/components/table/users/modals/UserSubscriptionsModal.jsx b/web/classic/src/components/table/users/modals/UserSubscriptionsModal.jsx index 455f7b188..47c68b703 100644 --- a/web/classic/src/components/table/users/modals/UserSubscriptionsModal.jsx +++ b/web/classic/src/components/table/users/modals/UserSubscriptionsModal.jsx @@ -84,7 +84,7 @@ const UserSubscriptionsModal = ({ visible, onCancel, user, t, onSuccess }) => { const [subs, setSubs] = useState([]); const [currentPage, setCurrentPage] = useState(1); - const pageSize = 10; + const pageSize = 20; const planTitleMap = useMemo(() => { const map = new Map(); diff --git a/web/classic/src/components/topup/modals/TopupHistoryModal.jsx b/web/classic/src/components/topup/modals/TopupHistoryModal.jsx index 1abd45df6..b01baf72f 100644 --- a/web/classic/src/components/topup/modals/TopupHistoryModal.jsx +++ b/web/classic/src/components/topup/modals/TopupHistoryModal.jsx @@ -61,7 +61,7 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => { const [topups, setTopups] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [keyword, setKeyword] = useState(''); const isMobile = useIsMobile(); diff --git a/web/classic/src/constants/channel.constants.js b/web/classic/src/constants/channel.constants.js index 278e75633..46879af7b 100644 --- a/web/classic/src/constants/channel.constants.js +++ b/web/classic/src/constants/channel.constants.js @@ -196,4 +196,4 @@ export const MODEL_FETCHABLE_CHANNEL_TYPES = new Set([ 1, 4, 14, 34, 17, 26, 27, 24, 47, 25, 20, 23, 31, 40, 42, 48, 43, ]); -export const MODEL_TABLE_PAGE_SIZE = 10; +export const MODEL_TABLE_PAGE_SIZE = 20; diff --git a/web/classic/src/constants/common.constant.js b/web/classic/src/constants/common.constant.js index 316356631..f5398d965 100644 --- a/web/classic/src/constants/common.constant.js +++ b/web/classic/src/constants/common.constant.js @@ -17,7 +17,7 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -export const ITEMS_PER_PAGE = 10; // this value must keep same as the one defined in backend! +export const ITEMS_PER_PAGE = 20; // this value must keep same as the one defined in backend! export const DEFAULT_ENDPOINT = '/api/pricing'; diff --git a/web/classic/src/constants/dashboard.constants.js b/web/classic/src/constants/dashboard.constants.js index 7e930b479..337cdcf33 100644 --- a/web/classic/src/constants/dashboard.constants.js +++ b/web/classic/src/constants/dashboard.constants.js @@ -144,6 +144,6 @@ export const STORAGE_KEYS = { export const DEFAULTS = { PAGE_SIZE: 20, CHART_HEIGHT: 96, - MODEL_TABLE_PAGE_SIZE: 10, + MODEL_TABLE_PAGE_SIZE: 20, MAX_TREND_POINTS: 7, }; diff --git a/web/classic/src/hooks/subscriptions/useSubscriptionsData.jsx b/web/classic/src/hooks/subscriptions/useSubscriptionsData.jsx index f426501b9..18b968f39 100644 --- a/web/classic/src/hooks/subscriptions/useSubscriptionsData.jsx +++ b/web/classic/src/hooks/subscriptions/useSubscriptionsData.jsx @@ -32,7 +32,7 @@ export const useSubscriptionsData = () => { // Pagination (client-side for now) const [activePage, setActivePage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); // Drawer states const [showEdit, setShowEdit] = useState(false); diff --git a/web/classic/src/pages/Setting/Chat/SettingsChats.jsx b/web/classic/src/pages/Setting/Chat/SettingsChats.jsx index fc1dae4cd..a9d9ee1f0 100644 --- a/web/classic/src/pages/Setting/Chat/SettingsChats.jsx +++ b/web/classic/src/pages/Setting/Chat/SettingsChats.jsx @@ -469,7 +469,7 @@ export default function SettingsChats(props) { dataSource={filteredConfigs} rowKey='id' pagination={{ - pageSize: 10, + pageSize: 20, showSizeChanger: false, showQuickJumper: true, showTotal: (total, range) => diff --git a/web/classic/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx b/web/classic/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx index 8116cfc0f..531d7e284 100644 --- a/web/classic/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx +++ b/web/classic/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx @@ -59,7 +59,7 @@ const SettingsAPIInfo = ({ options, refresh }) => { color: 'blue', }); const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [selectedRowKeys, setSelectedRowKeys] = useState([]); // 面板启用状态 state diff --git a/web/classic/src/pages/Setting/Dashboard/SettingsAnnouncements.jsx b/web/classic/src/pages/Setting/Dashboard/SettingsAnnouncements.jsx index c61102f15..11f51da3e 100644 --- a/web/classic/src/pages/Setting/Dashboard/SettingsAnnouncements.jsx +++ b/web/classic/src/pages/Setting/Dashboard/SettingsAnnouncements.jsx @@ -67,7 +67,7 @@ const SettingsAnnouncements = ({ options, refresh }) => { extra: '', }); const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [selectedRowKeys, setSelectedRowKeys] = useState([]); // 面板启用状态 diff --git a/web/classic/src/pages/Setting/Dashboard/SettingsFAQ.jsx b/web/classic/src/pages/Setting/Dashboard/SettingsFAQ.jsx index 3c6f69696..dfc81ebc6 100644 --- a/web/classic/src/pages/Setting/Dashboard/SettingsFAQ.jsx +++ b/web/classic/src/pages/Setting/Dashboard/SettingsFAQ.jsx @@ -56,7 +56,7 @@ const SettingsFAQ = ({ options, refresh }) => { answer: '', }); const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [selectedRowKeys, setSelectedRowKeys] = useState([]); // 面板启用状态 diff --git a/web/classic/src/pages/Setting/Dashboard/SettingsUptimeKuma.jsx b/web/classic/src/pages/Setting/Dashboard/SettingsUptimeKuma.jsx index 3f3d5dd8e..ec40c999e 100644 --- a/web/classic/src/pages/Setting/Dashboard/SettingsUptimeKuma.jsx +++ b/web/classic/src/pages/Setting/Dashboard/SettingsUptimeKuma.jsx @@ -56,7 +56,7 @@ const SettingsUptimeKuma = ({ options, refresh }) => { slug: '', }); const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [panelEnabled, setPanelEnabled] = useState(true); diff --git a/web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx b/web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx index 20b0bdf31..26e769ddd 100644 --- a/web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx +++ b/web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx @@ -122,7 +122,7 @@ export default function UpstreamRatioSync(props) { // 分页相关状态 const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(20); // 搜索相关状态 const [searchKeyword, setSearchKeyword] = useState(''); diff --git a/web/classic/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js b/web/classic/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js index ba9efdfce..22c935f64 100644 --- a/web/classic/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js +++ b/web/classic/src/pages/Setting/Ratio/hooks/useModelPricingEditorState.js @@ -23,7 +23,7 @@ import { splitBillingExprAndRequestRules, } from '../components/requestRuleExpr'; -export const PAGE_SIZE = 10; +export const PAGE_SIZE = 20; export const PRICE_SUFFIX = '$/1M tokens'; const EMPTY_CANDIDATE_MODEL_NAMES = []; diff --git a/web/default/.oxlintrc.json b/web/default/.oxlintrc.json index 9faf2d7e3..8e607707c 100644 --- a/web/default/.oxlintrc.json +++ b/web/default/.oxlintrc.json @@ -44,6 +44,33 @@ "no-duplicate-imports": "error", "no-nested-ternary": "error", "no-new-wrappers": "error", + "no-restricted-imports": [ + "error", + { + "patterns": [ + { + "group": [ + "@/components/ui/alert-dialog", + "@/components/ui/button", + "@/components/ui/combobox", + "@/components/ui/combobox-input", + "@/components/ui/command", + "@/components/ui/dialog", + "@/components/ui/input", + "@/components/ui/input-group", + "@/components/ui/pagination", + "@/components/ui/select", + "@/components/ui/sidebar", + "@/components/ui/table", + "@/components/ui/tabs", + "@/components/ui/toggle", + "@/components/ui/toggle-group" + ], + "message": "Import policy-bearing controls from @/components/design-system so responsive sizing stays centralized." + } + ] + } + ], "no-regex-spaces": "error", "no-unsafe-finally": "warn", "no-unsafe-negation": "error", @@ -196,6 +223,19 @@ "react/only-export-components": "off" } }, + { + "files": ["src/components/design-system/**/*.{ts,tsx}"], + "rules": { + "no-restricted-imports": "off", + "react/only-export-components": [ + "error", + { + "allowConstantExport": true, + "allowExportNames": ["useComboboxAnchor", "useSidebar"] + } + ] + } + }, { "files": ["**/scripts/**/*.{js,mjs,cjs}"], "rules": { diff --git a/web/default/AGENTS.md b/web/default/AGENTS.md index fcb732f9e..9605a6d35 100644 --- a/web/default/AGENTS.md +++ b/web/default/AGENTS.md @@ -145,6 +145,19 @@ - 标题三档契约:页题 `text-lg font-semibold tracking-tight`;卡片/区块题 `text-base font-semibold`;面板/小节题 `text-sm font-medium`。禁止响应式跳字号的标题(如 `text-base sm:text-lg`)。 - 表头不使用 `uppercase + tracking-wider`。 +**控件尺寸(移动优先响应式)** + +- `src/components/ui/` 是 shadcn CLI 管理的原始源码层:允许通过 `bunx --bun shadcn@latest add --dry-run/--diff` 审查并合并上游更新,但**禁止**在这里加入产品级响应式策略、业务语义或一次性样式。不得用 `--overwrite` 覆盖本地代码,除非用户明确批准。 +- `src/components/design-system/` 是产品设计系统适配层,只包装确实承载稳定跨站策略的控件(尺寸、产品级 variant、复合控件内部的策略传递);不要为了“统一入口”代理所有 shadcn 组件。业务代码对受管控件必须从该目录导入,`.oxlintrc.json` 的 `no-restricted-imports` 会阻止绕过边界。 +- 控件标准高度是响应式的:**手机(<640px)28px,桌面(`sm:` 起)32px**。断点由 `design-system` 适配层统一注入(`Button`/`Toggle` default 与 icon 档、`Input`、`InputGroup`、`SelectTrigger` default 档、`TabsList`、`Combobox`、`CommandInput`、分页、表头 `h-9 sm:h-10`),业务代码**不写**断点类即可获得正确尺寸。 +- 独立场景(工具栏、表单、对话框、卡片/列表行操作、页脚)一律用默认档,**禁止** `size='sm'`,也**禁止**用 `className` 写 `h-7`/`h-8`/`h-9`/`size-8` 等钉死尺寸——钉死的类会在某一断点上与内置的 `sm:h-8`/`sm:size-8` 打架(twMerge 不会跨断点去重)。 +- 密集场景(表格单元格内、行内编辑器一行多控件、紧凑抽屉行)统一 `size='sm'`(28px 恒定);微型场景用 `xs`(24px 恒定)。`sm`/`xs`/`icon-sm`/`icon-xs` 是**固定档**,不随断点缩放;需要"固定自定义尺寸"的特例(如覆盖层小按钮)应以固定档为基底再覆写,绝不以 default/icon 为基底。 +- 图标按钮用 `icon`(28→32px 响应式)/`icon-sm`(28px 恒定)/`icon-xs`(24px 恒定)并带 `aria-label`;禁止 `size-*`、`h-* w-*`、`p-0` 组合模拟。头部全局操作(侧栏开关/主题/语言/通知/头像)统一 `icon`。 +- CTA 档 `xl`(40→44px 响应式):auth 页主按钮(登录/注册/OTP/重置/OAuth/Passkey)与营销页 hero 按钮统一 `size='xl'`,禁止再写 `h-11`。 +- 确需自定义高度时必须**成对覆盖两个断点**:内容自适应写 `h-auto sm:h-auto`,特大输入写 `h-9 sm:h-10` 这类成对值;`min-h-*` 不与内置高度冲突可单独使用。flex-wrap 的 `TabsList` 用 `group-data-horizontal/tabs:h-auto sm:group-data-horizontal/tabs:h-auto`。 +- 手机端的“视觉紧凑”不得牺牲可操作性:所有指针目标至少满足 WCAG 2.2 AA 的 24×24 CSS px;相邻 28px 控件必须保留足够间距,主要提交/认证/高频操作优先使用 40px 的 `xl` 档。不要在手机端新增小于 `xs` 的交互目标。 +- 仅有例外:日历格(`--cell-size`)、OTP 输入格、营销页装饰元素。新增例外须在评审说明。 + **圆角与阴影** - 圆角单源 `--radius`(默认 0.625rem),组件用派生档 `rounded-md/lg/xl`;禁止在业务代码里写死圆角像素或混用 `rounded-2xl`/`rounded-4xl` 表达同类容器。 @@ -163,7 +176,7 @@ ### 3.11 文件组织 - **功能模块**:置于 `src/features//`,内含 `components/`、`lib/`、`hooks/`,以及按需的 `api.ts`、`types.ts`、`constants.ts`、入口组件等。 -- **通用**:通用组件放 `src/components/`,通用工具与类型放 `src/lib/`;组件文件 PascalCase,工具/类型文件 kebab-case 或 `types.ts`,类型使用 PascalCase 命名并 `export type`。 +- **通用**:通用组件放 `src/components/`,其中 `components/ui/` 仅存 shadcn 原始源码、`components/design-system/` 存产品策略适配器;通用工具与类型放 `src/lib/`。组件文件 PascalCase,工具/类型文件 kebab-case 或 `types.ts`,类型使用 PascalCase 命名并 `export type`。 ### 3.12 可访问性 @@ -214,3 +227,6 @@ - **2026-01-31**:在 3.2 中补充「类型检查」要求:改动 TS/TSX 后须执行 typecheck 并修复至无错。 - **2026-06-21**:在 3.2 中补充「Lint 检查」要求:完成代码改动前须修复所涉及文件的所有 lint error。 - **2026-07-11**:新增 3.10.1「设计系统纪律」:语义色硬约束(去彩虹徽章/硬编码调色板)、排版契约(字号/字重/标题三档/mono 边界)、圆角单源、后台零入场动效、徽章与图标单轨。 +- **2026-07-11**:3.10.1 新增「控件尺寸」:控件默认 32px(`h-8`)单一标准;独立场景禁用 `size='sm'` 与 `className` 高度硬改;密集行内统一 `sm`、微型 `xs`;图标按钮走 `icon/icon-sm/icon-xs` 档位。 +- **2026-07-11**:控件尺寸升级为移动优先响应式:设计系统统一手机 28px / 桌面 32px(default、icon 档随断点缩放,`sm`/`xs`/`icon-sm`/`icon-xs` 恒定);新增 CTA 档 `xl`(40→44px),auth 与 hero 主按钮统一迁入;调用点禁止钉死 `h-*`/`size-*`,自定义高度必须成对覆盖两个断点(如 `h-auto sm:h-auto`)。 +- **2026-07-11**:将响应式尺寸策略从 shadcn 原始源码迁至 `components/design-system/` 适配层;业务受管控件统一改从适配层导入,并由 lint 禁止绕过;`components/ui/` 恢复为可用 CLI `--diff` 维护的上游源码边界。 diff --git a/web/default/package.json b/web/default/package.json index 67906f26f..736a64672 100644 --- a/web/default/package.json +++ b/web/default/package.json @@ -34,7 +34,6 @@ "@tanstack/react-query": "^5.101.2", "@tanstack/react-router": "^1.170.17", "@tanstack/react-table": "^8.21.3", - "@tanstack/react-virtual": "^3.14.5", "@visactor/react-vchart": "^2.1.2", "@visactor/vchart": "^2.1.2", "ai": "^7.0.14", @@ -43,6 +42,7 @@ "class-variance-authority": "^0.7.1", "clsx": "catalog:", "cmdk": "^1.1.1", + "date-fns": "^4.4.0", "dayjs": "catalog:", "dompurify": "3.4.11", "i18next": "^26.3.4", @@ -63,7 +63,7 @@ "react-icons": "catalog:", "react-resizable-panels": "^4.12.0", "react-top-loading-bar": "^3.0.2", - "recharts": "3.9.1", + "recharts": "3.8.0", "shiki": "^4.3.0", "sonner": "^2.0.7", "sse.js": "catalog:", @@ -73,7 +73,6 @@ "tokenlens": "^1.3.1", "tw-animate-css": "^1.4.0", "use-stick-to-bottom": "^1.1.6", - "vaul": "^1.1.2", "zod": "^4.4.3", "zustand": "^5.0.14" }, diff --git a/web/default/src/components/ai-elements/actions.tsx b/web/default/src/components/ai-elements/actions.tsx index 9d3c2da43..93746fb84 100644 --- a/web/default/src/components/ai-elements/actions.tsx +++ b/web/default/src/components/ai-elements/actions.tsx @@ -20,7 +20,7 @@ For commercial licensing, please contact support@quantumnous.com import type { ComponentProps } from 'react' -import { Button } from '@/components/ui/button' +import { Button } from '@/components/design-system/button' import { Tooltip, TooltipContent, @@ -48,13 +48,13 @@ export const Action = ({ label, className, variant = 'ghost', - size = 'sm', + size = 'icon', ...props }: ActionProps) => { const button = (