turash/bugulma/frontend/components/matches/MatchCard.tsx
Damir Mukimov f24628a248
Some checks failed
CI/CD Pipeline / backend-build (push) Has been skipped
CI/CD Pipeline / backend-lint (push) Failing after 31s
CI/CD Pipeline / frontend-lint (push) Failing after 1m26s
CI/CD Pipeline / frontend-build (push) Has been skipped
CI/CD Pipeline / e2e-test (push) Has been skipped
fix: resolve remaining linting and React Compiler errors
- Fix prettier formatting issues in multiple components
- Fix React Compiler memoization issues in ProductServiceMarkers.tsx
- Replace literal strings with i18n keys across components
- Address i18n issues in heritage, network graph, and match components
- Fix dependency arrays in useMemo hooks to match React Compiler expectations
2025-12-25 00:25:51 +01:00

108 lines
3.7 KiB
TypeScript

import Badge from '@/components/ui/Badge';
import { Card } from '@/components/ui/Card';
import { Flex, Grid, Stack } from '@/components/ui/layout';
import { useTranslation } from '@/hooks/useI18n';
import type { BackendMatch } from '@/schemas/backend/match';
import { AlertTriangle, MapPin, TrendingUp } from 'lucide-react';
import React from 'react';
import { formatCurrency } from '../../lib/fin';
interface MatchCardProps {
match: BackendMatch;
onViewDetails?: (matchId: string) => void;
}
const MatchCard: React.FC<MatchCardProps> = ({ match, onViewDetails }) => {
const { t } = useTranslation();
const getStatusColor = () => {
switch (match.Status) {
case 'suggested':
return 'default';
case 'negotiating':
return 'outline';
case 'contracted':
case 'live':
return 'default';
case 'failed':
case 'cancelled':
return 'destructive';
default:
return 'outline';
}
};
const formatScore = (score: number) => {
return `${(score * 100).toFixed(0)}%`;
};
// use central fin utilities
return (
<Card
className="shadow-none hover:shadow-md transition-shadow cursor-pointer"
onClick={() => onViewDetails?.(match.ID)}
>
<Stack spacing="sm" className="p-4">
<Flex align="start" justify="between">
<div className="flex-1">
<Flex align="center" gap="xs" className="mb-2">
<Badge variant={getStatusColor()}>{match.Status}</Badge>
<Badge variant="outline" className="text-xs">
{t('matches.priority', { priority: match.Priority })}
</Badge>
</Flex>
<Grid cols={2} gap="md" className="mb-3">
<div>
<p className="text-xs text-muted-foreground mb-1">{t('matches.compatibility')}</p>
<p className="text-lg font-semibold">{formatScore(match.CompatibilityScore)}</p>
</div>
<div>
<p className="text-xs text-muted-foreground mb-1">{t('matches.economicValue')}</p>
<p className="text-lg font-semibold text-success">
{formatCurrency(match.EconomicValue)}
</p>
</div>
</Grid>
<Flex align="center" gap="md" className="text-sm text-muted-foreground">
<div className="flex items-center gap-1">
<MapPin className="h-4 w-4" />
<span>{t('matches.distance', { distance: match.DistanceKm.toFixed(1) })}</span>
</div>
{match.RiskAssessment && (
<div className="flex items-center gap-1">
<AlertTriangle className="h-4 w-4" />
<span>
{t('matches.riskScore', {
score: formatScore(
(match.RiskAssessment.technical_risk + match.RiskAssessment.regulatory_risk) /
2
)
})}
</span>
</div>
)}
</Flex>
{match.EconomicImpact && match.EconomicImpact.annual_savings && (
<div className="mt-3 pt-3 border-t">
<Flex align="center" gap="xs" className="text-sm">
<TrendingUp className="h-4 w-4 text-success" />
<span className="text-muted-foreground">{t('matches.annualSavings')}:</span>
<span className="font-semibold text-success">
{formatCurrency(match.EconomicImpact.annual_savings)}
</span>
</Flex>
</div>
)}
</div>
</Flex>
</Stack>
</Card>
);
};
export default React.memo(MatchCard);