diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d9cb932f..29410397 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -138,7 +138,6 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -2268,6 +2267,7 @@ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", "license": "MIT", + "peer": true, "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.2", @@ -2280,6 +2280,7 @@ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -2289,6 +2290,7 @@ "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", "license": "MIT", + "peer": true, "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/icu-skeleton-parser": "1.8.16", @@ -2300,6 +2302,7 @@ "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", "license": "MIT", + "peer": true, "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "tslib": "^2.8.0" @@ -2310,6 +2313,7 @@ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -2738,7 +2742,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6041,7 +6044,6 @@ "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-3.5.0.tgz", "integrity": "sha512-pKS3wZnJoL1iTyGBXAvCwduNNeghJHY6QSRSNNvpYnrrQrLZ6Owsazjyynu0e0ObRgks0i7Rv+pe2M7/MBTZpQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.16" } @@ -6131,7 +6133,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.89.0.tgz", "integrity": "sha512-SXbtWSTSRXyBOe80mszPxpEbaN4XPRUp/i0EfQK1uyj3KCk/c8FuPJNIRwzOVe/OU3rzxrYtiNabsAmk1l714A==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.89.0" }, @@ -6624,7 +6625,6 @@ "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6636,7 +6636,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -6778,7 +6777,6 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -7117,7 +7115,6 @@ "integrity": "sha512-xa57bCPGuzEFqGjPs3vVLyqareG8DX0uMkr5U/v5vLv5/ZUrBrPL7gzxzTJedEyZxFMfsozwTIbbYfEQVo3kgg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "1.6.1", "fast-glob": "^3.3.2", @@ -7215,7 +7212,6 @@ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7758,7 +7754,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", @@ -7964,7 +7959,6 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", "license": "MIT", - "peer": true, "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -8329,8 +8323,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/d3-array": { "version": "3.2.4", @@ -8512,7 +8505,6 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -8555,7 +8547,8 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/decimal.js-light": { "version": "2.5.1", @@ -9039,7 +9032,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -9143,7 +9135,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -10569,7 +10560,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.23.2" } @@ -10618,7 +10608,6 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz", "integrity": "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -12777,7 +12766,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -12944,7 +12932,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -13217,7 +13204,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -13290,7 +13276,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -13344,7 +13329,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.63.0.tgz", "integrity": "sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -13936,7 +13920,6 @@ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -14819,7 +14802,6 @@ "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -15351,7 +15333,6 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15764,7 +15745,6 @@ "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -16328,7 +16308,6 @@ "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "1.6.1", "@vitest/runner": "1.6.1", @@ -16710,7 +16689,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", diff --git a/frontend/src/components/dashboard/ActionQueueCard.tsx b/frontend/src/components/dashboard/ActionQueueCard.tsx index 0298ebc0..25d56ae9 100644 --- a/frontend/src/components/dashboard/ActionQueueCard.tsx +++ b/frontend/src/components/dashboard/ActionQueueCard.tsx @@ -67,6 +67,30 @@ const urgencyConfig = { }, }; +/** + * Helper function to translate keys with proper namespace handling + * Maps backend key formats to correct i18next namespaces + */ +function translateKey( + key: string, + params: Record, + tDashboard: any, + tReasoning: any +): string { + // Determine namespace based on key prefix + if (key.startsWith('reasoning.')) { + // Remove 'reasoning.' prefix and use reasoning namespace + const translationKey = key.substring('reasoning.'.length); + return tReasoning(translationKey, { ...params, defaultValue: key }); + } else if (key.startsWith('action_queue.') || key.startsWith('dashboard.') || key.startsWith('health.')) { + // Use dashboard namespace + return tDashboard(key, { ...params, defaultValue: key }); + } + + // Default to dashboard + return tDashboard(key, { ...params, defaultValue: key }); +} + function ActionItemCard({ action, onApprove, @@ -102,25 +126,25 @@ function ActionItemCard({ // Translate i18n fields (or fallback to deprecated text fields or reasoning_data for alerts) const reasoning = useMemo(() => { if (action.reasoning_i18n) { - return tDashboard(action.reasoning_i18n.key, action.reasoning_i18n.params); + return translateKey(action.reasoning_i18n.key, action.reasoning_i18n.params, tDashboard, tReasoning); } if (action.reasoning_data) { const formatted = formatPOAction(action.reasoning_data); return formatted.reasoning; } return action.reasoning || ''; - }, [action.reasoning_i18n, action.reasoning_data, action.reasoning, tDashboard, formatPOAction]); + }, [action.reasoning_i18n, action.reasoning_data, action.reasoning, tDashboard, tReasoning, formatPOAction]); const consequence = useMemo(() => { if (action.consequence_i18n) { - return tDashboard(action.consequence_i18n.key, action.consequence_i18n.params); + return translateKey(action.consequence_i18n.key, action.consequence_i18n.params, tDashboard, tReasoning); } if (action.reasoning_data) { const formatted = formatPOAction(action.reasoning_data); return formatted.consequence; } return ''; - }, [action.consequence_i18n, action.reasoning_data, tDashboard, formatPOAction]); + }, [action.consequence_i18n, action.reasoning_data, tDashboard, tReasoning, formatPOAction]); return (

- {action.title_i18n ? tDashboard(action.title_i18n.key, action.title_i18n.params) : (action.title || 'Action Required')} + {action.title_i18n ? translateKey(action.title_i18n.key, action.title_i18n.params, tDashboard, tReasoning) : (action.title || 'Action Required')}

- {action.subtitle_i18n ? tDashboard(action.subtitle_i18n.key, action.subtitle_i18n.params) : (action.subtitle || '')} + {action.subtitle_i18n ? translateKey(action.subtitle_i18n.key, action.subtitle_i18n.params, tDashboard, tReasoning) : (action.subtitle || '')}

@@ -493,7 +517,7 @@ function ActionItemCard({ {button.action === 'reject' && } {button.action === 'view_details' && } {button.action === 'modify' && } - {tDashboard(button.label_i18n.key, button.label_i18n.params)} + {translateKey(button.label_i18n.key, button.label_i18n.params, tDashboard, tReasoning)} ); })} diff --git a/frontend/src/components/dashboard/HealthStatusCard.tsx b/frontend/src/components/dashboard/HealthStatusCard.tsx index 5f89995d..a8ea5d37 100644 --- a/frontend/src/components/dashboard/HealthStatusCard.tsx +++ b/frontend/src/components/dashboard/HealthStatusCard.tsx @@ -50,8 +50,46 @@ const iconMap = { alert: AlertCircle, }; +/** + * Helper function to translate keys with proper namespace handling + * Maps backend key formats to correct i18next namespaces + */ +function translateKey( + key: string, + params: Record, + t: any +): string { + // Map key prefixes to their correct namespaces + const namespaceMap: Record = { + 'health.': 'dashboard', + 'dashboard.': 'dashboard', + 'reasoning.': 'reasoning', + 'production.': 'production', + 'jtbd.': 'reasoning', + }; + + // Find the matching namespace + let namespace = 'common'; + let translationKey = key; + + for (const [prefix, ns] of Object.entries(namespaceMap)) { + if (key.startsWith(prefix)) { + namespace = ns; + // Remove the first segment if it matches the namespace + // e.g., "health.headline_yellow" stays as is for dashboard:health.headline_yellow + // e.g., "reasoning.types.xyz" -> keep as "types.xyz" for reasoning:types.xyz + if (prefix === 'reasoning.') { + translationKey = key.substring(prefix.length); + } + break; + } + } + + return t(translationKey, { ...params, ns: namespace, defaultValue: key }); +} + export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProps) { - const { t, i18n } = useTranslation('reasoning'); + const { t, i18n } = useTranslation(['dashboard', 'reasoning', 'production']); // Get date-fns locale based on current language const dateLocale = i18n.language === 'es' ? es : i18n.language === 'eu' ? eu : enUS; @@ -85,21 +123,21 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp

{typeof healthStatus.headline === 'object' && healthStatus.headline?.key - ? t(healthStatus.headline.key, healthStatus.headline.params || {}) - : healthStatus.headline || t(`jtbd.health_status.${status}`)} + ? translateKey(healthStatus.headline.key, healthStatus.headline.params || {}, t) + : healthStatus.headline || t(`jtbd.health_status.${status}`, { ns: 'reasoning' })}

{/* Last Update */}
- {t('jtbd.health_status.last_updated')}:{' '} + {t('jtbd.health_status.last_updated', { ns: 'reasoning' })}:{' '} {healthStatus.lastOrchestrationRun ? formatDistanceToNow(new Date(healthStatus.lastOrchestrationRun), { addSuffix: true, locale: dateLocale, }) - : t('jtbd.health_status.never')} + : t('jtbd.health_status.never', { ns: 'reasoning' })}
@@ -108,7 +146,7 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp
- {t('jtbd.health_status.next_check')}:{' '} + {t('jtbd.health_status.next_check', { ns: 'reasoning' })}:{' '} {formatDistanceToNow(new Date(healthStatus.nextScheduledRun), { addSuffix: true, locale: dateLocale })}
@@ -126,7 +164,7 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp // Translate using textKey if available, otherwise use text const displayText = item.textKey - ? t(item.textKey.replace('.', ':'), item.textParams || {}) + ? translateKey(item.textKey, item.textParams || {}, t) : item.text || ''; return ( @@ -156,7 +194,7 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp
- {t('jtbd.health_status.critical_issues', { count: healthStatus.criticalIssues })} + {t('jtbd.health_status.critical_issues', { count: healthStatus.criticalIssues, ns: 'reasoning' })}
)} @@ -164,7 +202,7 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp
- {t('jtbd.health_status.actions_needed', { count: healthStatus.pendingActions })} + {t('jtbd.health_status.actions_needed', { count: healthStatus.pendingActions, ns: 'reasoning' })}
)} diff --git a/frontend/src/components/dashboard/ProductionTimelineCard.tsx b/frontend/src/components/dashboard/ProductionTimelineCard.tsx index de41e6a2..f2779c59 100644 --- a/frontend/src/components/dashboard/ProductionTimelineCard.tsx +++ b/frontend/src/components/dashboard/ProductionTimelineCard.tsx @@ -38,7 +38,7 @@ function TimelineItemCard({ }) { const priorityColor = priorityColors[item.priority as keyof typeof priorityColors] || 'var(--text-tertiary)'; const { formatBatchAction } = useReasoningFormatter(); - const { t } = useTranslation(['reasoning', 'dashboard']); + const { t } = useTranslation(['reasoning', 'dashboard', 'production']); // Translate reasoning_data (or use new reasoning_i18n or fallback to deprecated text field) // Memoize to prevent undefined values from being created on each render @@ -102,7 +102,13 @@ function TimelineItemCard({
- {item.status_i18n ? t(item.status_i18n.key, item.status_i18n.params) : item.statusText || 'Status'} + {item.status_i18n + ? t(item.status_i18n.key, { + ...item.status_i18n.params, + ns: item.status_i18n.key.startsWith('production.') ? 'production' : 'reasoning', + defaultValue: item.statusText || 'Status' + }) + : item.statusText || 'Status'} {item.status === 'IN_PROGRESS' && ( {item.progress || 0}%