turash/bugulma/frontend/pages/ResourceFlowDetailPage.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

288 lines
9.8 KiB
TypeScript

import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { MainLayout } from '@/components/layout/MainLayout.tsx';
import PageHeader from '@/components/layout/PageHeader.tsx';
import ResourceFlowCard from '@/components/resource-flow/ResourceFlowCard.tsx';
import MatchesList from '@/components/matches/MatchesList.tsx';
import { Container, Stack, Grid, Flex } from '@/components/ui/layout';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card.tsx';
import Button from '@/components/ui/Button.tsx';
import Badge from '@/components/ui/Badge.tsx';
import { useTranslation } from '@/hooks/useI18n.tsx';
import { useResourceFlow } from '@/hooks/api/useResourcesAPI.ts';
import { useNavigation } from '@/hooks/useNavigation.tsx';
import { ArrowLeft, DollarSign, FlaskConical, Thermometer, TrendingUp } from 'lucide-react';
import type { BackendResourceFlow } from '@/schemas/backend/resource-flow';
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: quantityData.amount ? `${quantityData.amount} ${quantityData.unit || ''}` : 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 ? `${qualityData.temperature_celsius}°C` : t('common.na'),
},
{
label: t('resourceFlowDetail.quality.pressure'),
value: qualityData.pressure_bar ? `${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;