mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
293 lines
9.9 KiB
TypeScript
293 lines
9.9 KiB
TypeScript
import { MainLayout } from '@/components/layout/MainLayout.tsx';
|
|
import PageHeader from '@/components/layout/PageHeader.tsx';
|
|
import MatchesList from '@/components/matches/MatchesList.tsx';
|
|
import ResourceFlowCard from '@/components/resource-flow/ResourceFlowCard.tsx';
|
|
import { formatQuantity } from '@/components/resource-flow/utils';
|
|
import Badge from '@/components/ui/Badge.tsx';
|
|
import Button from '@/components/ui/Button.tsx';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card.tsx';
|
|
import { Container, Flex, Grid, Stack } from '@/components/ui/layout';
|
|
import { useResourceFlow } from '@/hooks/api/useResourcesAPI.ts';
|
|
import { useTranslation } from '@/hooks/useI18n.tsx';
|
|
import { useNavigation } from '@/hooks/useNavigation.tsx';
|
|
import type { BackendResourceFlow } from '@/schemas/backend/resource-flow';
|
|
import { DollarSign, FlaskConical, Thermometer, TrendingUp } from 'lucide-react';
|
|
import React from 'react';
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
|
|
const ResourceFlowDetailPage = () => {
|
|
const { id } = useParams<{ id: string }>();
|
|
const navigate = useNavigate();
|
|
const { t } = useTranslation();
|
|
const { handleBackNavigation, handleFooterNavigate } = useNavigation();
|
|
|
|
const { data: resourceFlow, isLoading, error } = useResourceFlow(id);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<MainLayout onNavigate={handleFooterNavigate} className="bg-muted/30">
|
|
<Container size="2xl" className="py-8 sm:py-12">
|
|
<div className="animate-pulse">
|
|
<div className="h-8 bg-muted rounded w-1/3 mb-8"></div>
|
|
<div className="h-64 bg-muted rounded mb-8"></div>
|
|
<div className="h-48 bg-muted rounded"></div>
|
|
</div>
|
|
</Container>
|
|
</MainLayout>
|
|
);
|
|
}
|
|
|
|
if (error || !resourceFlow) {
|
|
return (
|
|
<MainLayout onNavigate={handleFooterNavigate} className="bg-muted/30">
|
|
<Container size="2xl" className="py-8 sm:py-12">
|
|
<PageHeader
|
|
title={t('resourceFlowDetail.errorTitle')}
|
|
subtitle={t('resourceFlowDetail.errorSubtitle')}
|
|
onBack={handleBackNavigation}
|
|
/>
|
|
<Card>
|
|
<CardContent className="py-12">
|
|
<div className="text-center">
|
|
<p className="text-destructive mb-4">
|
|
{error?.message || t('resourceFlowDetail.notFound')}
|
|
</p>
|
|
<Button onClick={handleBackNavigation}>{t('common.back')}</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</Container>
|
|
</MainLayout>
|
|
);
|
|
}
|
|
|
|
const handleViewMatches = (resourceId: string) => {
|
|
navigate(`/matching?resourceId=${resourceId}`);
|
|
};
|
|
|
|
return (
|
|
<MainLayout onNavigate={handleFooterNavigate} className="bg-muted/30">
|
|
<Container size="2xl" className="py-8 sm:py-12">
|
|
<PageHeader
|
|
title={t('resourceFlowDetail.title')}
|
|
subtitle={`${resourceFlow.Type} ${resourceFlow.Direction}`}
|
|
onBack={handleBackNavigation}
|
|
/>
|
|
|
|
<Stack spacing="2xl">
|
|
{/* Resource Flow Overview */}
|
|
<ResourceFlowCard
|
|
resourceFlow={resourceFlow}
|
|
onViewMatches={handleViewMatches}
|
|
showFullDetails={true}
|
|
/>
|
|
|
|
{/* Key Metrics */}
|
|
<Grid cols={{ md: 2, lg: 4 }} gap="md">
|
|
<ResourceFlowMetrics resourceFlow={resourceFlow} />
|
|
</Grid>
|
|
|
|
{/* Quality Parameters */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<FlaskConical className="h-4 h-5 text-current w-4 w-5" />
|
|
{t('resourceFlowDetail.qualityParameters')}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<QualityParametersTable resourceFlow={resourceFlow} />
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Economic Data */}
|
|
{(resourceFlow.EconomicData?.cost_in ||
|
|
resourceFlow.EconomicData?.cost_out ||
|
|
resourceFlow.EconomicData?.waste_disposal_cost) && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<DollarSign className="h-4 h-5 text-current w-4 w-5" />
|
|
{t('resourceFlowDetail.economicData')}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<EconomicDataDisplay resourceFlow={resourceFlow} />
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Related Matches */}
|
|
<MatchesList resourceId={resourceFlow.ID} maxDistanceKm={50} minScore={0.5} limit={10} />
|
|
</Stack>
|
|
</Container>
|
|
</MainLayout>
|
|
);
|
|
};
|
|
|
|
// Component for displaying key metrics
|
|
const ResourceFlowMetrics: React.FC<{ resourceFlow: BackendResourceFlow }> = ({ resourceFlow }) => {
|
|
const { t } = useTranslation();
|
|
|
|
const metrics = [
|
|
{
|
|
label: t('resourceFlowDetail.direction'),
|
|
value: (
|
|
<Badge variant={resourceFlow.Direction === 'input' ? 'secondary' : 'default'}>
|
|
{t(`resourceFlowDirection.${resourceFlow.Direction}`)}
|
|
</Badge>
|
|
),
|
|
icon: <TrendingUp className="h-4 h-5 text-current text-primary w-4 w-5" />,
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.type'),
|
|
value: <Badge variant="outline">{t(`resourceTypes.${resourceFlow.Type}`)}</Badge>,
|
|
icon: <FlaskConical className="h-4 h-5 text-current text-primary w-4 w-5" />,
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.precision'),
|
|
value: (
|
|
<Badge variant="outline">
|
|
{t(`precisionLevels.${resourceFlow.PrecisionLevel || 'estimated'}`)}
|
|
</Badge>
|
|
),
|
|
icon: <Thermometer className="h-4 h-5 text-current text-primary w-4 w-5" />,
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.source'),
|
|
value: (
|
|
<Badge variant="outline">{t(`sourceTypes.${resourceFlow.SourceType || 'declared'}`)}</Badge>
|
|
),
|
|
icon: <DollarSign className="h-4 h-5 text-current text-primary w-4 w-5" />,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
{metrics.map((metric, index) => (
|
|
<Card key={index}>
|
|
<CardContent className="p-4">
|
|
<Flex align="center" gap="sm" className="mb-2">
|
|
{metric.icon}
|
|
<span className="text-sm font-medium text-muted-foreground">{metric.label}</span>
|
|
</Flex>
|
|
<div className="text-lg font-semibold">{metric.value}</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</>
|
|
);
|
|
};
|
|
|
|
// Component for displaying quality parameters
|
|
const QualityParametersTable: React.FC<{ resourceFlow: BackendResourceFlow }> = ({
|
|
resourceFlow,
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
|
|
const qualityData = resourceFlow.Quality || {};
|
|
const quantityData = resourceFlow.Quantity || {};
|
|
|
|
const parameters = [
|
|
{
|
|
label: t('resourceFlowDetail.quantity.amount'),
|
|
value: formatQuantity(quantityData) || t('common.na'),
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.quantity.temporalUnit'),
|
|
value: quantityData.temporal_unit
|
|
? t(`temporalUnits.${quantityData.temporal_unit}`)
|
|
: t('common.na'),
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.quality.temperature'),
|
|
value:
|
|
qualityData.temperature_celsius !== undefined
|
|
? `${qualityData.temperature_celsius}°C`
|
|
: t('common.na'),
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.quality.pressure'),
|
|
value:
|
|
qualityData.pressure_bar !== undefined ? `${qualityData.pressure_bar} bar` : t('common.na'),
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.quality.purity'),
|
|
value: qualityData.purity_pct ? `${qualityData.purity_pct}%` : t('common.na'),
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.quality.physicalState'),
|
|
value: qualityData.physical_state
|
|
? t(`physicalStates.${qualityData.physical_state}`)
|
|
: t('common.na'),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{parameters.map((param, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex justify-between items-center py-2 border-b border-muted last:border-b-0"
|
|
>
|
|
<span className="text-sm font-medium">{param.label}</span>
|
|
<span className="text-sm text-muted-foreground">{param.value}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// Component for displaying economic data
|
|
const EconomicDataDisplay: React.FC<{ resourceFlow: BackendResourceFlow }> = ({ resourceFlow }) => {
|
|
const { t } = useTranslation();
|
|
|
|
const economicData = resourceFlow.EconomicData || {};
|
|
|
|
const costs = [
|
|
{
|
|
label: t('resourceFlowDetail.economic.costIn'),
|
|
value: economicData.cost_in
|
|
? `€${economicData.cost_in}/${resourceFlow.Quantity?.unit || 'unit'}`
|
|
: null,
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.economic.costOut'),
|
|
value: economicData.cost_out
|
|
? `€${economicData.cost_out}/${resourceFlow.Quantity?.unit || 'unit'}`
|
|
: null,
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.economic.wasteCost'),
|
|
value: economicData.waste_disposal_cost
|
|
? `€${economicData.waste_disposal_cost}/${resourceFlow.Quantity?.unit || 'unit'}`
|
|
: null,
|
|
},
|
|
{
|
|
label: t('resourceFlowDetail.economic.transportCost'),
|
|
value: economicData.transportation_cost ? `€${economicData.transportation_cost}/km` : null,
|
|
},
|
|
].filter((cost) => cost.value);
|
|
|
|
if (costs.length === 0) {
|
|
return (
|
|
<p className="text-muted-foreground text-center py-4">
|
|
{t('resourceFlowDetail.economic.noData')}
|
|
</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{costs.map((cost, index) => (
|
|
<div key={index} className="flex justify-between items-center py-2">
|
|
<span className="text-sm font-medium">{cost.label}</span>
|
|
<span className="text-sm font-semibold text-primary">{cost.value}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ResourceFlowDetailPage;
|