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.
176 lines
6.0 KiB
TypeScript
176 lines
6.0 KiB
TypeScript
import { AnimatePresence, motion } from 'framer-motion';
|
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
import { symbiosisExamples } from '@/data/symbiosisExamples.ts';
|
|
import { useTranslation } from '@/hooks/useI18n.tsx';
|
|
import SectionHeader from '@/components/layout/SectionHeader.tsx';
|
|
import Button from '@/components/ui/Button.tsx';
|
|
import { Container, Flex, Grid } from '@/components/ui/layout';
|
|
import Select from '@/components/ui/Select.tsx';
|
|
import DemoCard from '@/components/landing/DemoCard.tsx';
|
|
|
|
interface SymbiosisDemoProps {
|
|
onNavigateToMap: (page: 'map') => void;
|
|
}
|
|
|
|
const SymbiosisDemo = ({ onNavigateToMap }: SymbiosisDemoProps) => {
|
|
const { t } = useTranslation();
|
|
|
|
// State now holds only the key of the selected offer.
|
|
const [selectedOfferKey, setSelectedOfferKey] = useState(symbiosisExamples[0].offer.key);
|
|
|
|
// Derive the selected offer object from the key using useMemo for performance.
|
|
const selectedOffer = useMemo(
|
|
() => symbiosisExamples.find((ex) => ex.offer.key === selectedOfferKey) || null,
|
|
[selectedOfferKey]
|
|
);
|
|
|
|
// Derive the available needs based on the selected offer.
|
|
const availableNeeds = useMemo(() => selectedOffer?.needs || [], [selectedOffer]);
|
|
|
|
// Initialize selected need key based on available needs
|
|
const initialSelectedNeedKey = availableNeeds[0]?.key || '';
|
|
const [selectedNeedKey, setSelectedNeedKey] = useState(initialSelectedNeedKey);
|
|
|
|
// Derive the selected need object from its key.
|
|
const selectedNeed = useMemo(
|
|
() => availableNeeds.find((n) => n.key === selectedNeedKey) || null,
|
|
[availableNeeds, selectedNeedKey]
|
|
);
|
|
|
|
// Event handlers now only update the keys.
|
|
const handleOfferChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
setSelectedOfferKey(e.target.value);
|
|
}, []);
|
|
|
|
const handleNeedChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
setSelectedNeedKey(e.target.value);
|
|
}, []);
|
|
|
|
const translatedSectors = useMemo(
|
|
() => ({
|
|
production: t('sectors.production'),
|
|
construction: t('sectors.construction'),
|
|
recreation: t('sectors.recreation'),
|
|
logistics: t('sectors.logistics'),
|
|
}),
|
|
[t]
|
|
);
|
|
|
|
return (
|
|
<section
|
|
className="bg-background min-h-screen flex items-center scroll-snap-align-start"
|
|
aria-labelledby="symbiosis-demo-title"
|
|
>
|
|
<Container className="py-16 sm:py-24 w-full">
|
|
<SectionHeader
|
|
id="symbiosis-demo-title"
|
|
title={t('symbiosisDemo.title')}
|
|
subtitle={t('symbiosisDemo.subtitle')}
|
|
className="mb-10 sm:mb-12"
|
|
/>
|
|
<Grid cols={{ md: 2 }} gap="md" className="mb-12">
|
|
<div>
|
|
<label htmlFor="offer-select" className="block text-sm font-medium mb-1">
|
|
{t('symbiosisDemo.offerLabel')}
|
|
</label>
|
|
<Select id="offer-select" value={selectedOfferKey} onChange={handleOfferChange}>
|
|
{symbiosisExamples.map((ex) => (
|
|
<option key={ex.offer.key} value={ex.offer.key}>
|
|
{t(ex.offer.nameKey)}
|
|
</option>
|
|
))}
|
|
</Select>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="need-select" className="block text-sm font-medium mb-1">
|
|
{t('symbiosisDemo.needLabel')}
|
|
</label>
|
|
<Select
|
|
id="need-select"
|
|
value={selectedNeedKey}
|
|
onChange={handleNeedChange}
|
|
disabled={!selectedOffer}
|
|
>
|
|
{availableNeeds.map((need) => (
|
|
<option key={need.key} value={need.key}>
|
|
{t(need.nameKey)}
|
|
</option>
|
|
))}
|
|
</Select>
|
|
</div>
|
|
</Grid>
|
|
|
|
<Flex
|
|
direction="col"
|
|
align="center"
|
|
justify="between"
|
|
gap="2xl"
|
|
className="relative mt-16 sm:flex-row sm:gap-0"
|
|
>
|
|
{selectedOffer && (
|
|
<DemoCard
|
|
icon={selectedOffer.offer.icon}
|
|
sector={translatedSectors[selectedOffer.offer.sector]}
|
|
item={t(selectedOffer.offer.nameKey)}
|
|
type="Offer"
|
|
/>
|
|
)}
|
|
|
|
<div className="w-full sm:w-2/12 h-16 sm:h-auto flex items-center justify-center">
|
|
<svg className="w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
|
|
<motion.path
|
|
key={`${selectedOffer?.offer.key}-${selectedNeed?.key}`}
|
|
d="M 5 50 Q 50 20 95 50"
|
|
stroke="hsl(var(--primary))"
|
|
strokeWidth="2"
|
|
fill="none"
|
|
strokeDasharray="4 4"
|
|
initial={{ pathLength: 0, opacity: 0 }}
|
|
animate={{ pathLength: 1, opacity: 1 }}
|
|
transition={{ duration: 0.8, ease: 'easeInOut' }}
|
|
/>
|
|
</svg>
|
|
</div>
|
|
|
|
{selectedNeed && (
|
|
<DemoCard
|
|
icon={selectedNeed.icon}
|
|
sector={translatedSectors[selectedNeed.sector]}
|
|
item={t(selectedNeed.nameKey)}
|
|
type="Need"
|
|
/>
|
|
)}
|
|
</Flex>
|
|
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={selectedNeed?.key}
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -10 }}
|
|
transition={{ duration: 0.3 }}
|
|
className="mt-12 text-center max-w-2xl mx-auto p-4 bg-muted rounded-lg"
|
|
aria-live="polite"
|
|
>
|
|
<p className="text-lg text-foreground">
|
|
{selectedNeed ? t(selectedNeed.descriptionKey) : ' '}
|
|
</p>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
|
|
<div className="text-center mt-16">
|
|
<Button
|
|
size="lg"
|
|
onClick={() => onNavigateToMap('map')}
|
|
className="shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30"
|
|
>
|
|
{t('symbiosisDemo.ctaButton')}
|
|
</Button>
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default React.memo(SymbiosisDemo);
|