feat(dashboard): Add i18n support for action buttons

- Create getActionLabelKey() mapper function for action types
- Map action types (approve_po, call_supplier, etc.) to i18n keys
- Extract parameters from metadata (amount, supplier, customer, hours)
- Update button rendering to use translations instead of backend strings

Translation updates:
- Add missing action keys: reject_po, complete_receipt, mark_received
- Spanish translations: "Rechazar pedido", "Completar recepción", etc.
- Basque translations: "Baztertu eskaera", "Osatu stockaren harrera", etc.

Action buttons now respect user's language preference (EN/ES/EU)
instead of showing hardcoded backend strings.

Fixes: Issue #4 - Missing i18n for action buttons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Urtzi Alfaro
2025-11-27 07:40:12 +01:00
parent 70931cb4fd
commit 407429d50a
4 changed files with 962 additions and 1 deletions

View File

@@ -110,6 +110,71 @@ function EscalationBadge({ alert }: { alert: EnrichedAlert }) {
);
}
/**
* Map action type to i18n translation key with extracted parameters
*/
function getActionLabelKey(actionType: string, metadata?: Record<string, any>): { key: string; params: Record<string, any> } {
const actionTypeMap: Record<string, { key: string; extractParams?: (meta: Record<string, any>) => Record<string, any> }> = {
'approve_po': {
key: 'alerts:actions.approve_po',
extractParams: (meta) => ({ amount: meta.amount || meta.po_amount || 0 })
},
'reject_po': {
key: 'alerts:actions.reject_po',
extractParams: () => ({})
},
'call_supplier': {
key: 'alerts:actions.call_supplier',
extractParams: (meta) => ({ supplier: meta.supplier || meta.name || 'Supplier', phone: meta.phone || '' })
},
'open_reasoning': {
key: 'alerts:actions.see_reasoning',
extractParams: () => ({})
},
'complete_stock_receipt': {
key: 'alerts:actions.complete_receipt',
extractParams: () => ({})
},
'mark_delivery_received': {
key: 'alerts:actions.mark_received',
extractParams: () => ({})
},
'adjust_production': {
key: 'alerts:actions.adjust_production',
extractParams: () => ({})
},
'navigate': {
key: 'alerts:actions.navigate',
extractParams: () => ({})
},
'notify_customer': {
key: 'alerts:actions.notify_customer',
extractParams: (meta) => ({ customer: meta.customer_name || meta.customer || 'Customer' })
},
'snooze': {
key: 'alerts:actions.snooze',
extractParams: (meta) => ({ hours: meta.duration_hours || meta.hours || 4 })
},
'dismiss': {
key: 'alerts:actions.dismiss',
extractParams: () => ({})
},
'mark_read': {
key: 'alerts:actions.mark_read',
extractParams: () => ({})
},
'cancel_auto_action': {
key: 'alerts:actions.cancel_auto_action',
extractParams: () => ({})
},
};
const config = actionTypeMap[actionType] || { key: 'alerts:actions.navigate', extractParams: () => ({}) };
const params = config.extractParams ? config.extractParams(metadata || {}) : {};
return { key: config.key, params };
}
function ActionCard({ alert, showEscalationBadge = false, onActionSuccess, onActionError }: ActionCardProps) {
const [expanded, setExpanded] = useState(false);
const [loadingAction, setLoadingAction] = useState<string | null>(null);
@@ -305,7 +370,10 @@ function ActionCard({ alert, showEscalationBadge = false, onActionSuccess, onAct
}}
className={`${isPrimary ? 'font-semibold' : ''} sm:w-auto w-full`}
>
{action.label}
{(() => {
const { key, params } = getActionLabelKey(action.type, action.metadata);
return String(t(key, params));
})()}
{action.estimated_time_minutes && !isLoading && (
<span className="ml-1 opacity-60 text-xs">({action.estimated_time_minutes}m)</span>
)}