mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- 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.
113 lines
3.7 KiB
TypeScript
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);
|