Commit Graph
100 Commits
Author SHA1 Message Date
t0ng7u 94f4d58aea Merge remote-tracking branch 'origin/alpha' into alpha 2025-07-18 09:38:19 +08:00
t0ng7u 38bc3badb1 📚 docs: add comprehensive Web-interface API reference (excluding Relay)
A new Markdown file `docs/api/web_api.md` has been added that documents all backend REST endpoints used by the Web UI.

Details:
• Lists every `/api`, `/dashboard`, and `/v1/dashboard` route relevant to the Web front-end
• Excludes every Relay-specific path to keep scope focused on Web operations
• Groups endpoints by functional module (initialisation, public info, user, channel, token, logging, etc.)
• Specifies HTTP method, path, required auth level, and concise description for each entry
• Includes an auth-level legend and update-date placeholder for future maintenance

No application logic was modified; this is documentation-only and improves developer onboarding and API discoverability.
2025-07-17 23:50:56 +08:00
t0ng7u 9f957da5ac 🌐 feat: add configurable USD exchange-rate support across backend & frontend
Backend
- setting/payment.go: introduce default `USDExchangeRate` (7.3)
- model/option.go:
  • inject `USDExchangeRate` into `InitOptionMap`
  • persist & sync value in `updateOptionMap`
- controller/misc.go: expose `usd_exchange_rate` via `/api/status`

Frontend
- OperationSetting.js & SettingsGeneral.js:
  • extend state/inputs with `USDExchangeRate`
  • add form field “美元汇率 (non-top-up rate, pricing only)”
- ModelPricing.js already consumes `status.usd_exchange_rate`; no change needed

API
- Administrators can update the rate via `PUT /api/option` (key: `USDExchangeRate`)
- All clients receive the latest rate through `GET /api/status`

This closes the end-to-end flow for displaying model prices in both USD and CNY based on a configurable exchange rate.
2025-07-17 23:04:45 +08:00
t0ng7u 4789b412fe 💸 feat(model-pricing): add “show with recharge price” toggle and USD/CNY selector
* Introduced `showWithRecharge` switch in the actions bar to display model prices based on recharge cost.
* Added a `Select` dropdown (USD / CNY) that appears only when the recharge-price mode is enabled.
* Implemented `displayPrice()` helper to:
  * Convert USD prices to recharge prices using `status.price` and `status.usd_exchange_rate`.
  * Format output according to the selected currency.
* Updated price rendering for both quota types to use the new helper and respect K/M unit conversion.
* Removed the old currency switch from the header, retaining only the K/M unit toggle.
* Extended `SearchAndActions` memo dependencies; imported `Select` from Semi UI.
* Minor refactors and comment clean-up.

No breaking changes.
2025-07-17 22:07:06 +08:00
t0ng7u 082e57173f 💱 feat(model-pricing): add currency (USD/CNY) & token unit (M/K) toggles to Model Pricing table
* Introduced a currency switch to toggle prices between USD and CNY.
  * CNY prices are calculated by multiplying USD prices with the site-wide `price` rate from `/api/status`.
* Added a second switch to display prices per 1 M tokens or per 1 K tokens.
  * When “K” is selected, prices are divided by 1 000 and labels are updated accordingly.
* Extended component state with `currency` and `tokenUnit` variables.
* Integrated `StatusContext` to retrieve and memoize the current exchange rate.
* Updated price rendering logic and labels to reflect selected currency and token unit.
* Minor UI tweaks: kept Switch components compact and aligned with the table header.

No breaking changes.
2025-07-17 19:07:11 +08:00
t0ng7u bf6e1bed0c 🧹 refactor(PageLayout): remove unused state variables
Removed unused `userState` and `statusState` bindings from `PageLayout.js`
while retaining their dispatch functions for authentication and status
management. Confirmed correct use of array destructuring to skip the
unused toggle function returned by `useSidebarCollapsed`.

This change introduces no functional differences and solely improves
readability and maintainability.
2025-07-16 04:50:23 +08:00
t0ng7u 409a3eac5a 🐛 fix: SSR hydration mismatch in mobile detection & clean up sidebar style
- Added a server-snapshot fallback (`() => false`) to `useIsMobile` to ensure
  consistent results between server-side rendering and the browser, preventing
  hydration mismatches.
- Removed a redundant ternary in `PageLayout` sidebar styles, replacing
  `isMobile ? 'fixed' : 'fixed'` with a single `'fixed'` value for clarity.

These changes improve SSR reliability and tidy up inline styles without
affecting runtime functionality.
2025-07-16 04:46:31 +08:00
t0ng7u 94e0c0d838 🎨 feat(sidebar): replace custom collapse button with Semi UI Button
* Replaced the handmade collapse/expand div with Semi UI `<Button>`
  * Uses `theme="outline"` and `type="tertiary"` for outlined style
  * Shows icon + text when sidebar is expanded, icon-only when collapsed
* Added `iconOnly` prop and dynamic padding to remove extra text area in collapsed state
* Ensured full-width layout with consistent padding for both states
* Updated imports to include `Button` from `@douyinfe/semi-ui`
* Maintains tooltip content for accessibility and better UX
2025-07-16 04:35:19 +08:00
t0ng7u 7ee3a7d65a ♻️ refactor(auth, ui): simplify Loading component & optimize OAuth2Callback flow
* Removed `prompt` prop from `Loading` and switched to built-in Spin indicator with default size `small`
* Dropped overlay background to make the spinner more reusable
* Replaced custom text span; callers can now supply tip via their own UI if needed
* Cleaned up `OAuth2Callback`:
  - Eliminated unused state/variables
  - Added MAX_RETRIES with incremental back-off
  - Centralized error handling via try/catch
  - Streamlined navigation logic on success/failure
  - Updated imports to match new Loading signature

BREAKING CHANGE: `Loading` no longer accepts a `prompt` prop. Update all invocations accordingly.
2025-07-16 04:21:13 +08:00
t0ng7u a3f098c50b feat(HeaderBar): ensure skeleton shows ≥500 ms and waits for real status data
The header’s skeleton screen now remains visible for at least 500 ms and
only disappears after `/api/status` has successfully populated
`StatusContext`.

Changes include:
• Added `loadingStartRef` to record the mount time.
• Reworked loading effect to compute the remaining delay based on the
  elapsed time and the presence of real status data.
• Removed the previous fixed‐timer logic, preventing premature content
  rendering and improving perceived loading consistency across pages.
2025-07-16 04:02:05 +08:00
t0ng7u c4718a1e41 📱 feat(ui): auto-close sidebar on mobile after menu navigation
Adds a smoother mobile experience by automatically closing the sidebar
drawer once a menu item is tapped.

### Details
* SiderBar
  * Introduce `onNavigate` prop and invoke it on every `<Link>` click.
  * Remove unused `useIsMobile` hook and related `isMobile` variable.
* PageLayout
  * Pass `onNavigate` callback to `SiderBar` that sets `drawerOpen` to
    `false` when on mobile, ensuring the sidebar collapses after
    navigation.

This eliminates the “isMobile declared but never used” warning and
aligns the behaviour of the sidebar with common mobile UX expectations.
2025-07-16 03:52:40 +08:00
t0ng7u 527b9966a9 💄 style(layout): add horizontal padding to top-offset divs for consistent spacing
Replaced every instance of
<div className="mt-[64px]">
with
<div className="mt-[64px] px-2">
to provide uniform horizontal padding across pages. No functional changes—visual layout improvement only.
2025-07-16 03:42:19 +08:00
t0ng7u eab067678b 🖌️ feat(ui): always show colorful console banner for NewAPI in every environment
Add a branded console log to `web/src/index.js` that prints:

“We ❤ NewAPI  Github: https://github.com/QuantumNous/new-api”

Changes include:
• Remove the `NODE_ENV` guard so the banner appears in both development and production.
• Increase font size to 24 px and keep “NewAPI” in bold green for stronger branding.

This is a purely visual/developer-experience enhancement—no runtime behavior is affected.
2025-07-16 03:38:06 +08:00
t0ng7u 9c36aaa277 🔧 chore(router): enable React Router v7 future flags to suppress warnings
Add `future` prop to `BrowserRouter` in `web/src/index.js` with
`v7_startTransition` and `v7_relativeSplatPath` turned on.
This opts the app into upcoming React Router v7 behavior, removes
console warnings, and readies the codebase for a smoother future upgrade.

No runtime behavior changes; developer-experience improvement only.
2025-07-16 03:08:04 +08:00
t0ng7u edb4f2f654 📱 refactor(web): remove legacy isMobile util and migrate to useIsMobile hook
BREAKING CHANGE:
helpers/utils.js no longer exports `isMobile()`.
Any external code that relied on this function must switch to the `useIsMobile` React hook.

Summary
-------
1. Deleted the obsolete `isMobile()` function from helpers/utils.js.
2. Introduced `MOBILE_BREAKPOINT` constant and `matchMedia`-based detection for non-React contexts.
3. Reworked toast positioning logic in utils.js to rely on `matchMedia`.
4. Updated render.js:
   • Removed isMobile import.
   • Added MOBILE_BREAKPOINT detection in `truncateText`.
5. Migrated every page/component to the `useIsMobile` hook:
   • Layout: HeaderBar, PageLayout, SiderBar
   • Pages: Home, Detail, Playground, User (Add/Edit), Token, Channel, Redemption, Ratio Sync
   • Components: ChannelsTable, ChannelSelectorModal, ConflictConfirmModal
6. Purged all remaining `isMobile()` calls and legacy imports.
7. Added missing `const isMobile = useIsMobile()` declarations where required.

Benefits
--------
• Unifies mobile detection with a React-friendly hook.
• Eliminates duplicated logic and improves maintainability.
• Keeps non-React helpers lightweight by using `matchMedia` directly.
2025-07-16 02:54:58 +08:00
t0ng7u 0480d43bcd 🚀 feat(frontend): add robust boolean handling across settings pages
Summary
-------
1. Introduced a reusable `toBoolean` utility (`web/src/helpers/boolean.js`) that converts
   strings (`'true'/'false'`, `'1'/'0'`), numbers, and native booleans to a proper boolean.
2. Re-exported `toBoolean` via `web/src/helpers/index.js` for simple one-line imports.

Refactors
---------
• Systematically replaced all legacy `item.value === 'true'` checks with `toBoolean(item.value)` in
  the following components:
  – `SystemSetting.js`
  – `OperationSetting.js`
  – `PaymentSetting.js`
  – `RatioSetting.js`
  – `RateLimitSetting.js`
  – `ModelSetting.js`
  – `DrawingSetting.js`
  – `DashboardSetting.js`
  – `ChatsSetting.js`

• Unified import statements to
  `import { …, toBoolean } from '../../helpers';`
  removing redundant `../../helpers/boolean` paths.

Why
---
SQLite sometimes returns `1/0` or boolean literals instead of the string `'true'/'false'`, causing
checkbox states to reset on page reload. The new utility guarantees consistent boolean parsing,
fixing the issue across all environments (SQLite, MySQL, etc.) while improving code clarity.
2025-07-15 17:18:48 +08:00
t0ng7u 3425b6ebb6 🐛 fix: multi-key channel sync and Vertex-AI key-upload edge cases
Backend
1. controller/channel.go
   • Always hydrate `ChannelInfo` from DB in `UpdateChannel`, keeping `IsMultiKey` true so `MultiKeySize` is recalculated.

2. model/channel.go
   • getKeys(): accept both newline-separated keys and JSON array (`[ {...}, {...} ]`).
   • Update(): reuse new parser-logic to recalc `MultiKeySize`; prune stale indices in `MultiKeyStatusList`.

Frontend
1. pages/Channel/EditChannel.js
   • `handleVertexUploadChange`
     – Reset `vertexErroredNames` on every change so the “ignored files” prompt always re-appears.
     – In single-key mode keep only the last file; in batch mode keep all valid files.
     – Parse files, display “以下文件解析失败,已忽略:…”.
   • Batch-toggle checkbox
     – When switching from batch→single while multiple files are present, show a confirm dialog and retain only the first file (synchronises state, form and local caches).
   • On opening the “new channel” side-sheet, clear `vertexErroredNames` to restore error prompts.

Result
• “已启用 x/x” count updates immediately after editing multi-key channels.
• Vertex-AI key upload works intuitively: proper error feedback, no duplicated files, and safe down-switch from batch to single mode.
2025-07-15 12:02:04 +08:00
t0ng7u df85acb80f style(ui): Change the size of the icons in the dashboard to normal 2025-07-14 23:47:45 +08:00
t0ng7u 194e73b1de feat: Add fade-in animation for greeting message
- Add greetingVisible state to control animation trigger
- Implement fade-in effect with 1-second smooth transition
- Set 100ms delay before animation starts
- Apply opacity transition from 0 to 1 using ease-in-out timing
- Enhance user experience with smooth visual feedback on page load

The greeting message now appears with an elegant fade-in animation,
transitioning from transparent to fully visible over 1 second,
providing better visual appeal and user engagement.
2025-07-14 23:42:03 +08:00
t0ng7u 7c17f380e4 feat(ui): enhance loading states and fix layout issues
- Fix uptime service card bottom spacing by removing flex layout
- Replace IconRotate with IconSend for request count to better represent semantic meaning
- Add skeleton loading placeholders for all dashboard statistics with 500ms minimum duration
- Unify avgRPM and avgTPM calculation with consistent NaN handling
- Standardize skeleton usage across HeaderBar and Detail components with active animations
- Remove unnecessary empty wrapper elements in skeleton implementations
- Remove gradient styling from system name in header

The changes improve user experience with consistent loading states, better semantic icons,
and eliminate visual layout issues in the dashboard cards.
2025-07-14 23:31:01 +08:00
t0ng7u 42f38b823a 💄 fix(ui): improve uptime card layout and request count icon
- Remove flex layout from uptime service card to eliminate bottom spacing
- Remove flex-1 and mt-auto classes that caused unnecessary stretching
- Replace IconRotate with IconSend for request count to better represent the semantic meaning
- Legend now sits directly below content instead of being pushed to bottom

Fixes the visual issue where uptime service availability card had unwanted white space at the bottom when content was minimal.
2025-07-14 22:52:00 +08:00
t0ng7u 3338b1f598 feat: implement backend channel duplication & streamline frontend copy flow
Add a dedicated backend endpoint to clone an existing channel (including its key) and
replace all previous front-end cloning logic with a single API call.

Backend
• controller/channel.go
  – add CopyChannel: safely clone a channel, reset balance/usage, append name suffix,
    preserve key, create abilities, return new ID.
  – supports optional query params: `suffix`, `reset_balance`.
• router/api-router.go
  – register POST /api/channel/copy/:id (secured by AdminAuth).
• model interaction uses BatchInsertChannels to ensure transactional integrity.

Frontend
• ChannelsTable.js
  – simplify copySelectedChannel: call /api/channel/copy/{id} and refresh list.
  – remove complex field-manipulation & key-fetching logic.
  – improved error handling.

Security & stability
• All cloning done server-side; sensitive key never exposed to client.
• Route inherits existing admin middleware.
• Graceful JSON responses with detailed error messages.
2025-07-14 21:54:53 +08:00
t0ng7u 0bd826c48b 🚀 feat(detail): enhance API Info list with jump button & responsive layout
* Added an “Jump” (`ExternalLink`) tag to each API entry that opens the URL in a new tab
* Placed “Speed Test” and “Jump” tags on the same line as the route
  * Route is left-aligned; tags are right-aligned and wrap to next line when space is insufficient
* Inserted `<Divider />` between API items to improve visual separation
* Tweaked flex gaps and utility classes for consistent spacing and readability
2025-07-14 20:22:09 +08:00
t0ng7u 33f4e4048c 🍎style(button): Optimize the colors of all buttons to reduce visual fatigue 2025-07-14 19:49:40 +08:00
t0ng7u 5c55da29e7 🐛 fix: Ensure channel tooltip displays correctly in LogsTable
* Refactored the render logic of the **Channel** column
  * Wrapped tooltip around the first `Tag` only to restore hover behavior when multi-key mode is enabled
  * Added i18n-friendly fallback (`t('未知渠道')`) for cases where `channel_name` is missing
* Improves user experience for administrators by reliably showing channel names even with multiple keys
2025-07-14 13:12:40 +08:00
t0ng7u 3c3247f7bd 🎨 feat(ui): Change TolenTables Progress Style marginTop 0 to 1px 2025-07-13 19:34:20 +08:00
t0ng7uand[Your Name] 3df78868ad 🎨 feat(ui): Enhance model dropdowns with icons in Token & Channel editors
Summary
• Added visual model icons to dropdown options in both Token (`EditToken.js`) and Channel (`EditChannel.js`) editors.

Details
1. Token Editor
   - Imported `getModelCategories` from helpers.
   - Re-built Model Limits option list to prepend the matching icon to each model label.

2. Channel Editor
   - Imported `getModelCategories`.
   - Extended model‐option construction to include icons, unifying behaviour with Token editor.
   - Maintained existing logic for merging origin options and user-selected models.

Benefits
• Provides immediate visual identification of model vendors.
• Aligns UX with existing icon usage across the application, improving consistency and clarity.
• No functional changes to data handling; purely UI/UX enhancement.

Co-authored-by: [Your Name]
2025-07-13 19:32:01 +08:00
t0ng7u 7731a02082 💄 feat(ui): Enhance TokensTable quota presentation and clean up code
Summary
• Re-architect the status column to embed quota information directly inside the Tag suffix.
• Consolidate rendering logic for clearer structure and easier maintenance.

Details
• Moved the quota Progress bar under the remaining / total text, inside `quotaSuffix`.
• Added “Unlimited” label for tokens with `unlimited_quota`; hides Progress and Tooltip in this case.
• Tightened vertical spacing with a flex container (`gap-[2px]`, `leading-none`) and removed extra wrappers; Progress now has zero top/bottom margin and full-width style.
• Refactored variables:
  – Replaced `tagNode/bodyContent` with a single `content` node.
  – Wrapped `content` with Tooltip only when quota is limited.
• Visual tweaks:
  – Applied `size='large'` to the Tag for better alignment.
  – Ensured consistent color via shared `getProgressColor`.
• Deleted obsolete comments and redundant code.

Result
Improves readability of the component and delivers a cleaner, more compact quota display.
2025-07-13 19:14:43 +08:00
t0ng7u 301c78d9f3 🔍 feat(edit-token): add search capability to model limit selector
Changes:
• `Form.Select` for “Model Limits” now supports in-dropdown searching (`filter` + `searchPosition='dropdown'`) enabling quick model lookup.
• Removed `maxTagCount` to display all selected models without truncation.

Benefit: simplifies selecting specific models when the list is large, improving usability during token creation/editing.
2025-07-13 18:25:05 +08:00
t0ng7u ac5f028d10 🎨 style(tokens-table): enhance quota usage progress bar UI
Improves the “Balance” column in `TokensTable`:

• Re-styled progress bar: wider container (`w-[140px]`) and `showInfo` enabled to display percentage label.
• Added `format={() => \`\${percent}%\`}` for precise percentage feedback.
• Maintains dynamic color mapping (success / warning / danger) for clarity.

Result: clearer, more informative visual representation of remaining token quota.
2025-07-13 18:10:59 +08:00
t0ng7u c244837719 🔄 fix(tables): keep current page after edits & auto-fallback when page becomes empty
Includes ChannelsTable, RedemptionsTable and UsersTable:

• Refactor `refresh(page = activePage)` in all three tables so data reloads the requested (or current) page instead of forcing page 1.
• On single-row deletion (and bulk deletion in ChannelsTable):
  – Refresh current page immediately.
  – If the refreshed page has no data and `activePage > 1`, automatically load the previous page to avoid blank views.
• RedemptionsTable: corrected prior bug where `refresh` used `activePage - 1`.
• Misc: removed outdated inline comments and aligned search / reset flows.

Result: smoother UX—users stay on their working page, and pagination gracefully adjusts after deletions.
2025-07-13 17:45:31 +08:00
t0ng7u 5a92a5fe97 feat(tokens-table): show “Other” avatar for models without vendor logo
Adds UI fallback in TokensTable “Available Models” column:
• Tracks models already matched to a known vendor icon.
• Collects unmatched models and renders a neutral “Other” avatar (labelled via `t('其他')`) with a tooltip listing the model names.
• Removes previous generic fallback so every model is now either vendor-specific or grouped under “Other”.

This improves clarity for users by explicitly indicating models from unrecognized providers rather than leaving them unlabelled.
2025-07-13 17:24:55 +08:00
t0ng7u c39b3cfefe 🎨 refactor: TokensTable UI for clearer quota info & compact controls
• Display remaining-quota percentage instead of used-quota in the Progress indicator
  - 100 % when quota is untouched, shown in green
  - Warn at ≤ 30 % (yellow) and at ≤ 10 % (red)
  - Hide internal label (`showInfo={false}`) and move the percentage text into the Tooltip
  - Switch Progress `size` to `small` for a lighter visual footprint

• Update Tooltip to list used, remaining, total quota and the new percentage value

• Uniformly set `size="small"` on all header Buttons and Form inputs within the table
  — enhances readability and keeps the main content centered

UI/UX improvement only; no backend logic affected.
2025-07-13 01:02:55 +08:00
t0ng7u 1eb07211b9 🚑 fix: safeguard NewAPIError.Error() against nil pointer panic
Backend
• `types/error.go`
  – Return empty string when receiver itself is `nil`.
  – If `Err` is `nil`, fall back to `errorCode` string to avoid calling `nil.Error()`.

This prevents runtime panics when the error handler builds an OpenAI-style error response but the underlying `Err` field has not been set.
2025-07-13 00:16:38 +08:00
t0ng7u d47695c402 🐛 fix: preserve key data when editing channels & update MultiKeySize correctly
Backend
• `model/channel.go`
  – On multi-key channel updates, recalculate `MultiKeySize` from the current key list.
  – If the request omits `key`, fetch existing keys from DB to avoid resetting the count to `0/0`.
  – Remove out-of-range entries from `MultiKeyStatusList` to keep data consistent.

Frontend
• `web/src/pages/Channel/EditChannel.js`
  – Vertex AI channels no longer require re-uploading a key file in edit mode.
  – When no new key is provided during editing, exclude the `key` field from the payload to prevent overwriting the stored value.

These changes ensure that editing a channel no longer zeroes out the enabled key counter and that Vertex AI channels can be modified without mandatory key re-submission.
2025-07-13 00:09:27 +08:00
t0ng7u e6333e93c0 🐛 fix: synchronize MultiKeySize after editing multi-key channels
1. Recalculate `ChannelInfo.MultiKeySize` based on the current key list when a multi-key channel is updated.
2. Purge any indexes in `MultiKeyStatusList` that exceed the new key count to avoid stale or out-of-range data.

This ensures the front-end display (`enabled x/x`) always reflects the real number of keys after users add or remove keys in edit mode.
2025-07-12 23:57:59 +08:00
t0ng7u a66430c82c Merge remote-tracking branch 'origin/multi_keys_channel' into alpha
# Conflicts:
#	web/src/components/table/LogsTable.js
#	web/src/i18n/locales/en.json
#	web/src/pages/Channel/EditChannel.js
2025-07-12 23:47:24 +08:00
t0ng7u 23744465da 🎨 style(footer): remove redundant bg-semi-color-bg-2 class (#1354)
The background utility class `bg-semi-color-bg-2` was unnecessary after recent
design adjustments and unintentionally overrode intended styles.
Removing it cleans up the markup and allows the footer to inherit the correct
background from its parent containers.

File affected:
- web/src/components/layout/Footer.js

Closes #1354
2025-07-12 03:53:06 +08:00
t0ng7u 2c8f104f58 🐛 fix(tokens-table): show all extra IP addresses in tooltip
Previously, the tooltip that appears when more than one IP address is configured
skipped the second IP (`ips.slice(2)`), so users could not see it unless they
expanded the list in another way.
Changed the slice start index to `1`, ensuring that **every IP after the first
display tag** is included in the tooltip (`ips.slice(1).join(', ')`).

File affected:
- web/src/components/table/TokensTable.js
2025-07-12 03:48:55 +08:00
t0ng7u e8a586879f ⏱️ fix(token): disallow selecting expiration date earlier than now
Add custom validator to the `expired_time` DatePicker in `EditToken.js`
to ensure that selected expiration timestamps are strictly in the future.

- Introduce Promise-based validator that:
  • Rejects invalid date formats with a clear error message
  • Prevents past or current dates and shows “Expiration time cannot be earlier than the current time!”
- Keeps support for the special “never expires” value (-1)
- Blocks form submission until a valid future datetime is provided

This change prevents accidental creation of already expired tokens and
improves overall robustness of token management.
2025-07-12 03:42:05 +08:00
t0ng7u 7b271bb110 🎨 refactor(TokensTable): refactor TokensTable UI & UX for clearer data and inline actions
This commit overhauls the `TokensTable` component to deliver a cleaner, more intuitive experience.

Key changes
1. Quota
   • Merged “Used” & “Remaining” into a single “Quota” column.
   • Uses a circular `Progress` with %-label; full details shown on tooltip.

2. Status
   • Tag now embeds a small `Switch` (prefixIcon) to enable/disable a token in-place.
   • Removed enable/disable actions from the old dropdown.

3. Columns & layout
   • Added dedicated “Group” column (moved from Status).
   • Added “Key” column:
     – Read-only `Input` styled like Home page base-URL field.
     – Masked value (`sk-abc********xyz`) by default.
     – Eye button toggles reveal/hide; Copy button copies full key (without modal).
   • Dropped “More” menu; Delete is now a direct button in the action area.

4. Model limits
   • Shows vendor icons inside an `AvatarGroup`; tooltip lists the exact models.

5. IP restriction
   • Displays first IP, extra count as “+N” Tag with tooltip.
   • Unlimited shows white Tag.

6. Cleanup / misc.
   • Removed unused helpers (`getQuotaPerUnit`), icons (`IconMore`, eye/copy duplicates, etc.).
   • Replaced legacy modal view of key with inline input behaviour.
   • Tweaked paddings, themes, sizes to align with design system.

BREAKING CHANGE: Table column order & names have changed; update any tests or docs referencing the old structure.
2025-07-12 03:35:19 +08:00
t0ng7u c96bdfbe99 🎨 feat(ui): add icon support for Kling (50) & Jimeng (51) channels and iconize type selector
- import Kling & Jimeng icons from @lobehub/icons
- extend getChannelIcon() to return corresponding icons for new channel types 50 (Kling) and 51 (Jimeng)
- enhance EditChannel type selector:
  • introduce useMemo‐based channelOptionList with leading icons
  • utilize getChannelIcon for consistent icon rendering in dropdown options
- minor refactor: add useMemo and getChannelIcon imports in EditChannel.js
2025-07-12 00:02:12 +08:00
t0ng7u 17a42e2130 🎨 style(ui): move type-specific “Other / Organization” inputs to Basic Info card for smoother channel setup
Summary
• **EditChannel.js**
  – Relocated the following conditional inputs from “Advanced Settings” to the initial “Basic Info” card:
    • Model version (type 18)
    • Deployment region (type 41)
    • Knowledge-base ID (type 21)
    • Account ID (type 39)
    • Agent ID (type 49)
    • OpenAI organization (type 1)
  – No functional changes; layout only.

Why
These fields are commonly filled during the first steps of channel creation. Surfacing them earlier reduces scrolling and streamlines the user workflow.
2025-07-11 23:47:13 +08:00
t0ng7u 9ac961a750 🌐 i18n: add missing English strings for auto-disable switch in channel settings
Summary
• **en.json**
  – Added translation for “是否自动禁用” → “Whether to automatically disable”.
  – Stubbed/added key for helper text “仅当自动禁用开启时有效,关闭后不会自动禁用该渠道” (translation placeholder left for future update).

Why
These keys were previously untranslated, causing mixed-language UI for the “Auto Disable” toggle in the channel edit form. Filling them (or at least registering the keys) ensures consistent localization and prevents runtime fallbacks.
2025-07-11 23:43:27 +08:00
t0ng7u b41d6792e0 feat: enable fetching model list in creation mode & refine toast-based error handling
Summary
• **EditChannel.js**
  – Displays “Fetch Model List” button for both *create* and *edit* modes (removed `isEdit` guard).
  – Unified model selector placeholder to “Please choose supported models”.
  – Added null-safety checks when parsing Axios responses.
  – Sends requests with `{ skipErrorHandler: true }`, preventing generic *500 Internal Server Error* toasts and relying on context-specific messages instead.

• **helpers/api.js**
  – Introduced `skipErrorHandler` flag in the Axios response interceptor.
    If present, global `showError` is bypassed, giving callers full control over user-facing notifications.
  – Ensures `Promise.reject(error)` is returned for proper error propagation.

Why
Channel creators now enjoy the same convenience as editors when importing upstream model lists.
Meanwhile, suppressing redundant toasts removes confusion caused by simultaneous custom and generic error messages.
2025-07-11 23:37:47 +08:00
t0ng7u b54b590db4 🌐 feat(system-settings): add ServerAddress option & full i18n support
This commit brings System Settings in line with Payment Settings and
prepares the whole component for multi-language deployments.

Key points
----------
• Introduced a “General Settings” card in `SystemSetting.js`
  - Adds a `ServerAddress` field (copied from Payment Settings).
  - Explains that this URL is used for payment callback & default homepage.
  - Implements `submitServerAddress` to persist the option.

• Internationalisation
  - Imported `useTranslation` and wrapped **all** Chinese UI strings in `t()`.
  - Localised section titles, labels, placeholders, banners, buttons,
    modal texts and toast messages.
  - Dynamic banner/tool-tip descriptions now combine `t()` fragments with
    template literals for runtime URLs.

• UX improvements
  - Added contextual `extraText` under the ServerAddress input.
  - Ensured placeholders like “sensitive info…” are translatable.

With these changes, administrators can configure the server URL from the
System Settings tab, and the entire page is ready for seamless language
switching via the existing i18n framework.
2025-07-11 02:53:53 +08:00
t0ng7u e30eb7ff61 🎨 feat(ui): dynamic multi-key controls in Channel editor
Summary
1. Load `channel_info` when editing:
   • Detect if the channel is in multi-key mode (`is_multi_key`).
   • Auto-initialize `batch`, `multiToSingle`, and `multiKeyMode` from backend data.

2. Visibility logic
   • Creation page: “Batch create / Multi-key mode” always available.
   • Edit page: show these controls **only when** the channel itself is multi-key.

3. State consistency
   • `multi_key_mode` added to `inputs`; `setValues(inputs)` now preserves the user’s selection.

Result
Single-key channels no longer display irrelevant “key aggregation” options, while multi-key channels open with the correct defaults, providing a cleaner and more accurate editing experience.
2025-07-07 01:53:19 +08:00
t0ng7u d757125ff6 🔧 **fix(model, controller): robust serialization for ChannelInfo & correct Vertex-AI key storage**
Summary
1. **model/channel.go**
   • Replaced the pointer-only `Value()` with a value-receiver implementation
   • GORM can now marshal both `ChannelInfo` and `*ChannelInfo`, eliminating
     `unsupported type model.ChannelInfo` runtime error.

2. **controller/channel.go**
   • Refactored `getVertexArrayKeys()` – every element is now
     - passed through `json.Marshal` when not already a string
     - trimmed & validated before insertion
   • Guarantees each service-account key is persisted as a **pure JSON string**
     instead of the previous `map[...]` dump.

Result
• Channel creation / update succeeds without SQL driver errors.
• Vertex-AI batch & multi-key uploads are stored in canonical JSON, ready for
  downstream SDKs to consume.
2025-07-07 01:31:41 +08:00
t0ng7u f48ef010d9 feat(channel): Robust Vertex AI batch-key upload & stable multi-key settings
Summary
-------
1. Vertex AI JSON key upload
   • Accept multiple `.json` files (drag & drop / click).
   • Parse each `fileInstance`; build valid key array.
   • Malformed files are skipped and collected into **one** toast message.
   • Upload list now仅displays valid files; form state (`vertex_files`) 同步保持.
   • On *Submit* keys are re-parsed to prevent async timing loss.

2. Multi-key mode stability
   • Added `multi_key_mode: "random"` to initial form values.
   • `Form.Select` becomes fully controlled (`value`/`onChange`).
   • Toggling the “multi-key mode” checkbox writes default / removes field, so
     `setValues(inputs)` no longer clears the user’s choice.

3. UX / compatibility tweaks
   • Preserve uploaded files when editing any other field.
   • Use `fileInstance` only – compatible with legacy Semi Upload API.
   • Removed redundant `limit` prop on `Form.Upload`.
   • Aggregated error handling avoids toast spam.

Result
------
Channel creation/update now supports:
• Reliable batch import of Vertex AI service-account keys.
• Consistent retention of multi-key strategy (`random` / `polling`).
• Cleaner, user-friendly error feedback.
2025-07-07 01:12:01 +08:00
t0ng7u 8bb1522de6 📊 feat(detail): add model consumption trend & call ranking charts
Introduce two new visualizations to the “Model Data Analysis” panel:

1. Model Consumption Trend (line chart)
   • Added `spec_model_line` state and legend support.
   • Calculates per-model counts over time and updates via `updateChartData`.
2. Model Call Ranking (bar chart)
   • Added `spec_rank_bar` state with `seriesField` and legend enabled.
   • Ranks models by total call count.

Additional changes:
• Extended tab navigation with two new `TabPane`s and adjusted chart rendering logic.
• Swapped icons/texts to match new chart purposes.
• Reused existing color mapping to ensure consistent palette.

No breaking changes; UI now offers richer insights into model usage patterns.
2025-07-05 00:37:05 +08:00
t0ng7u 3d8a72d15c 🔢 feat(user-edit): replace add-quota input with Semi-UI InputNumber
Summary:
• Imported InputNumber from @douyinfe/semi-ui.
• Swapped plain Input for InputNumber in “Add Quota” modal.
• Added UX tweaks: full-width styling, showClear, step = 500 000.
• Initialized addQuotaLocal to an empty string so the field starts blank.
• Adjusted state handling and kept quota calculation logic unchanged.

This improves numeric input accuracy and overall user experience without breaking existing functionality.
2025-07-05 00:03:12 +08:00
t0ng7u 83dc498268 🖼️ style(RatioSync): remove the useless rounded-full style 2025-07-04 23:49:34 +08:00
t0ng7u d03c1f4bea 🐛 fix: fix the header height calculation issue in the custom HTML styles on the homepage 2025-07-04 23:42:46 +08:00
t0ng7u f45caf29af 🎨 refactor(EditTagModal): tidy imports & enhance state-sync on open
Motivation
• Remove unused UI components to keep the bundle lean and silence linter warnings.
• Ensure every time the side-sheet opens it reflects the latest tag data, avoiding stale form values (e.g., model / group mismatches).

Key Changes
1. UI Imports
   – Dropped `Input`, `Select`, `TextArea` from `@douyinfe/semi-ui` (unused in Form-based version).
2. State Reset & Form Sync
   – On `visible` or `tag` change:
     • Refresh model & group options.
     • Reset `inputs` to clean defaults (`originInputs`) carrying the current `tag`.
     • Pre-fill Form through `formApiRef` to keep controlled fields aligned.
3. Minor Cleanup
   – Added inline comment clarifying local state reset purpose.

Result
Opening the “Edit Tag” side-sheet now always displays accurate data without residual selections, and build output is cleaner due to removed dead imports.
2025-07-04 06:14:15 +08:00
t0ng7u 56dce5a745 refactor(EditChannel&EditToken): refactor Channel & Token edit pages with Semi Form and UX enhancements
Overview
• Migrated both `EditChannel.js` and `EditToken.js` to fully leverage Semi UI `Form.*` components, removing legacy `Input/Select/TextArea` + manual labels.
• Unified data-loading strategy: when the drawer becomes visible we load (or reset) data via `props.visible + id` effect and `formApi.setValues()`, guaranteeing fields are always populated; form resets on close.
• Fixed blank-form bug when opening the same record twice.

Key improvements
1. Validation
   • `type`, `models` always required.
   • `key` required only while creating (not on edit).
2. Batch key creation
   • Checkbox moved into `extraText`; hidden when editing or when channel type = 41.
3. Layout & UI
   • `Row / Col` (12 + 12) for “Priority” and “Weight”.
   • Placeholders revised; model selector now shows creation hint; removed obsolete banner.
   • Help / extraText used for long hints, template buttons (`model_mapping`, `status_code_mapping`, `param_override`, etc.), and API address notice.
   • Added `showClear`, `min`, rounded card class names for consistency.
4. Reusable helpers
   • `batchAllowed`, `batchExtra` utilities.
   • `getInitValues()` + centralized `inputs`→form synchronization.
5. Token editor aligned to the same pattern (`props.visiable` watcher).

Result
Cleaner code, consistent UX, instant field population on every open, and clearer validation/error feedback across both editors.
2025-07-04 05:36:10 +08:00
t0ng7u dd9a1534e0 🔌 feat(api): extend endpoint type support & expose in pricing UI
* backend
  - constant/endpoint_type.go
    • Add EndpointTypeMidjourney, EndpointTypeSuno, EndpointTypeKling, EndpointTypeJimeng.
  - common/endpoint_type.go
    • Map Midjourney / MidjourneyPlus, SunoAPI, Kling, Jimeng channel types to the new endpoint types.

* frontend
  - ModelPricing.js
    • Add “Supported Endpoint Type” column.
    • Implement renderSupportedEndpoints with `stringToColor` for consistent tag colors.

These changes allow `/api/pricing` and model lists to return accurate
`supported_endpoint_types` covering all non-OpenAI providers and display
them clearly in the UI.

No breaking changes.
2025-07-04 03:15:34 +08:00
t0ng7u 16cb2a4a14 style: change the border radius of most components from full to lg size 2025-06-29 02:32:09 +08:00
t0ng7u 8e7d3b3d86 🤯style: remove useless card headerStyle 2025-06-29 00:11:15 +08:00
t0ng7u d3602cbb39 chore: Improve channel creation UX: defer "Fetch Model List" action until after creation
Previously, the "Fetch Model List" button was visible in the channel-creation view even though
it only functions once a channel record exists, leading to user confusion.

Changes introduced:
• Render the "Fetch Model List" button only when editing an existing channel (`isEdit === true`).
• Display an informational Banner in creation mode to remind users that the upstream model list
  can be fetched after the channel has been created.
• Refactored JSX to apply the above conditional rendering without altering existing logic.

This update streamlines the creation workflow and sets clearer expectations for users.
2025-06-27 10:08:44 +08:00
t0ng7u 39fc38662e 💄refactor: enhance EditUser and AddUser form validation & UX
Changes in `web/src/pages/User/EditUser.js`:
• Added `rules` to
  – `Form.Select group`: now required with error “Please select group”.
  – `Form.InputNumber quota`: now required with error “Please enter quota”.
• Added `step={500000}` to quota `InputNumber` for quicker numeric input.
• Replaced invalid `readonly` with React-correct `readOnly`, and added descriptive placeholders for all binding-info fields (GitHub/OIDC/WeChat/Email/Telegram).
• Removed unused `downloadTextAsFile` import.

These updates tighten form validation, improve data entry ergonomics, and restore clear read-only indicators for third-party bindings.
2025-06-27 09:44:18 +08:00
t0ng7u 0cad369fda 🐛 fix(redemptions-table): correct initial page index and pagination state
Summary:
The redemption list occasionally displayed an invalid range such as “Items -9 - 0” and failed to highlight page 1 after a refresh. This was caused by the table being initialized with `currentPage = 0`.

Changes:
• update `useEffect` to load data starting from page 1 instead of page 0
• refactor `loadRedemptions` to accept `page` (default 1) and sanitize backend‐returned pages (`<= 0` coerced to 1)
• keep other logic unchanged

Impact:
Pagination text and page selection now show correct values on first load or refresh, eliminating negative ranges and ensuring the first page is properly highlighted.
2025-06-27 07:42:04 +08:00
t0ng7u 256b9a9fe5 🐛 fix(redemptions-table): show loading indicator while refetching data
Previously, the table did not enter the loading state after performing actions such as deleting, enabling, or disabling a redemption code. This caused a brief period where the UI appeared unresponsive while awaiting the backend response.

Changes made:
• Added `setLoading(true)` at the beginning of `loadRedemptions` to activate the loading spinner whenever data is (re)fetched.
• Added an explanatory code comment to clarify the intent.

This improves user experience by clearly indicating that the system is processing and prevents confusion during data refresh operations.
2025-06-27 07:29:28 +08:00
t0ng7u f2dd64a3b1 🎁 refactor(ui/redemption): migrate EditRedemption page to Semi Form & enhance UX
SUMMARY
• Re-implemented `web/src/pages/Redemption/EditRedemption.js` with Semi Form components, removing legacy local-state handling.
• Added `formApiRef` for centralized control; external “Submit” button now triggers `formApi.submitForm()`.
• Replaced `Input/AutoComplete/DatePicker` etc. with `<Form.*>` fields, leveraging built-in validation & accessibility.
• Field validations:
  – `name` (create only), `quota`, `count` → required with localized messages.
• Expiration-time flow:
  – Default value `null` (no more 1970-01-01).
  – When loading data, convert 0 → null, timestamp → Date.
  – On submit, Date → unix seconds; empty → 0.
• Responsive grid layout (`Row/Col`) for tidy alignment.
• Added helpful `showClear` & full-width styling for inputs; quota presets retained.
• Cleaned unused imports & handlers; fixed linter issues.

RESULT
The Redemption form now benefits from higher performance, clearer validation, and a cleaner codebase consistent with Semi Design best practices.
2025-06-27 07:25:46 +08:00
t0ng7u 0900f43c2a refactor(ui/token): migrate EditToken page to Semi Form API and polish UX
SUMMARY
• Re-implemented `EditToken.js` with Semi Form components, eliminating manual state handling and reducing re-renders.
• Added grid-based layout; “Expiration Time” selector now sits inline with quick-set buttons for consistent alignment on desktop & mobile.
• Introduced dedicated “Quota”, “Access”, “Model Limits”, and “Group” cards for clearer field grouping.
• Reworked model-limit interaction: single multi-select list replaces checkbox toggle; backend flag `model_limits_enabled` is now inferred automatically.
• Applied required validation rules to critical fields (`name`, `remain_quota`, `group`, `expired_time`, `tokenCount`) with localized messages.
• Enabled dynamic option loading for models & groups; default auto-group honoured.
• Added unlimited-quota switch, quota presets, and helpful extraText/tooltips.
• Removed obsolete `handleInputChange` & `setUnlimitedQuota` helpers; formApi now manages all data flow.
• Cleaned imports (e.g., dropped unused `IconUserGroup`), fixed linter errors, and updated submit logic to use `formApi.submitForm()`.

RESULT
The token creation/editing experience is faster, more accessible, and easier to maintain, fully aligned with Semi Design best practices.
2025-06-26 22:58:25 +08:00
t0ng7u b785071ca6 🎨 style: remove all prefix icons to simplify the layout of the sidesheet component 2025-06-26 16:36:36 +08:00
t0ng7u f80eb95864 🎨 style: unify card header UI, switch to Avatar icons & remove oversized props
Summary
• Replaced gradient header blocks with compact, neutral headers wrapped in `Avatar` across the following pages:
  - Channel / EditChannel.js
  - Channel / EditTagModal.js
  - Redemption / EditRedemption.js
  - Token / EditToken.js
  - User / EditUser.js
  - User / AddUser.js

Details
1. Added `Avatar` import and substituted raw icon elements, assigning semantic colors (`blue`, `green`, `purple`, `orange`, etc.) and consistent 16 px icons for a cleaner look.
2. Removed gradient backgrounds, decorative “blur-ball” shapes, and extra paddings from header containers to achieve a tight, flat design.
3. Stripped all `size="large"` attributes from `Button`, `Input`, `Select`, `DatePicker`, `AutoComplete`, and `Avatar` components, allowing default sizing for better visual density.
4. Eliminated redundant `bodyStyle` background overrides in some `SideSheet` components.
5. No business logic touched; all changes are purely presentational.

Result
The editing and creation dialogs now share a unified, compact style consistent with the latest design language, improving readability and user experience without altering functionality.
2025-06-26 16:05:13 +08:00
t0ng7u a976d9d366 🔒 style: Hide registration link when Self-Use Mode is enabled
• Add conditional rendering (`!status.self_use_mode_enabled`) to LoginForm
• Suppress “Don't have an account? Register” CTA in self-hosted scenarios
• Keeps UI clean and prevents unintended user sign-ups under self-use mode
• No impact on regular multi-user deployments
2025-06-26 04:29:44 +08:00
t0ng7u 1275667175 🐛 fix(auth): prevent duplicate “session expired” toast on login
Login Form used to display the message “未登录或登录已过期,请重新登录” twice
because the `useEffect` that inspects the `expired` query parameter was
re-executed on every re-render (e.g. language change or React StrictMode’s
double-mount in development).

### What's changed
• **LoginForm.js** – `useEffect` that shows the toast now has an empty
  dependency array so it runs only once on initial mount.
• Reviewed **PasswordResetConfirm.js**, **PasswordResetForm.js** and
  **RegisterForm.js** and confirmed they do not contain the same issue;
  no changes were required.

### Impact
Users now see the “session expired” notification exactly once, removing
confusion and improving the overall UX.
2025-06-26 03:51:19 +08:00
t0ng7u 7630838743 🧶style(TokensTable): add IconDelete in Delete selected token button 2025-06-25 23:23:59 +08:00
t0ng7u dabd9a9a23 🐛 fix(auth): restore proper state & context destructuring in Login- and Register-forms
Why
Clicking the “Continue” button on the login page no longer triggered the submission logic. The issue was introduced when `useState`/`useContext` hooks were destructured incorrectly, breaking the setter reference and omitting required values.

What’s changed
• **LoginForm.js**
  – Re-added setter in `useSearchParams` (`[searchParams, setSearchParams]`).
  – Corrected order of destructuring for `inputs` so `username`/`password` are available after hooks.
  – Switched `useContext` to `[userState, userDispatch]` for consistency.

• **RegisterForm.js**
  – Adopted `[userState, userDispatch]` from `UserContext` to mirror LoginForm and retain full state access.

Outcome
Login button now successfully invokes `handleSubmit`, and both auth components have consistent, fully-featured hook destructuring, preventing runtime errors and ensuring future state usage is straightforward.
2025-06-25 23:13:55 +08:00
t0ng7u 06b7b595c6 💫 feat(ui): introduce dispersed blur-ball background to all auth views
This commit refreshes the visual design of the authentication pages and aligns them with the Home banner style.

Details
• LoginForm.js / RegisterForm.js / PasswordResetForm.js / PasswordResetConfirm.js
  – Wrap top-level container with `relative overflow-hidden` to provide a positioning context.
  – Inject two decorative blur balls:
    ▸ Indigo ball on the top-right (`blur-ball-indigo`).
    ▸ Teal ball on the middle-left (`blur-ball-teal`).
  – Disabled the default X-axis transform on the indigo ball to keep the ball anchored to the corner.
  – Removed redundant `mt-[64px]` from the outer container and shifted it to the inner wrapper to maintain vertical rhythm without affecting the background placement.

Result
The auth screens now feature subtle, non-intrusive atmospheric gradients in the top-right and mid-left corners, offering a cohesive look & feel across the application without obstructing the main content.
2025-06-25 22:57:04 +08:00
t0ng7u 8fb5cb6686 🐛 fix(auth): prevent initial render flicker & clean up state usage
• LoginForm / RegisterForm now initialise `status` directly from localStorage,
  avoiding a post-mount state update that caused a UI flash between OAuth
  options and email/username forms.

• Move Turnstile configuration into a dedicated effect that depends on
  `status`, ensuring setState is not called during rendering.

• Remove unused `setStatus` setter to resolve ESLint “declared but never read”
  warnings.

• Minor refactors: reorder hooks, de-duplicate navigate/context variables and
  streamline state destructuring for improved readability.
2025-06-25 22:46:11 +08:00
t0ng7u f9971df2ef 🍭style: add mt-[64px] in class auth componets 2025-06-25 22:21:14 +08:00
t0ng7u 6d2f34a889 🎨 style(ui): refactor Tabs in ModelPricing to use native Semi UI styling
• Removed the custom `renderArrow` helper and its `Dropdown`-based arrow navigation, simplifying the component logic.
• Switched the `<Tabs>` component to rely on Semi UI’s built-in behaviour (no more `renderArrow` override).
• Kept `type="card"` and `collapsible` props for consistent visual appearance while still using the default style.
• Eliminated the now-unused `Dropdown` import.

This cleanup reduces bespoke UI code, makes future maintenance easier, and keeps the interface consistent with the rest of the application.
2025-06-25 15:40:27 +08:00
t0ng7u 3a5618ede9 Merge remote-tracking branch 'origin/alpha' into alpha 2025-06-25 15:26:59 +08:00
t0ng7u 70a4b5765d feat(homepage): enhance banner visuals & UX
• Added read-only Base URL input that shows `status.server_address` (fallback `window.location.origin`) and copies value on click.
• Embedded `ScrollList` as input `suffix`; auto-cycles common endpoints every 3 s and allows manual selection.
• Introduced `API_ENDPOINTS` array in `web/src/constants/common.constant.js` for centralized endpoint management.
• Implemented custom CSS to hide ScrollList wheel indicators / scrollbars for a cleaner look.
• Created two blurred colour spheres behind the banner (`blur-ball-indigo`, `blur-ball-teal`) with light-/dark-mode opacity tweaks and lower vertical placement.
• Increased letter-spacing for Chinese heading via conditional `tracking-wide` / `md:tracking-wider` classes to improve readability.
• Misc: updated imports, helper functions, and responsive sizes to keep UI consistent across devices.
2025-06-25 15:26:51 +08:00
t0ng7u 6220b3ee0f Merge remote-tracking branch 'origin/alpha' into alpha 2025-06-24 18:10:04 +08:00
t0ng7u 87e90e5219 🔧 style(ui): Inline tag edit action in ChannelsTable
• Removed the dropdown menu previously used for tag-level operations.
• Added a standalone “Edit” button directly after the “Disable All” button, reducing the number of clicks required to edit a tag group.
• Deleted the now-unused `IconEdit` import and its icon reference.

This streamlines the tag management flow and keeps the UI cleaner and more accessible.
2025-06-24 18:09:16 +08:00
t0ng7u 7a4c213b65 🎨 style(channels-table): standardize operation component size to small
All operation-related UI controls in `ChannelsTable` (buttons, dropdowns,
switches, inputs, tags, etc.) now explicitly use `size="small"`.

Reasons & benefits:
- Creates a more compact and consistent look across the table and modals.
- Improves visual coherence between desktop and mobile views.
- Purely presentational; no functional logic is affected.

No database changes or API interactions are involved.
2025-06-24 18:02:34 +08:00
t0ng7u 610853e9c8 🚀 feat: enhance model testing UI with bulk selection, copy & success-filter buttons (#1288)
* ChannelsTable
  - Added row-level checkboxes to the model-testing table for multi-selection
  - Implemented cross-page “Select All / Deselect All” via rowSelection.onSelectAll
  - Introduced allSelectingRef to ignore redundant onChange after onSelectAll
  - Added “Copy Selected” button to copy chosen model names (comma-separated) using helpers.copy
  - Added “Select Successful” button to auto-tick all models that passed testing
  - Moved search bar and new action buttons into the modal title for better UX
  - Centralised page size constant MODEL_TABLE_PAGE_SIZE in channel.constants.js
  - Fixed pagination slicing and auto-page-switch logic during batch testing

* channel.constants
  - Exported MODEL_TABLE_PAGE_SIZE (default 10) for unified pagination control

This commit enables users to conveniently copy or filter successful models, fully supports cross-page bulk operations, and resolves previous selection inconsistencies.

Refs: #1288
2025-06-24 17:46:08 +08:00
t0ng7u 62daf16b19 fix: ensure table shows correct loading state on first render & during search
Frontend (`ChannelsTable.js`)
1. Initialize `loading` state to `true` so the spinner is visible while the first data request is in-flight.
2. Set `<Table>` prop `loading={loading || searching}` — the spinner now appears for both the initial load and any subsequent search requests.

Result
Users immediately see a loading indicator on page entry and whenever a search is running, improving perceived responsiveness.
2025-06-24 05:20:54 +08:00
t0ng7u d19ab54e32 🚀 feat: Align search API with channel listing & fix sorting toggle
1. Backend
   • `controller/channel.go`
     – Added pagination (`p`, `page_size`) support to `SearchChannels`.
     – Added independent `type` filter (keeps `type_counts` unaffected).
     – Returned `total`, `type_counts` to match `/api/channel/` response.

2. Frontend
   • `ChannelsTable.js`
     – `loadChannels` / `searchChannels` now pass `p`, `page_size`, `id_sort`, `type`, `status` correctly.
     – Pagination, page-size selector and type tabs work for both normal list and search mode.
     – Switch for “ID sort” calls proper API and keeps UI state in sync.
     – Removed unnecessary `normalize` helper; `getFormValues` back to concise form.

Result
• Search mode and normal listing now share identical pagination and filtering behavior.
• Type tabs show correct counts even after searching.
• “ID Sort” toggle no longer inverses actual behaviour.
2025-06-24 05:13:47 +08:00
t0ng7u 8f0f0c0d27 fix(channels-table): preserve group filter when switching type or status tabs
Refactors `ChannelsTable.js` to ensure that the selected group filter is **never lost** when:

1. Cycling between channel-type tabs.
2. Changing the status dropdown (all / enabled / disabled).

Key points:

• `loadChannels` now detects active search filters (keyword / group / model) and transparently delegates to `searchChannels`, guaranteeing all parameters are sent in every request.
• `searchChannels` accepts optional `typeKey` and `statusF` arguments, enabling reuse without code duplication.
• Loading state handling is unified; no extra renders or side effects were introduced, keeping UI performance intact.
• Duplicate logic removed and responsibilities clearly separated for easier future maintenance.
2025-06-24 04:16:40 +08:00
t0ng7u 9bf32ef581 Revert "🐛 fix: preserve group filter when switching channel type/status"
This reverts commit 4949d986c7.
2025-06-24 01:51:26 +08:00
t0ng7u 9469c4973c 💄 i18n: shorten channel search placeholder and update i18n
Replaced the verbose placeholder “Search channel ID, name, key and API address ...”
with a concise version “Channel ID, name, key, API address” in
`ChannelsTable.js` and synchronized the corresponding i18n entries.

This improves readability and keeps UI text consistent across languages.
2025-06-24 01:48:39 +08:00
t0ng7u 4949d986c7 🐛 fix: preserve group filter when switching channel type/status
Ensure that the selected "group" filter (and other form search values) persist across
type tab changes, status filter updates, pagination, and page-size changes.

Changes include:
• loadChannels: added `searchParams` argument and now appends keyword, group and model
  query strings to API calls.
• refresh / page handlers / type tabs / status Select: now pass current form values
  to loadChannels, keeping filters intact.
• searchChannels: maintains active type and status filters when issuing search requests.
• Form.Select (searchGroup): triggers loadChannels when only group filter is active,
  preventing parameter loss.
• Minor cleanup and comment adjustments.
2025-06-24 01:45:22 +08:00
t0ng7u 949d462534 🐛 fix(channel): remove duplicate model names in “Edit Channel” model dropdown (#1292)
• Unify the Select option structure as `{ key, label, value }`; add missing `key` to prevent duplicated rendering by Semi-UI.
• Trim and deduplicate the `models` array via `Set` inside `handleInputChange`, ensuring state always contains unique values.
• In the options-merging `useEffect`, use a `Map` keyed by `value` (after `trim`) to guarantee a unique `optionList` when combining backend data with currently selected models.
• Apply the same structure and de-duplication when:
  – Fetching models from `/api/channel/models`
  – Adding custom models (`addCustomModels`)
  – Fetching upstream model lists (`fetchUpstreamModelList`)
• Replace obsolete `text` field with `label` in custom option objects for consistency.

No backend changes are required; the fix is entirely front-end.

Closes #1292
2025-06-24 00:25:29 +08:00
t0ng7u fc2e2c1aff 🎨 feat(EditChannel): improve model selection UX, clipboard feedback & rounded styling (#1290)
* Added a dedicated effect to merge origin and selected models, ensuring selected items always remain in the dropdown list.
* Enhanced “Copy all models” button:
  * Shows info message when list is empty.
  * Displays success / error notification based on copy result.
* Unified UI look-and-feel by applying `!rounded-lg` class to inputs, selects, banners and buttons.
* i18n: added English translations for new prompts
  - "No models to copy"
  - "Model list copied to clipboard"
  - "Copy failed"
2025-06-24 00:02:22 +08:00
t0ng7u 13fd901d17 🚀 feat: add enabled/disabled channel filtering & optimize type-based pagination (#1289)
WHAT’S NEW
• Backend
  – Introduced `parseStatusFilter` helper to normalize `status` query across handlers.
  – `GET /api/channel` & `GET /api/channel/search` now accept `status=enabled|disabled` to return only enabled or disabled channels.
  – Tag-mode branch respects both `statusFilter` and `typeFilter`; SQL paths trimmed to one query + one lightweight `GROUP BY` for `type_counts`.

• Frontend (`ChannelsTable.js`)
  – Added “Status Filter” `<Select>` (All / Enabled / Disabled) with localStorage persistence.
  – All data-loading and search requests now always append `type` (when not “all”) and `status` params, so filtering & pagination are handled entirely server-side.
  – Removed client-side post-filtering for type, preventing short pages and reducing CPU work.
  – Tabs’ type counts stay in sync via backend-provided `type_counts`.

IMPROVEMENTS
• Eliminated duplicated status-parsing logic; single source of truth eases future extension.
• Reduced redundant queries, improved consistency of counts in UI.
• Secured key leakage with `Omit("key")` unchanged; no perf regressions observed.

Closes #1289
2025-06-23 23:40:34 +08:00
t0ng7u bce87295b6 Merge remote-tracking branch 'origin/alpha' into alpha 2025-06-23 17:35:50 +08:00
t0ng7u 0ac7406db8 🚀 chore(ui): Refactor UpstreamRatioSync with conflict-modal component, performance hooks & cleanup (#1286)
WHAT’S NEW
• Extracted reusable ConflictConfirmModal for clearer JSX hierarchy
• Added detailed conflict detection & confirmation flow before syncing options
• Refactored state-heavy callbacks (`selectValue`, `performSync`) with `useCallback` to avoid unnecessary renders
• Introduced build-time constants (later removed unused export) and unified helper utilities
• Ensured final ratios are rebuilt accurately before API `PUT`, fixing “value not updated” bug
• Enhanced UI hints: warning icon on conflict, multiline billing info, mobile-friendly modal size
• General code cleanup: removed dead variables, adopted early returns, improved comments

WHY
Improves maintainability, user clarity when billing-type collisions occur, and guarantees data consistency after synchronisation.
2025-06-23 17:35:39 +08:00
t0ng7u ef9c5b3acb 🐛 fix(ratio-sync): reset pagination when filter/search changes
Add a `useEffect` hook in `UpstreamRatioSync.js` to automatically set
`currentPage` to `1` whenever `ratioTypeFilter` or `searchKeyword`
updates.
This prevents the table from appearing empty when users switch to the
“model_price” (fixed price) filter or perform a new search while on a
later page.

Additional changes:
- Import `useEffect` from React.

This enhancement delivers a smoother UX by ensuring the first page of
results is always shown after any filtering action.
2025-06-23 16:34:00 +08:00
t0ng7u f73da57acb 🎛️ feat(web): add “Conflict Rates” filter & highlight in Model Settings Visual Editor (#1286)
Introduce the ability to quickly locate models with conflicting billing configurations.

Key points
• Added `hasConflict` flag to detect models that define both a fixed price (`ModelPrice`) and any ratio (`ModelRatio` or `CompletionRatio`).
• Added “Show Only Conflict Rates” `Checkbox` to toolbar; filtering logic now supports keyword + conflict filtering.
• Display a red `Tag` beside the model name when a conflict is detected for immediate visual feedback.
• Kept `hasConflict` state in sync during add, update and delete operations.
• Imported `Checkbox` and `Tag` from **@douyinfe/semi-ui**.
• Minor UI tweaks (circle tag style, margin) for consistency.

This enhancement helps administrators swiftly identify and resolve incompatible pricing rules, addressing the need discussed in issue #1286.
2025-06-23 15:55:10 +08:00
t0ng7u 6cac7d96e7 🍭style: update home main title style font-semibold to font-bold 2025-06-22 18:28:32 +08:00
t0ng7u 2df9ed2892 Merge remote-tracking branch 'origin/alpha' into alpha 2025-06-22 18:10:44 +08:00
t0ng7u 6bd4e202a1 feat(ui): Implement unified compact/adaptive table mode + icon refinement
Summary
• Added per-table “Compact / Adaptive” view toggle to all major table components (Tokens, Channels, Logs, MjLogs, TaskLogs, Redemptions, Users).
• Persist user preference in a single localStorage entry (`table_compact_modes`) instead of scattered keys.

Details
1. utils.js
   • Re-implemented `getTableCompactMode` / `setTableCompactMode` to read & write a shared JSON object.
   • Imported storage-key constant from `constants`.

2. hooks/useTableCompactMode.js
   • Hook now consumes the unified helpers and listens to `storage` events via the shared key constant.

3. constants
   • Added `TABLE_COMPACT_MODES_KEY` to `common.constant.js` and re-exported via `constants/index.js`.

4. Table components
   • Integrated `useTableCompactMode('<tableName>')`.
   • Dynamically remove `fixed: 'right'` column and horizontal `scroll` when in compact mode.
   • UI: toggle button placed at card title’s right; responsive layout on small screens.

5. UI polish
   • Replaced all lucide-react `List`/`ListIcon` usages with Semi UI `IconDescend` for consistency.
   • Restored correct icons where `Hash` was intended (TaskLogsTable).

Benefits
• Consistent UX for switching list density across the app.
• Cleaner localStorage footprint with easier future maintenance.
2025-06-22 18:10:00 +08:00
t0ng7u d11798cc68 feat(tokens-table): add selectable copy modes for bulk token copy action
This commit enhances the “Copy Selected Tokens to Clipboard” feature in `TokensTable.js` by introducing a user-friendly modal that lets users choose how they want to copy tokens.

Changes made
• Replaced direct copy logic with a `Modal.info` dialog.
• Modal displays the prompt “Please choose your copy mode”.
• Added two buttons in a custom footer:
  – **Name + Secret**: copies `tokenName    sk-tokenKey`.
  – **Secret Only**: copies `sk-tokenKey`.
• Each button triggers the copy operation and closes the dialog.
• Maintains existing validations (e.g., selection check, clipboard feedback).

Benefits
• Gives users clear control over copy format, reducing manual editing.
• Aligns UI with Semi UI’s best practices via custom modal footer.

No backend/API changes are involved; all updates are limited to the front-end UI logic.
2025-06-22 16:49:44 +08:00
t0ng7u 2d305d5bb1 Merge remote-tracking branch 'origin/alpha' into alpha 2025-06-22 16:39:52 +08:00
t0ng7u 1ad9c97d7c 🗑️ feat(token): implement batch token deletion API & front-end integration
• Back-end
  • model/token.go
    • Add `BatchDeleteTokens(ids []int, userId int)` – transactional DB removal + async Redis cache cleanup.
  • controller/token.go
    • Introduce `TokenBatch` DTO and `DeleteTokenBatch` handler calling the model layer; returns amount deleted.
  • router/api-router.go
    • Register `POST /api/token/batch` route (user-scoped).

• Front-end (TokensTable.js)
  • Replace per-token deletion loops with single request to `/api/token/batch`.
  • Display dynamic i18n message: “Deleted {{count}} tokens!”.
  • Add modal confirmation:
    • Title “Batch delete token”.
    • Content “Are you sure you want to delete the selected {{count}} tokens?”.
  • UI/UX tweaks
    • Responsive button group (flex-wrap, mobile line-break).
    • Clear `selectedKeys` after refresh / successful deletion to avoid ghost selections.

• i18n
  • Ensure placeholder style matches translation keys (`{{count}}`).

This commit delivers efficient, scalable token management and an improved user experience across devices.
2025-06-22 16:35:30 +08:00
t0ng7u e69f4199ea Merge remote-tracking branch 'origin/alpha' into alpha 2025-06-21 22:31:54 +08:00