turash/bugulma/frontend/components/matches/MatchCard.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

113 lines
3.7 KiB
TypeScript

import React from 'react';
import { useTranslation } from '@/hooks/useI18n';
import { Card } from '@/components/ui/Card';
import Badge from '@/components/ui/Badge';
import { TrendingUp, MapPin, AlertTriangle } from 'lucide-react';
import type { BackendMatch } from '@/schemas/backend/match';
import { Flex, Grid, Stack } from '@/components/ui/layout';
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)}%`;
};
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(value);
};
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">
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>{match.DistanceKm.toFixed(1)} km</span>
</div>
{match.RiskAssessment && (
<div className="flex items-center gap-1">
<AlertTriangle className="h-4 w-4" />
<span>
Risk:{' '}
{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);