♻️ refactor(channels): rebuild channel create/edit drawer with modular sections and improved form UX

Restructure the default-theme channel create/edit experience to align with
classic frontend behavior, modern form UX patterns, and the project's Base UI
design system.

Channel editor architecture:
- Split the monolithic channel mutate drawer into focused section components
  (basic, API access, auth, models, advanced) with shared drawer layout
  primitives
- Extract submission, toast handling, and react-query cache invalidation into
  `useChannelMutateForm`
- Add a dedicated loading skeleton for channel detail fetch during edit mode
- Remove the top-level configuration summary block per UX feedback

Form validation and data handling:
- Strengthen `channel-form` Zod schema with JSON, model mapping, status code
  mapping, Codex credential, and Vertex AI key refinements
- Move type-specific conditional validation into `superRefine`
- Normalize base URL formatting and tighten model mapping value validation

Model mapping editor:
- Add Visual/JSON tabbed editing with inline JSON and duplicate-key feedback
- Improve accessibility for icon-only actions and add model suggestion datalists

MultiSelect component:
- Replace the custom cmdk-based implementation with Base UI Combobox chips
- Align focus, border, ring, disabled, and invalid states with standard Input
  styling via `ComboboxChips`
- Preserve existing API (`options`, `selected`, `onChange`, `allowCreate`,
  `createLabel`) for all current callers
- Support inline custom value creation, comma/newline batch input, searchable
  options, portal-based dropdown positioning, and chip removal

Models & groups UX:
- Integrate manual custom model entry directly into the model MultiSelect
- Remove the separate manual model input/button block
- Keep selected-model count and existing model-mapping guardrail behavior

i18n:
- Add and sync translation keys for new editor sections, validation messages,
  model mapping UI, and MultiSelect empty/create labels across en, zh, fr, ja,
  ru, and vi
- Remove obsolete keys tied to the deprecated summary and manual model entry UI

Affected areas:
- `web/default/src/features/channels/components/drawers/`
- `web/default/src/features/channels/hooks/use-channel-mutate-form.ts`
- `web/default/src/features/channels/lib/channel-form.ts`
- `web/default/src/features/channels/lib/model-mapping-validation.ts`
- `web/default/src/features/channels/components/model-mapping-editor.tsx`
- `web/default/src/components/multi-select.tsx`
- `web/default/src/i18n/locales/*.json`
This commit is contained in:
t0ng7u
2026-05-26 01:22:49 +08:00
parent 583da45296
commit 3d850d38b6
19 changed files with 3465 additions and 2478 deletions
+34 -13
View File
@@ -37,7 +37,6 @@
"{{count}} IP(s)": "{{count}} IP",
"{{count}} log entries removed.": "Đã xóa {{count}} mục nhật ký.",
"{{count}} minutes ago": "{{count}} phút trước",
"{{count}} model(s)": "{{count}} mô hình",
"{{count}} models": "{{count}} mô hình",
"{{count}} months ago": "{{count}} tháng trước",
"{{count}} override": "{{count}} ghi đè",
@@ -135,6 +134,7 @@
"Actual Model": "Mô hình thực tế",
"Actual Model:": "Mô hình thực tế:",
"Add": "Add",
"Add \"{{value}}\"": "Thêm \"{{value}}\"",
"Add {{title}}": "Thêm {{title}}",
"Add a group identifier to the auto assignment list.": "Thêm một mã định danh nhóm vào danh sách phân công tự động.",
"Add a new API key by providing necessary info.": "Thêm khóa API mới bằng cách cung cấp thông tin cần thiết.",
@@ -152,7 +152,7 @@
"Add condition": "Thêm điều kiện",
"Add Condition": "Thêm điều kiện",
"Add credits": "Thêm tín dụng",
"Add custom model(s), comma-separated": "Thêm mô hình tùy chỉnh, phân tách bằng dấu phẩy",
"Add custom model \"{{value}}\"": "Thêm mô hình tùy chỉnh \"{{value}}\"",
"Add discount tier": "Thêm bậc giảm giá",
"Add each model or tag you want to include.": "Thêm mỗi mô hình hoặc thẻ bạn muốn đưa vào.",
"Add FAQ": "Thêm FAQ",
@@ -164,6 +164,7 @@
"Add group rules": "Thêm quy tắc nhóm",
"Add Mapping": "Thêm ánh xạ",
"Add method": "Thêm phương thức",
"Add missing models": "Thêm mô hình còn thiếu",
"Add Mode": "Thêm Chế độ",
"Add model": "Thêm mô hình",
"Add Model": "Thêm Mô hình",
@@ -192,7 +193,6 @@
"Add User": "Thêm người dùng",
"Add user group": "Thêm nhóm người dùng",
"Add your API keys, set up channels and configure access permissions": "Thêm khóa API, thiết lập kênh và cấu hình quyền truy cập",
"Added {{count}} custom model(s)": "Đã thêm {{count}} mô hình tùy chỉnh",
"Added {{count}} model(s)": "Đã thêm {{count}} mô hình",
"Added successfully": "Thêm thành công",
"Additional Conditions": "Điều kiện bổ sung",
@@ -201,8 +201,8 @@
"Additional Limit": "Hạn mức bổ sung",
"Additional Limits": "Các hạn mức bổ sung",
"Additional metered capability": "Tính năng tính phí theo mức dùng bổ sung",
"Adjust Quota": "Điều chỉnh hạn mức",
"Adjust filters, then search to refresh the logs.": "Điều chỉnh bộ lọc, sau đó tìm kiếm để làm mới nhật ký.",
"Adjust Quota": "Điều chỉnh hạn mức",
"Adjust response formatting, prompt behavior, proxy, and upstream automation.": "Điều chỉnh định dạng phản hồi, hành vi prompt, proxy và tự động hóa upstream.",
"Adjust the appearance and layout to suit your preferences.": "Điều chỉnh giao diện và bố cục để phù hợp với sở thích của bạn.",
"Admin": "Quản trị viên",
@@ -424,6 +424,11 @@
"Audio Tokens": "Token âm thanh",
"Auth configured": "Đã cấu hình xác thực",
"Auth Style": "Kiểu xác thực",
"auth.resetPasswordConfirm.backToLogin": "Quay lại đăng nhập",
"auth.resetPasswordConfirm.confirm": "Xác nhận đặt lại mật khẩu",
"auth.resetPasswordConfirm.description": "Xác nhận yêu cầu đặt lại để tạo mật khẩu mới.",
"auth.resetPasswordConfirm.retry": "Thử lại ({{seconds}} giây)",
"auth.resetPasswordConfirm.success": "Mật khẩu của bạn đã được đặt lại thành công",
"Authentication": "Xác thực",
"Authenticator code": "Mã xác thực",
"Authorization Endpoint": "Điểm cuối ủy quyền",
@@ -502,6 +507,7 @@
"Base Price": "Giá cơ bản",
"Base rate limit windows for this account.": "Cửa sổ giới hạn tốc độ cơ bản cho tài khoản này.",
"Base URL": "URL cơ sở",
"Base URL is required for this channel type": "Loại kênh này yêu cầu Base URL",
"Base URL of your Uptime Kuma instance": "URL cơ sở của phiên bản Uptime Kuma của bạn",
"Basic Authentication": "Xác thực cơ bản",
"Basic Configuration": "Cấu hình cơ bản",
@@ -759,8 +765,10 @@
"Codex": "Codex",
"Codex Account & Usage": "Tài khoản và sử dụng Codex",
"Codex Authorization": "Ủy quyền Codex",
"Codex channels do not support batch creation": "Kênh Codex không hỗ trợ tạo hàng loạt",
"Codex channels use an OAuth JSON credential as the key.": "Kênh Codex dùng thông tin xác thực OAuth JSON làm khóa.",
"Codex CLI Header Passthrough": "Chuyển tiếp header Codex CLI",
"Codex credential must be a JSON object with access_token and account_id": "Thông tin xác thực Codex phải là đối tượng JSON có access_token và account_id",
"Cohere": "Cohere",
"Collapse": "Thu gọn",
"Collapse All": "Thu gọn tất cả",
@@ -867,8 +875,6 @@
"Confirm New Password": "Xác nhận mật khẩu mới",
"Confirm password": "Xác nhận mật khẩu",
"Confirm Payment": "Xác nhận Thanh toán",
"auth.resetPasswordConfirm.confirm": "Xác nhận đặt lại mật khẩu",
"auth.resetPasswordConfirm.description": "Xác nhận yêu cầu đặt lại để tạo mật khẩu mới.",
"Confirm Selection": "Xác nhận lựa chọn",
"Confirm settings and finish setup": "Xác nhận cài đặt và hoàn tất thiết lập",
"confirm that I bear legal responsibility arising from deployment": "xác nhận rằng tôi chịu trách nhiệm pháp lý phát sinh từ việc triển khai",
@@ -999,6 +1005,7 @@
"Create, revoke, and audit API tokens.": "Tạo, thu hồi và kiểm toán token API.",
"Created": "Đã tạo",
"Created At": "Ngày tạo",
"Creates a Pancake product in the saved store using this plans title and price. Requires Waffo Pancake to be fully configured in Payment settings first.": "Tạo một sản phẩm Pancake trong cửa hàng đã lưu bằng tiêu đề và giá của gói này. Trước tiên cần cấu hình đầy đủ Waffo Pancake trong cài đặt Thanh toán.",
"Creating...": "Đang tạo...",
"Creation failed": "Tạo thất bại",
"Credential generated": "Đã tạo thông tin xác thực",
@@ -1122,6 +1129,7 @@
"Delete invalid redemption codes": "Xóa mã đổi thưởng không hợp lệ",
"Delete Invalid Redemption Codes?": "Xóa Mã đổi thưởng không hợp lệ?",
"Delete logs": "Xóa nhật ký",
"Delete mapping": "Xóa ánh xạ",
"Delete Model": "Xóa Mô hình",
"Delete Models?": "Xóa mô hình?",
"Delete Provider": "Xóa nhà cung cấp",
@@ -1249,6 +1257,8 @@
"Drawing task records": "Lịch sử tác vụ vẽ",
"Duplicate": "Nhân bản",
"Duplicate group names: {{names}}": "Tên nhóm bị trùng: {{names}}",
"Duplicate source model mappings are not allowed": "Không cho phép ánh xạ mô hình nguồn trùng lặp",
"Duplicate source model(s): {{models}}": "Mô hình nguồn trùng lặp: {{models}}",
"Duration": "Thời lượng",
"Duration (hours)": "Thời lượng (giờ)",
"Duration Settings": "Cài đặt thời lượng",
@@ -1935,7 +1945,7 @@
"How client credentials are sent to the token endpoint": "Cách thông tin xác thực client được gửi đến endpoint token",
"How frequently the system tests all channels": "Tần suất hệ thống kiểm tra tất cả các kênh là bao nhiêu?",
"How It Works": "Cách hoạt động",
"How model mapping works": "Cách ánh xạ mô hình hoạt động",
"How model mapping works": "Cách hoạt động của ánh xạ mô hình",
"How much to charge for each US dollar of balance (Epay)": "Tính phí bao nhiêu cho mỗi đô la Mỹ số dư (Epay)",
"How this model name should match requests": "Tên mô hình này nên khớp với các yêu cầu như thế nào",
"How to deliver the resulting image": "Cách trả về ảnh kết quả",
@@ -2177,6 +2187,7 @@
"Load template...": "Tải mẫu...",
"Loader": "Trình tải",
"Loading": "Đang tải",
"Loading channel details": "Đang tải chi tiết kênh",
"Loading configuration": "Đang tải cấu hình",
"Loading content settings...": "Đang tải cài đặt nội dung...",
"Loading current models...": "Đang tải các mô hình hiện tại...",
@@ -2293,7 +2304,6 @@
"Minimum:": "Tối thiểu:",
"Minor blips in the last 30 days": "Vài gián đoạn nhỏ trong 30 ngày qua",
"Mint a fresh pair below — or pick an existing one further down. Click Save when ready.": "Tạo một cặp mới bên dưới, hoặc chọn một cặp hiện có ở phía dưới. Nhấn Lưu khi đã sẵn sàng.",
"Creates a Pancake product in the saved store using this plans title and price. Requires Waffo Pancake to be fully configured in Payment settings first.": "Tạo một sản phẩm Pancake trong cửa hàng đã lưu bằng tiêu đề và giá của gói này. Trước tiên cần cấu hình đầy đủ Waffo Pancake trong cài đặt Thanh toán.",
"Minute": "Phút",
"minutes": "phút",
"Missing code": "Thiếu mã",
@@ -2324,7 +2334,9 @@
"Model Mapping": "Ánh xạ mô hình",
"Model Mapping (JSON)": "Ánh xạ mô hình (JSON)",
"Model Mapping must be a JSON object like": "Ánh xạ Mô hình phải là một đối tượng JSON như",
"Model mapping must be a JSON object with string values": "Ánh xạ mô hình phải là đối tượng JSON với giá trị chuỗi",
"Model mapping must be valid JSON": "Ánh xạ mô hình phải là JSON hợp lệ",
"Model mapping values must be strings": "Giá trị ánh xạ mô hình phải là chuỗi",
"Model name": "Tên mẫu",
"Model Name": "Tên mẫu",
"Model Name *": "Tên mẫu *",
@@ -2333,8 +2345,8 @@
"Model not found": "Không tìm thấy mô hình",
"Model performance metrics": "Chỉ số hiệu năng mô hình",
"Model Price": "Giá mô hình",
"Model Price Not Configured": "Giá mô hình chưa được cấu hình",
"Model price is not configured. Please complete model pricing in settings.": "Giá mô hình chưa được cấu hình. Vui lòng hoàn tất định giá mô hình trong cài đặt.",
"Model Price Not Configured": "Giá mô hình chưa được cấu hình",
"Model prices": "Giá mô hình",
"Model prices reset successfully": "Đã đặt lại giá mô hình thành công",
"Model Pricing": "Định giá mô hình",
@@ -2532,6 +2544,7 @@
"No Logs Found": "Không tìm thấy nhật ký",
"No mappings configured. Click \"Add Row\" to get started.": "Chưa có ánh xạ nào được cấu hình. Nhấp vào \"Thêm hàng\" để bắt đầu.",
"No matches found": "Không tìm thấy kết quả nào",
"No matching items": "Không có mục phù hợp",
"No matching results": "Không có kết quả phù hợp",
"No matching rules": "Không có quy tắc phù hợp",
"No messages yet": "Chưa có tin nhắn",
@@ -2950,6 +2963,7 @@
"Please upload key file(s)": "Vui lòng tải lên (các) tệp khóa",
"Please wait a moment before trying again.": "Vui lòng chờ một lát rồi thử lại.",
"Please wait a moment, human check is initializing...": "Vui lòng đợi một chút, kiểm tra con người đang khởi tạo...",
"Please wait before editing to avoid overwriting saved values.": "Vui lòng chờ trước khi chỉnh sửa để tránh ghi đè các giá trị đã lưu.",
"Policy JSON": "JSON chính sách",
"Polling": "Thăm dò",
"Polling mode requires Redis and memory cache, otherwise performance will be significantly degraded": "Chế độ thăm dò yêu cầu Redis và bộ nhớ đệm, nếu không hiệu suất sẽ bị suy giảm đáng kể.",
@@ -2983,6 +2997,7 @@
"Prepend to Start": "Thêm vào đầu",
"Prepend value to array / string / object start": "Thêm giá trị vào đầu mảng / chuỗi / đối tượng",
"Preserve the original field when applying this rule": "Giữ trường gốc khi áp dụng quy tắc này",
"Preset groups": "Nhóm đặt sẵn",
"Preset recharge amounts (JSON array)": "Số tiền nạp đặt trước (mảng JSON)",
"Preset recharge amounts displayed to users": "Các mức nạp tiền đặt trước hiển thị cho người dùng",
"Preset Template": "Mẫu cài sẵn",
@@ -3098,6 +3113,7 @@
"Querying...": "Đang truy vấn...",
"Question": "Câu hỏi",
"Queued": "Trong hàng đợi",
"Quick actions": "Thao tác nhanh",
"Quick insert common payment methods": "Chèn nhanh các phương thức thanh toán phổ biến",
"Quick Range": "Phạm vi nhanh",
"Quick Setup from Preset": "Thiết lập nhanh từ cấu hình sẵn",
@@ -3219,6 +3235,7 @@
"Remaining:": "Còn lại:",
"Remark": "Nhận xét",
"Remove": "Xóa",
"Remove {{value}}": "Xóa {{value}}",
"Remove ${{amount}}": "Xóa ${{amount}}",
"Remove all log entries created before the selected timestamp.": "Xóa tất cả các mục nhật ký được tạo trước mốc thời gian đã chọn.",
"Remove attachment": "Xóa tệp đính kèm",
@@ -3226,6 +3243,7 @@
"Remove Duplicates": "Xóa trùng lặp",
"Remove filter": "Xóa bộ lọc",
"Remove functionResponse.id field": "Loại bỏ trường functionResponse.id",
"Remove mapped targets": "Xóa đích đã ánh xạ",
"Remove Models": "Xóa mô hình",
"Remove Passkey": "Xóa Khóa truy cập",
"Remove Passkey?": "Xóa khóa truy cập?",
@@ -3329,7 +3347,6 @@
"Retain last N files": "Giữ lại N tệp gần nhất",
"Retention days": "Số ngày lưu giữ",
"Retry": "Thử lại",
"auth.resetPasswordConfirm.retry": "Thử lại ({{seconds}} giây)",
"Retry Chain": "Chuỗi thử lại",
"Retry Suggestion": "Gợi ý thử lại",
"Retry Times": "Số lần thử lại",
@@ -3339,7 +3356,6 @@
"Return Error": "Trả về lỗi",
"Return per-token log probabilities": "Trả về log probabilities cho từng token",
"Return to dashboard": "Quay lại bảng điều khiển",
"auth.resetPasswordConfirm.backToLogin": "Quay lại đăng nhập",
"Return vector embeddings for inputs": "Trả về vector embedding cho đầu vào",
"Reveal API key": "Hiển thị khóa API",
"Reveal key": "Display key",
@@ -3491,7 +3507,6 @@
"Select all (filtered)": "Chọn tất cả (đã lọc)",
"Select all models": "Chọn tất cả mô hình",
"Select All Visible": "Chọn tất cả hiển thị",
"Select model {{model}}": "Chọn mô hình {{model}}",
"Select an operation mode and enter the amount": "Chọn chế độ thao tác và nhập số tiền",
"Select announcement type": "Select notification type",
"Select at least one field to overwrite.": "Chọn ít nhất một trường để ghi đè.",
@@ -3518,6 +3533,7 @@
"Select layout style": "Chọn kiểu bố cục",
"Select locations": "Chọn vị trí",
"Select Model": "Chọn mẫu",
"Select model {{model}}": "Chọn mô hình {{model}}",
"Select models (empty for allow all)": "Chọn model (để trống nếu muốn cho",
"Select models and apply to channel models list.": "Chọn mô hình và áp dụng cho danh sách mô hình kênh.",
"Select models or add custom ones": "Chọn các mô hình hoặc thêm các mô hình tùy chỉnh",
@@ -3547,6 +3563,7 @@
"Select vendor": "Chọn nhà cung cấp",
"Selectable groups": "Nhóm có thể chọn",
"selected": "đã chọn",
"Selected {{count}}": "Đã chọn {{count}}",
"selected channel(s). Leave empty to remove tag.": "Kênh đã chọn. Để trống để xóa thẻ.",
"Selected conflicts were overwritten successfully.": "Các xung đột được chọn đã được ghi đè thành công.",
"Selected when creating a token and used as the default billing group for API calls.": "Được chọn khi tạo token và dùng làm nhóm tính phí mặc định cho các lệnh gọi API.",
@@ -3690,6 +3707,7 @@
"Status & Sync": "Trạng thái & Đồng bộ",
"Status Code": "Mã trạng thái",
"Status Code Mapping": "Ánh xạ mã trạng thái",
"Status code mapping must use valid HTTP status codes": "Ánh xạ mã trạng thái phải dùng mã trạng thái HTTP hợp lệ",
"Status Page Slug": "Đường dẫn phụ trang trạng thái",
"Status short": "TT",
"Status:": "Trạng thái:",
@@ -3897,6 +3915,7 @@
"This action will permanently remove 2FA protection from your account.": "Hành động này sẽ vĩnh viễn gỡ bỏ tính năng bảo vệ",
"This channel is not an Ollama channel.": "Kênh này không phải là kênh Ollama.",
"This channel type does not support fetching models": "Loại kênh này không hỗ trợ lấy mô hình",
"This channel type requires additional configuration": "Loại kênh này yêu cầu cấu hình bổ sung",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "Xác nhận này mở khóa các tính năng thanh toán, mã đổi thưởng, gói đăng ký và phần thưởng mời. Vui lòng đọc kỹ các tuyên bố.",
"This controls model request rate limiting. Web/API route throttling is configured by environment variables and may still return 429.": "Thiết lập này kiểm soát giới hạn tốc độ yêu cầu mô hình. Giới hạn tuyến Web/API được cấu hình bằng biến môi trường và vẫn có thể trả về 429.",
"This data may be unreliable, use with caution": "Dữ liệu này có thể không đáng tin cậy, sử dụng thận trọng",
@@ -4215,6 +4234,7 @@
"Use external tools to extend capabilities": "Sử dụng công cụ ngoài để mở rộng khả năng",
"Use our unified OpenAI-compatible endpoint in your applications": "Sử dụng endpoint thống nhất tương thích OpenAI trong ứng dụng của bạn",
"Use Passkey to sign in without entering your password.": "Sử dụng Khóa truy cập để đăng nhập mà không cần nhập mật khẩu của bạn.",
"Use presets or upstream discovery to populate the model list faster.": "Dùng mẫu đặt sẵn hoặc phát hiện từ upstream để điền danh sách mô hình nhanh hơn.",
"Use secure connection when sending emails": "Sử dụng kết nối an toàn khi gửi email",
"Use sidebar shortcut": "Sử dụng phím tắt thanh bên",
"Use the full-width table to scan prices, then select a row to edit it here.": "Duyệt giá trong bảng, rồi chọn một hàng để chỉnh sửa tại đây.",
@@ -4302,8 +4322,10 @@
"Verifying credentials and pulling stores from your Pancake account...": "Đang xác minh thông tin xác thực và lấy cửa hàng từ tài khoản Pancake của bạn...",
"Version Overrides": "Ghi đè phiên bản",
"Vertex AI": "Vertex AI",
"Vertex AI API Key mode does not support batch creation": "Chế độ API Key của Vertex AI không hỗ trợ tạo hàng loạt",
"Vertex AI does not support functionResponse.id. Enable this to remove the field automatically.": "Vertex AI không hỗ trợ functionResponse.id. Bật để tự động loại bỏ trường này.",
"Vertex AI Key Format": "Định dạng khóa Vertex AI",
"Vertex AI service account key must be valid JSON": "Khóa tài khoản dịch vụ Vertex AI phải là JSON hợp lệ",
"Video": "Video",
"Video length in seconds": "Độ dài video (giây)",
"Video Remix": "Remix video",
@@ -4466,7 +4488,6 @@
"Your GitHub OAuth Client ID": "Client ID OAuth GitHub của bạn",
"Your GitHub OAuth Client Secret": "Bí mật ứng dụng OAuth của GitHub của bạn",
"Your new backup codes are ready": "Mã dự phòng mới của bạn đã sẵn sàng",
"auth.resetPasswordConfirm.success": "Mật khẩu của bạn đã được đặt lại thành công",
"Your Referral Link": "Liên kết giới thiệu của bạn",
"Your setup guide is collapsed so usage stays in focus.": "Hướng dẫn thiết lập đã thu gọn để giữ phần sử dụng ở vị trí nổi bật.",
"Your system access token for API authentication. Keep it secure and don't share it with others.": "Mã truy cập hệ thống của bạn để xác thực API. Hãy giữ nó an toàn và đừng chia sẻ nó với người khác.",