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.
288 lines
9.8 KiB
TypeScript
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;
|