fix(currency): respect system currencyDisplay setting in UI#717
Conversation
Fixes #678 - Currency display unit configuration was not applied. Root cause: - `users-page-client.tsx` hardcoded `currencyCode="USD"` - `UserLimitBadge` and `LimitStatusIndicator` had hardcoded `unit="$"` default - `big-screen/page.tsx` used hardcoded "$" in multiple places Changes: - Add `getCurrencySymbol()` helper function to currency.ts - Fetch system settings in `users-page-client.tsx` and pass to table - Pass `currencySymbol` from `user-key-table-row.tsx` to limit badges - Remove hardcoded "$" defaults from badge components - Update big-screen page to fetch settings and use dynamic symbol - Add unit tests for `getCurrencySymbol` Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthrough该变更将系统的货币显示从硬编码的"$"改为动态系统设置值。在仪表板和大屏页面中集成货币符号解析,新增getCurrencySymbol工具函数以支持多种货币代码的符号映射,并添加了相应的单元测试。 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request implements a crucial fix to ensure that the application's user interface consistently respects the global Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request effectively addresses the issue of hardcoded currency symbols by introducing a centralized getCurrencySymbol helper and fetching the currencyDisplay setting from the backend. The changes are applied consistently across the user management and big screen pages, removing hardcoded '$' and 'USD' values. The inclusion of unit tests for the new currency utility is a great addition. My review includes a few suggestions to improve code consistency, reduce duplication, and enhance robustness.
|
|
||
| // Fetch system settings for currency display | ||
| const { data: systemSettings } = useSWR( | ||
| "system-settings", |
There was a problem hiding this comment.
The cache key for system settings is inconsistent between useQuery and useSWR. In users-page-client.tsx, the key ['system-settings'] is used. To ensure data is fetched once and cached consistently across different data-fetching hooks for the same resource, it's best to use the same key format. useSWR supports array keys, so aligning it with the useQuery implementation is recommended.
| "system-settings", | |
| ["system-settings"], |
| { revalidateOnFocus: false } | ||
| ); | ||
|
|
||
| const currencySymbol = CURRENCY_CONFIG[systemSettings?.currencyDisplay ?? "USD"]?.symbol ?? "$"; |
There was a problem hiding this comment.
This line duplicates the logic that is now available in the new getCurrencySymbol helper function. To improve maintainability and avoid code duplication, you should use the helper function here.
You will also need to update the import from @/lib/utils/currency to include getCurrencySymbol and you can remove the CURRENCY_CONFIG import as it's no longer directly used.
| const currencySymbol = CURRENCY_CONFIG[systemSettings?.currencyDisplay ?? "USD"]?.symbol ?? "$"; | |
| const currencySymbol = getCurrencySymbol(systemSettings?.currencyDisplay); |
| if (!currencyCode || !(currencyCode in CURRENCY_CONFIG)) { | ||
| return CURRENCY_CONFIG.USD.symbol; | ||
| } | ||
| return CURRENCY_CONFIG[currencyCode as CurrencyCode].symbol; |
There was a problem hiding this comment.
The current implementation for checking the currency code is case-sensitive. For example, getCurrencySymbol('usd') would fall back to the default '$' instead of returning '$' for USD. To make the function more robust, consider converting the input currencyCode to uppercase before checking its existence in CURRENCY_CONFIG.
After making this change, please ensure the unit test in tests/unit/lib/utils/currency.test.ts is updated to reflect the case-insensitive behavior (e.g., expect(getCurrencySymbol('cny')).toBe('¥')).
const code = currencyCode?.toUpperCase();
if (code && code in CURRENCY_CONFIG) {
return CURRENCY_CONFIG[code as CurrencyCode].symbol;
}
return CURRENCY_CONFIG.USD.symbol;
🧪 测试结果
总体结果: ✅ 所有测试通过 |
There was a problem hiding this comment.
Code Review Summary
This PR successfully addresses issue #678 by implementing dynamic currency display throughout the UI. The implementation is clean and well-tested, but there is one high-priority issue that should be addressed.
PR Size: S
- Lines changed: 110 (102 additions, 8 deletions)
- Files changed: 7
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 1 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
High Priority Issue (Should Fix)
1. [ERROR-SILENT] Silent error handling in system settings fetch
Location:
src/app/[locale]/dashboard/users/users-page-client.tsx:195-203src/app/[locale]/internal/dashboard/big-screen/page.tsx:751-759
Problem: Both fetch operations for system settings throw errors that React Query/SWR will silently swallow. When the API call fails, users see no indication that currency settings couldn't be loaded, and the fallback to USD happens silently.
Why this matters: This violates CLAUDE.md Critical Rule #1: "No Silent Failures - Any error caught without logging or user feedback is a CRITICAL defect."
Real-world impact: If /api/system-settings is unavailable, users who configured CNY/EUR will see USD symbols without any error feedback, causing confusion about whether their settings are being respected.
Current code:
const { data: systemSettings } = useQuery({
queryFn: async () => {
const response = await fetch("/api/system-settings");
if (\!response.ok) throw new Error("Failed to fetch settings"); // ← Error thrown but not logged
return response.json() as Promise<{ currencyDisplay: CurrencyCode }>;
},
staleTime: 30_000,
});Suggested fix (Option 1 - Explicit error handling):
const { data: systemSettings } = useQuery({
queryKey: ["system-settings"],
queryFn: async () => {
try {
const response = await fetch("/api/system-settings");
if (\!response.ok) {
logger.warn("Failed to fetch system settings", { status: response.status });
return { currencyDisplay: "USD" as CurrencyCode }; // Explicit fallback
}
return response.json() as Promise<{ currencyDisplay: CurrencyCode }>;
} catch (error) {
logger.error("System settings fetch error", { error });
return { currencyDisplay: "USD" as CurrencyCode }; // Explicit fallback
}
},
staleTime: 30_000,
});Suggested fix (Option 2 - onError callback):
const { data: systemSettings } = useQuery({
queryKey: ["system-settings"],
queryFn: async () => {
const response = await fetch("/api/system-settings");
if (\!response.ok) throw new Error("Failed to fetch settings");
return response.json() as Promise<{ currencyDisplay: CurrencyCode }>;
},
staleTime: 30_000,
onError: (error) => {
logger.warn("Failed to load currency settings, using USD fallback", { error });
},
});Apply the same fix to both files.
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - 1 issue found
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Good (comprehensive unit tests for getCurrencySymbol)
- Code clarity - Good
Positive Observations (Not Issues)
- Proper fallback to USD when settings unavailable
- Comprehensive unit tests covering all currency codes
- Consistent pattern across both pages
- Clean separation of concerns with
getCurrencySymbolhelper - Appropriate caching strategy (30s staleTime)
Automated review by Claude AI
| */ | ||
| export function getCurrencySymbol(currencyCode?: CurrencyCode | string): string { | ||
| if (!currencyCode || !(currencyCode in CURRENCY_CONFIG)) { | ||
| return CURRENCY_CONFIG.USD.symbol; |
There was a problem hiding this comment.
[HIGH] [LOGIC-BUG] getCurrencySymbol uses in which accepts prototype keys, so some invalid inputs bypass the fallback and can return a non-string.
Evidence (src/lib/utils/currency.ts):
if (!currencyCode || !(currencyCode in CURRENCY_CONFIG)) {
return CURRENCY_CONFIG.USD.symbol;
}
return CURRENCY_CONFIG[currencyCode as CurrencyCode].symbol;Why this is a problem: currencyCode in CURRENCY_CONFIG is also true for inherited properties (e.g. "toString", "constructor"). In those cases, CURRENCY_CONFIG[currencyCode as CurrencyCode] is not a currency config entry, so .symbol becomes undefined, violating the declared return type (: string) and dropping the symbol in the UI.
Suggested fix:
export function getCurrencySymbol(currencyCode?: CurrencyCode | string): string {
if (!currencyCode || !Object.hasOwn(CURRENCY_CONFIG, currencyCode)) {
return CURRENCY_CONFIG.USD.symbol;
}
return CURRENCY_CONFIG[currencyCode as CurrencyCode].symbol;
}(Optional) Add an edge-case test to lock this down:
expect(getCurrencySymbol("toString")).toBe("$");There was a problem hiding this comment.
Code Review Summary
This PR wires currencyDisplay into several UI surfaces, but the new getCurrencySymbol helper has a membership check bug that can return undefined for certain invalid inputs (prototype keys). That breaks the function contract (: string) and can drop the currency symbol in the UI.
PR Size: S
- Lines changed: 110
- Files changed: 7
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 1 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Critical Issues (Must Fix)
None
High Priority Issues (Should Fix)
src/lib/utils/currency.ts:141-getCurrencySymbolusesinfor membership checks; prototype keys can bypass the fallback and returnundefined.
Review Coverage
- Logic and correctness
- Security (OWASP Top 10)
- Error handling
- Type safety
- Documentation accuracy
- Test coverage
- Code clarity
Automated review by Codex AI
There was a problem hiding this comment.
- Identified PR
#717(fix(currency): respect system currencyDisplay setting in UI) and generated the full diff for review. - Applied size label:
size/S(110 lines changed across 7 files). - Posted 1 inline review comment (HIGH / LOGIC-BUG) on
src/lib/utils/currency.tsforgetCurrencySymbolusingin(prototype keys can bypass fallback and yieldundefined); suggested switching toObject.hasOwn(...)and adding an edge-case test. - Submitted the required PR review summary via
gh pr review --comment.
* fix(proxy): add 'cannot be modified' error detection to thinking signature rectifier Extend the thinking signature rectifier to detect and handle the Anthropic API error when thinking/redacted_thinking blocks have been modified from their original response. This error occurs when clients inadvertently modify these blocks in multi-turn conversations. The rectifier will now remove these blocks and retry the request, similar to how it handles other thinking-related signature errors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(deps): bump jspdf in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [jspdf](https://github.com/parallax/jsPDF). Updates `jspdf` from 3.0.4 to 4.1.0 - [Release notes](https://github.com/parallax/jsPDF/releases) - [Changelog](https://github.com/parallax/jsPDF/blob/master/RELEASE.md) - [Commits](parallax/jsPDF@v3.0.4...v4.1.0) --- updated-dependencies: - dependency-name: jspdf dependency-version: 4.1.0 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * fix: Hot-reload cache invalidation for Request Filters and Sensitive Words (#710) * fix: hot-reload request filters via globalThis singleton pattern EventEmitter and RequestFilterEngine now use globalThis caching to ensure the same instance is shared across different Next.js worker contexts. This fixes the issue where filter changes required Docker restart. Added diagnostic logging for event subscription and propagation. * fix(redis): wait for subscriber connection ready before subscribe - ensureSubscriber now returns Promise<Redis>, waits for 'ready' event - subscribeCacheInvalidation returns null on failure instead of noop - RequestFilterEngine checks cleanup !== null before logging success - Fixes false "Subscribed" log when Redis connection fails * feat(sensitive-words): add hot-reload via Redis pub/sub Enable real-time cache invalidation for sensitive words detector, matching the pattern used by request-filter-engine and error-rule-detector. * fix(redis): harden cache invalidation subscriptions Ensure sensitive-words CRUD emits update events so hot-reload propagates across workers. Roll back failed pub/sub subscriptions, add retry/timeout coverage, and avoid sticky provider-cache subscription state. * fix(codex): bump default User-Agent fallback Update the hardcoded Codex UA used when requests lack an effective user-agent (e.g. filtered out). Keep unit tests in sync with the new default. * fix(redis): resubscribe cache invalidation after reconnect Clear cached subscription state on disconnect and resubscribe on ready so cross-worker cache invalidation survives transient Redis reconnects. Add unit coverage, avoid misleading publish logs, track detector cleanup handlers, and translate leftover Russian comments to English. * fix(sensitive-words): use globalThis singleton detector Align SensitiveWordDetector with existing __CCH_* singleton pattern to avoid duplicate instances across module reloads. Extend singleton unit tests to cover the detector. * chore: format code (req-fix-dda97fd) * fix: address PR review comments - pubsub.ts: use `once` instead of `on` for ready event to prevent duplicate resubscription handlers on reconnect - forwarder.ts: extract DEFAULT_CODEX_USER_AGENT constant - provider-cache.ts: wrap subscribeCacheInvalidation in try/catch - tests: use exported constant instead of hardcoded UA string * fix(redis): resubscribe across repeated reconnects Ensure pub/sub resubscribe runs on every reconnect, extend unit coverage, and keep emitRequestFiltersUpdated resilient when logger import fails. --------- Co-authored-by: John Doe <johndoe@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * feat(logs): make cost column toggleable with improved type safety (#715) close #713 * fix(proxy): add OpenAI chat completion format support in usage extraction (#705) (#716) The `extractUsageMetrics` function was missing support for OpenAI chat completion format fields (`prompt_tokens`/`completion_tokens`), causing token statistics to not be recorded for OpenAI-compatible providers. Changes: - Add `prompt_tokens` -> `input_tokens` mapping - Add `completion_tokens` -> `output_tokens` mapping - Preserve priority: Claude > Gemini > OpenAI format - Add 5 unit tests for OpenAI format handling Closes #705 Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * fix(currency): respect system currencyDisplay setting in UI (#717) Fixes #678 - Currency display unit configuration was not applied. Root cause: - `users-page-client.tsx` hardcoded `currencyCode="USD"` - `UserLimitBadge` and `LimitStatusIndicator` had hardcoded `unit="$"` default - `big-screen/page.tsx` used hardcoded "$" in multiple places Changes: - Add `getCurrencySymbol()` helper function to currency.ts - Fetch system settings in `users-page-client.tsx` and pass to table - Pass `currencySymbol` from `user-key-table-row.tsx` to limit badges - Remove hardcoded "$" defaults from badge components - Update big-screen page to fetch settings and use dynamic symbol - Add unit tests for `getCurrencySymbol` Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * feat(gemini): add Google Search web access preference for Gemini providers (#721) * feat(gemini): add Google Search web access preference for Gemini providers Add provider-level preference for Gemini API type providers to control Google Search (web access) tool injection: - inherit: Follow client request (default) - enabled: Force inject googleSearch tool into request - disabled: Force remove googleSearch tool from request Changes: - Add geminiGoogleSearchPreference field to provider schema - Add GeminiGoogleSearchPreference type and validation - Implement applyGeminiGoogleSearchOverride with audit trail - Add UI controls in provider form (Gemini Overrides section) - Add i18n translations for 5 languages (en, zh-CN, zh-TW, ja, ru) - Integrate override logic in proxy forwarder for Gemini requests - Add 22 unit tests for the override logic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(gemini): address code review feedback - Use explicit else-if for disabled preference check (gemini-code-assist) - Use i18n for SelectValue placeholder instead of hardcoded string (coderabbitai) - Sync overridden body back to session.request.message for log consistency (coderabbitai) - Persist Gemini special settings immediately, matching Anthropic pattern (coderabbitai) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(gemini): use strict types for provider config and audit - Narrow preference type to "enabled" | "disabled" (exclude unreachable "inherit") - Use ProviderType and GeminiGoogleSearchPreference types instead of string Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据 (#720) * fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据 * fix(auth): 让 scoped session 继承 allowReadOnlyAccess 语义并支持内部降权校验 * chore: format code (dev-93585fa) * fix: bound SessionTracker active_sessions zsets by env TTL (#718) * fix(session): bound active_sessions zsets by env ttl Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(rate-limit): pass session ttl to lua cleanup Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(session): validate SESSION_TTL env and prevent ZSET leak on invalid values - Add input validation for SESSION_TTL (reject NaN, 0, negative; default 300) - Guard against invalid TTL in Lua script to prevent clearing all sessions - Use dynamic EXPIRE based on SESSION_TTL instead of hardcoded 3600s - Add unit tests for TTL validation and dynamic expiry behavior --------- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(provider): stop standard-path fallback to legacy provider url * feat(providers): expose vendor endpoint pools in settings UI (#719) * feat(providers): add endpoint status mapping * feat(providers): add endpoint pool hover * feat(providers): show vendor endpoints in list rows * feat(providers): extract vendor endpoint CRUD table * chore(i18n): add provider endpoint UI strings * fix(providers): integrate endpoint pool into provider form * fix(provider): wrap endpoint sync in transactions to prevent race conditions (#730) * fix(provider): wrap provider create/update endpoint sync in transactions Provider create and update operations now run vendor resolution and endpoint sync inside database transactions to prevent race conditions that could leave orphaned or inconsistent endpoint rows. Key changes: - createProvider: wrap vendor + insert + endpoint seed in a single tx - updateProvider: wrap vendor + update + endpoint sync in a single tx - Add syncProviderEndpointOnProviderEdit for atomic URL/type/vendor migration with in-place update, soft-delete, and conflict handling - Vendor cleanup failures degrade to warnings instead of propagating - Add comprehensive unit and integration tests for sync edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(provider): defer endpoint circuit reset until transaction commit Avoid running endpoint circuit reset side effects inside DB transactions to prevent rollback inconsistency. Run resets only after commit and add regression tests for deferred reset behavior in helper and provider update flows. * fix(provider): distinguish noop from created-next in endpoint sync action label When ensureNextEndpointActive() returns "noop" (concurrent transaction already created an active next endpoint), the action was incorrectly labelled "kept-previous-and-created-next". Add a new "kept-previous-and-kept-next" action to ProviderEndpointSyncAction and use a three-way branch so callers and logs reflect the true outcome. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review comments from PR #731 - fix(auth): prevent scoped session access widening via ?? -> && guard - fix(i18n): standardize zh-CN provider terminology to "服务商" - fix(i18n): use consistent Russian translations for circuit status - fix(i18n): replace raw formatDistanceToNow with locale-aware RelativeTime - fix(gemini): log warning for unknown google search preference values - fix(error-rules): check subscribeCacheInvalidation return value - fix(test): correct endpoint hover sort test to assert URLs not labels Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export auth session storage and fix test mock types - Export authSessionStorage from auth-session-storage.node.ts to prevent undefined on named imports; remove duplicate declare global block - Fix mockEndpoints in provider-endpoint-hover test: remove nonexistent lastOk/lastLatencyMs fields, add missing lastProbe* fields, use Date objects for createdAt/updatedAt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com> Co-authored-by: John Doe <johndoe@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: 泠音 <im@ling.plus> Co-authored-by: Longlone <41830147+WAY29@users.noreply.github.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Summary
currencyDisplaysystem setting (e.g., CNY, EUR) now correctly applies across the dashboardRoot Cause
users-page-client.tsxhardcodedcurrencyCode="USD"instead of reading from system settingsUserLimitBadgeandLimitStatusIndicatorhad hardcodedunit="$"default valuesbig-screen/page.tsxused hardcoded "$" symbols in multiple placesChanges
currency.tsgetCurrencySymbol()helper functionusers-page-client.tsxcurrencyDisplayto tableuser-key-table-row.tsxuser-limit-badge.tsx"$"default fromunitproplimit-status-indicator.tsx"$"default fromunitpropbig-screen/page.tsxcurrency.test.tsgetCurrencySymbolTest Plan
getCurrencySymbolpasscurrencyDisplayto "CNY" in system settingsGenerated with Claude Code
Greptile Overview
Greptile Summary
Fixed issue #678 where currency display configuration (
currencyDisplaysystem setting) was not applied to the UI. The fix correctly propagates the configured currency symbol throughout the application.Key Changes
getCurrencySymbol()helper function to convert currency codes to symbols"$"defaults fromuser-limit-badge.tsxandlimit-status-indicator.tsxusers-page-client.tsxto fetch system settings and passcurrencyDisplayto the tableuser-key-table-row.tsxto convert currency code to symbol for all limit badgesbig-screen/page.tsxto fetch system settings and use dynamic currency symbols throughoutImplementation Quality
The implementation is clean and follows best practices:
/api/system-settingsendpoint with appropriate caching (staleTime: 30_000)Testing
Unit tests verify correct symbol mapping for all supported currencies (USD, CNY, EUR, JPY, GBP, HKD, TWD, KRW, SGD) and proper fallback behavior.
Confidence Score: 5/5
Important Files Changed
getCurrencySymbolhelper function with proper fallback to USDcurrencyDisplayto table instead of hardcoded USDSequence Diagram
sequenceDiagram participant User participant UsersPage as users-page-client.tsx participant BigScreen as big-screen/page.tsx participant API as /api/system-settings participant DB as Database participant TableRow as user-key-table-row.tsx participant Badge as user-limit-badge.tsx participant Currency as currency.ts Note over User,Currency: Currency Display Configuration Flow User->>UsersPage: Load users page UsersPage->>API: GET /api/system-settings API->>DB: getSystemSettings() DB-->>API: { currencyDisplay: "CNY" } API-->>UsersPage: { currencyDisplay: "CNY" } UsersPage->>TableRow: Pass currencyCode="CNY" TableRow->>Currency: getCurrencySymbol("CNY") Currency-->>TableRow: "¥" TableRow->>Badge: Pass unit="¥" Badge-->>User: Display "¥100.50" User->>BigScreen: Load big screen dashboard BigScreen->>API: GET /api/system-settings API->>DB: getSystemSettings() DB-->>API: { currencyDisplay: "CNY" } API-->>BigScreen: { currencyDisplay: "CNY" } BigScreen->>Currency: CURRENCY_CONFIG["CNY"].symbol Currency-->>BigScreen: "¥" BigScreen-->>User: Display metrics with "¥" symbol