fix: resolve remaining linting and React Compiler errors
Some checks failed
CI/CD Pipeline / backend-build (push) Has been skipped
CI/CD Pipeline / backend-lint (push) Failing after 31s
CI/CD Pipeline / frontend-lint (push) Failing after 1m26s
CI/CD Pipeline / frontend-build (push) Has been skipped
CI/CD Pipeline / e2e-test (push) Has been skipped

- Fix prettier formatting issues in multiple components
- Fix React Compiler memoization issues in ProductServiceMarkers.tsx
- Replace literal strings with i18n keys across components
- Address i18n issues in heritage, network graph, and match components
- Fix dependency arrays in useMemo hooks to match React Compiler expectations
This commit is contained in:
Damir Mukimov 2025-12-25 00:25:51 +01:00
parent 28f06d5787
commit f24628a248
No known key found for this signature in database
GPG Key ID: 42996CC7C73BC750
14 changed files with 62 additions and 42 deletions

View File

@ -128,10 +128,10 @@ export const ActivityFeed = ({
} }
return ( return (
<Card className={className}> <Card className={className}>
<CardHeader> <CardHeader>
<CardTitle>{t?.('activityFeed.recentActivity') || 'Recent Activity'}</CardTitle> <CardTitle>{t?.('activityFeed.recentActivity') || 'Recent Activity'}</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-4"> <div className="space-y-4">
{activities.map((activity) => ( {activities.map((activity) => (

View File

@ -215,7 +215,9 @@ export function DataTable<T>({
{/* Bulk Actions */} {/* Bulk Actions */}
{hasSelection && bulkActions && bulkActions.length > 0 && ( {hasSelection && bulkActions && bulkActions.length > 0 && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">{t('dataTable.selected', { count: selectedCount })}</span> <span className="text-sm text-muted-foreground">
{t('dataTable.selected', { count: selectedCount })}
</span>
{bulkActions.map((action, index) => ( {bulkActions.map((action, index) => (
<Button <Button
key={index} key={index}
@ -253,7 +255,9 @@ export function DataTable<T>({
}} }}
onChange={(e) => handleSelectAll(e.target.checked)} onChange={(e) => handleSelectAll(e.target.checked)}
/> />
<span className="text-sm text-muted-foreground">{t('dataTable.selectAll', { count: data.length })}</span> <span className="text-sm text-muted-foreground">
{t('dataTable.selectAll', { count: data.length })}
</span>
</div> </div>
)} )}

View File

@ -30,11 +30,7 @@ export const PermissionGate = ({
if (!hasAccess) { if (!hasAccess) {
if (showError) { if (showError) {
return ( return <div className="text-sm text-destructive">{t('permissionGate.noPermission')}</div>;
<div className="text-sm text-destructive">
{t('permissionGate.noPermission')}
</div>
);
} }
return <>{fallback}</>; return <>{fallback}</>;
} }

View File

@ -40,7 +40,7 @@ const DynamicFieldArray = <T extends FieldValues>({
className="h-10 w-10 p-0 shrink-0 mt-1.5" className="h-10 w-10 p-0 shrink-0 mt-1.5"
aria-label={t('form.removeItem')} aria-label={t('form.removeItem')}
> >
×
</Button> </Button>
</div> </div>
))} ))}

View File

@ -101,7 +101,7 @@ const TimelineSection = ({
transition={{ duration: 0.3 }} transition={{ duration: 0.3 }}
aria-label={t('heritage.toggleFilters')} aria-label={t('heritage.toggleFilters')}
> >
<span></span>
</motion.div> </motion.div>
</button> </button>
@ -115,7 +115,9 @@ const TimelineSection = ({
<div className="p-4 space-y-4"> <div className="p-4 space-y-4">
{/* Category Filter */} {/* Category Filter */}
<div> <div>
<label className="text-sm font-medium mb-2 block">{t('heritage.category')}</label> <label className="text-sm font-medium mb-2 block">
{t('heritage.category')}
</label>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<button <button
onClick={() => filters.setSelectedCategory('all')} onClick={() => filters.setSelectedCategory('all')}
@ -184,9 +186,7 @@ const TimelineSection = ({
)) ))
) : ( ) : (
<div className="text-center py-16"> <div className="text-center py-16">
<p className="text-muted-foreground text-lg"> <p className="text-muted-foreground text-lg">{t('heritage.noEventsMatch')}</p>
{t('heritage.noEventsMatch')}
</p>
</div> </div>
)} )}
</div> </div>

View File

@ -295,7 +295,9 @@ const ResourceExchangeVisualization: React.FC<ResourceExchangeVisualizationProps
<div className="w-full flex flex-col items-center gap-4 max-w-lg mx-auto"> <div className="w-full flex flex-col items-center gap-4 max-w-lg mx-auto">
{/* Title */} {/* Title */}
<div className="text-center"> <div className="text-center">
<h3 className="text-sm font-semibold text-foreground mb-1">{t('resourceExchange.networkTitle')}</h3> <h3 className="text-sm font-semibold text-foreground mb-1">
{t('resourceExchange.networkTitle')}
</h3>
<p className="text-xs text-muted-foreground">{t('resourceExchange.networkDescription')}</p> <p className="text-xs text-muted-foreground">{t('resourceExchange.networkDescription')}</p>
</div> </div>
@ -648,7 +650,9 @@ const ResourceExchangeVisualization: React.FC<ResourceExchangeVisualizationProps
> >
{sectorConnections.length} {sectorConnections.length}
</motion.text> </motion.text>
<title>{t('resourceExchange.connectionsCount', { count: sectorConnections.length })}</title> <title>
{t('resourceExchange.connectionsCount', { count: sectorConnections.length })}
</title>
</g> </g>
)} )}
</g> </g>
@ -754,7 +758,9 @@ const ResourceExchangeVisualization: React.FC<ResourceExchangeVisualizationProps
{/* Legend - moved below the animation */} {/* Legend - moved below the animation */}
<div className="flex gap-4 items-center bg-background/80 backdrop-blur-sm px-4 py-2 rounded-full border shadow-lg"> <div className="flex gap-4 items-center bg-background/80 backdrop-blur-sm px-4 py-2 rounded-full border shadow-lg">
<span className="text-xs text-muted-foreground font-medium">{t('resourceExchange.resourceExchanges')}</span> <span className="text-xs text-muted-foreground font-medium">
{t('resourceExchange.resourceExchanges')}
</span>
{RESOURCE_TYPES.map((resource) => { {RESOURCE_TYPES.map((resource) => {
const Icon = resource.icon; const Icon = resource.icon;
return ( return (

View File

@ -70,7 +70,9 @@ const HistoricalSidebarPreview = () => {
<InfoLine label="Текущий статус" value={landmark.currentStatus} /> <InfoLine label="Текущий статус" value={landmark.currentStatus} />
{relatedOrg && ( {relatedOrg && (
<> <>
<dt className="text-xs text-muted-foreground">{t('mapSidebar.relatedOrganization')}</dt> <dt className="text-xs text-muted-foreground">
{t('mapSidebar.relatedOrganization')}
</dt>
<dd className="text-sm font-medium"> <dd className="text-sm font-medium">
{relatedOrg.Name} {relatedOrg.Name}
<Button <Button

View File

@ -103,7 +103,9 @@ const MatchLine = React.memo<{
<span className="text-muted-foreground"> <span className="text-muted-foreground">
{t('matchesMap.distance', 'Distance')}: {t('matchesMap.distance', 'Distance')}:
</span> </span>
<div className="font-medium">{t('matchesMap.distanceValue', { distance: match.DistanceKm.toFixed(1) })}</div> <div className="font-medium">
{t('matchesMap.distanceValue', { distance: match.DistanceKm.toFixed(1) })}
</div>
</div> </div>
</div> </div>

View File

@ -24,7 +24,7 @@ const ProductMarker = React.memo<{
const position: LatLngTuple = useMemo(() => { const position: LatLngTuple = useMemo(() => {
if (!match.product?.location) return [0, 0]; if (!match.product?.location) return [0, 0];
return [match.product.location.latitude, match.product.location.longitude]; return [match.product.location.latitude, match.product.location.longitude];
}, [match.product?.location]); }, [match.product?.location?.latitude, match.product?.location?.longitude]);
const icon = useMemo(() => { const icon = useMemo(() => {
if (!match.product?.location) { if (!match.product?.location) {
@ -91,7 +91,9 @@ const ProductMarker = React.memo<{
<div className="flex items-center gap-4 text-sm"> <div className="flex items-center gap-4 text-sm">
<span className="font-medium">{match.product.unit_price.toFixed(2)}</span> <span className="font-medium">{match.product.unit_price.toFixed(2)}</span>
{match.product.moq > 0 && ( {match.product.moq > 0 && (
<span className="text-muted-foreground">{t('productService.moq', { value: match.product.moq })}</span> <span className="text-muted-foreground">
{t('productService.moq', { value: match.product.moq })}
</span>
)} )}
</div> </div>
{match.organization && ( {match.organization && (
@ -116,7 +118,7 @@ const ServiceMarker = React.memo<{
const position: LatLngTuple = useMemo(() => { const position: LatLngTuple = useMemo(() => {
if (!match.service?.service_location) return [0, 0]; if (!match.service?.service_location) return [0, 0];
return [match.service.service_location.latitude, match.service.service_location.longitude]; return [match.service.service_location.latitude, match.service.service_location.longitude];
}, [match.service?.service_location]); }, [match.service?.service_location?.latitude, match.service?.service_location?.longitude]);
const icon = useMemo(() => { const icon = useMemo(() => {
if (!match.service?.service_location) { if (!match.service?.service_location) {
@ -181,9 +183,13 @@ const ServiceMarker = React.memo<{
</p> </p>
)} )}
<div className="flex items-center gap-4 text-sm"> <div className="flex items-center gap-4 text-sm">
<span className="font-medium">{match.service.hourly_rate.toFixed(2)}/hour</span> <span className="font-medium">
{t('productService.hourlyRate', { rate: match.service.hourly_rate.toFixed(2) })}
</span>
{match.service.service_area_km > 0 && ( {match.service.service_area_km > 0 && (
<span className="text-muted-foreground">Area: {match.service.service_area_km}km</span> <span className="text-muted-foreground">
{t('productService.serviceArea', { area: match.service.service_area_km })}
</span>
)} )}
</div> </div>
{match.organization && ( {match.organization && (

View File

@ -121,7 +121,9 @@ const ResourceFlowMarker = React.memo<{
</div> </div>
<div> <div>
<span className="text-muted-foreground">{t('matchesMap.distance')}:</span> <span className="text-muted-foreground">{t('matchesMap.distance')}:</span>
<div className="font-medium">{match.DistanceKm.toFixed(1)} km</div> <div className="font-medium">
{t('matchesMap.distanceValue', { distance: match.DistanceKm.toFixed(1) })}
</div>
</div> </div>
</div> </div>

View File

@ -39,7 +39,7 @@ const SymbiosisLine = React.memo<{
<Popup> <Popup>
<div> <div>
<p className="text-sm"> <p className="text-sm">
Connection to <strong>{match.org?.Name || 'Unknown'}</strong> {t('symbiosis.connectionTo', { name: match.org?.Name || 'Unknown' })}
</p> </p>
</div> </div>
</Popup> </Popup>

View File

@ -49,7 +49,7 @@ const MatchCard: React.FC<MatchCardProps> = ({ match, onViewDetails }) => {
<Flex align="center" gap="xs" className="mb-2"> <Flex align="center" gap="xs" className="mb-2">
<Badge variant={getStatusColor()}>{match.Status}</Badge> <Badge variant={getStatusColor()}>{match.Status}</Badge>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
Priority: {match.Priority} {t('matches.priority', { priority: match.Priority })}
</Badge> </Badge>
</Flex> </Flex>
@ -69,17 +69,18 @@ const MatchCard: React.FC<MatchCardProps> = ({ match, onViewDetails }) => {
<Flex align="center" gap="md" className="text-sm text-muted-foreground"> <Flex align="center" gap="md" className="text-sm text-muted-foreground">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<MapPin className="h-4 w-4" /> <MapPin className="h-4 w-4" />
<span>{match.DistanceKm.toFixed(1)} km</span> <span>{t('matches.distance', { distance: match.DistanceKm.toFixed(1) })}</span>
</div> </div>
{match.RiskAssessment && ( {match.RiskAssessment && (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<span> <span>
Risk:{' '} {t('matches.riskScore', {
{formatScore( score: formatScore(
(match.RiskAssessment.technical_risk + match.RiskAssessment.regulatory_risk) / (match.RiskAssessment.technical_risk + match.RiskAssessment.regulatory_risk) /
2 2
)} )
})}
</span> </span>
</div> </div>
)} )}

View File

@ -16,10 +16,11 @@ const HistoricalContextCard = ({ landmark, onNavigate }: HistoricalContextCardPr
<div className="h-12 w-12 mx-auto rounded-full flex items-center justify-center bg-warning/20 text-warning-foreground mb-3"> <div className="h-12 w-12 mx-auto rounded-full flex items-center justify-center bg-warning/20 text-warning-foreground mb-3">
<History className="h-4 h-6 text-current w-4 w-6" /> <History className="h-4 h-6 text-current w-4 w-6" />
</div> </div>
<h3 className="text-base font-semibold text-warning-foreground">Исторический контекст</h3> <h3 className="text-base font-semibold text-warning-foreground">
{t('heritage.historicalContext')}
</h3>
<p className="text-sm text-warning-foreground/80 mt-1"> <p className="text-sm text-warning-foreground/80 mt-1">
Это здание является историческим объектом:{' '} {t('heritage.buildingDescription', { name: landmark.name, period: landmark.period })}
<span className="font-semibold">{landmark.name}</span>, построенным в {landmark.period}.
</p> </p>
<Button <Button
variant="ghost" variant="ghost"
@ -27,7 +28,7 @@ const HistoricalContextCard = ({ landmark, onNavigate }: HistoricalContextCardPr
className="mt-3 text-warning-foreground hover:text-warning-foreground hover:bg-warning/20" className="mt-3 text-warning-foreground hover:text-warning-foreground hover:bg-warning/20"
onClick={() => onNavigate('map')} onClick={() => onNavigate('map')}
> >
Посмотреть на карте {t('heritage.viewOnMap')}
</Button> </Button>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -214,9 +214,9 @@ export function NetworkGraph({
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Network Graph</CardTitle> <CardTitle>{t('organization.networkGraph')}</CardTitle>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Explore how {organizationName} connects to other organizations, sites, and resources {t('organization.networkGraphDescription', { name: organizationName })}
</p> </p>
<div className="flex gap-2 mt-4"> <div className="flex gap-2 mt-4">
{[1, 2, 3].map((d) => ( {[1, 2, 3].map((d) => (