From d9865c5bfe87aee072918eab89e5bb415e39fc65 Mon Sep 17 00:00:00 2001 From: alireza Date: Tue, 25 Aug 2026 16:15:14 +0330 Subject: [PATCH] feat: show 1 post with auto-slider, touch swipe and dots on mobile view --- .../pages/Home/sections/TechnogenSection.tsx | 387 +++++++++++------- 1 file changed, 241 insertions(+), 146 deletions(-) diff --git a/frontend/src/pages/Home/sections/TechnogenSection.tsx b/frontend/src/pages/Home/sections/TechnogenSection.tsx index e37cfd9..6f8fd3f 100644 --- a/frontend/src/pages/Home/sections/TechnogenSection.tsx +++ b/frontend/src/pages/Home/sections/TechnogenSection.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Download, FileText, ExternalLink, X, Eye, ChevronLeft, ChevronRight } from 'lucide-react' import { useLang } from '@/context/LangContext' @@ -7,7 +7,6 @@ import { FALLBACK_TECHNOGEN_REPORTS, type TechnogenReport } from '@/data/technog const NAVY = '#032340' const GOLD = '#CD9E53' const EASE = [0.22, 1, 0.36, 1] as const -const ITEMS_PER_PAGE = 5 const faNum = (n: number | string) => String(n).replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[+d]) @@ -18,6 +17,9 @@ export default function TechnogenSection() { const [selectedReport, setSelectedReport] = useState(null) const [page, setPage] = useState(0) const [direction, setDirection] = useState(0) + const [isMobile, setIsMobile] = useState(false) + const [isPaused, setIsPaused] = useState(false) + const touchStartX = useRef(null) useEffect(() => { let unmounted = false @@ -37,8 +39,36 @@ export default function TechnogenSection() { } }, []) - const totalPages = Math.max(1, Math.ceil(reports.length / ITEMS_PER_PAGE)) - const currentReports = reports.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) + // Detect mobile screen (< 768px) + useEffect(() => { + const checkMobile = () => { + const mobile = window.innerWidth < 768 + setIsMobile(mobile) + } + checkMobile() + window.addEventListener('resize', checkMobile) + return () => window.removeEventListener('resize', checkMobile) + }, []) + + const itemsPerPage = isMobile ? 1 : 5 + const totalPages = Math.max(1, Math.ceil(reports.length / itemsPerPage)) + + // Reset page if out of bounds on resize + useEffect(() => { + setPage((p) => Math.min(p, totalPages - 1)) + }, [totalPages]) + + // Auto-slide on mobile every 4.5 seconds + useEffect(() => { + if (!isMobile || isPaused || selectedReport || totalPages <= 1) return + const interval = setInterval(() => { + setDirection(1) + setPage((p) => (p + 1) % totalPages) + }, 4500) + return () => clearInterval(interval) + }, [isMobile, isPaused, selectedReport, totalPages]) + + const currentReports = reports.slice(page * itemsPerPage, page * itemsPerPage + itemsPerPage) const goNext = () => { setDirection(1) @@ -50,6 +80,27 @@ export default function TechnogenSection() { setPage((p) => (p - 1 + totalPages) % totalPages) } + const handleTouchStart = (e: React.TouchEvent) => { + touchStartX.current = e.touches[0].clientX + setIsPaused(true) + } + + const handleTouchEnd = (e: React.TouchEvent) => { + setIsPaused(false) + if (touchStartX.current === null) return + const diff = touchStartX.current - e.changedTouches[0].clientX + if (Math.abs(diff) > 40) { + if (isFa) { + if (diff > 0) goNext() + else goPrev() + } else { + if (diff > 0) goPrev() + else goNext() + } + } + touchStartX.current = null + } + const openPreview = (report: TechnogenReport) => { setSelectedReport(report) } @@ -166,7 +217,7 @@ export default function TechnogenSection() {

- {/* Slider controls (visible if more than 5 reports) */} + {/* Slider controls (visible on desktop if >5 or always on mobile if >1) */} {totalPages > 1 && (
@@ -216,172 +267,216 @@ export default function TechnogenSection() { )}
- {/* ── 5-Card Row / Slider Strip (Clean Typography, Breathing Border Glow) ── */} - - - {currentReports.map((report, idx) => { - const isSpecial = !!report.isFeatured - return ( -
- {/* Top Cover Image */} + {/* ── Cards Row / Mobile Single Card View ── */} +
setIsPaused(true)} + onMouseLeave={() => setIsPaused(false)} + > + + + {currentReports.map((report, idx) => { + const isSpecial = !!report.isFeatured + return (
openPreview(report)} + key={report.id} + className="technogen-card-glow" style={{ - width: '100%', - aspectRatio: '1 / 1.15', - overflow: 'hidden', - background: NAVY, position: 'relative', - cursor: 'pointer', - }} - > - {isFa -
- - {/* Card Content Body */} -
-

openPreview(report)} style={{ - fontSize: 15, - fontWeight: 900, - color: '#FFFFFF', - lineHeight: 1.5, - margin: 0, + width: '100%', + aspectRatio: '1 / 1.15', + overflow: 'hidden', + background: NAVY, + position: 'relative', cursor: 'pointer', - minHeight: 48, - display: 'flex', - alignItems: 'center', }} - title={isFa ? report.titleFa : (report.titleEn || report.titleFa)} > - {isFa ? report.titleFa : (report.titleEn || report.titleFa)} -

+ {isFa +
- {/* Bottom Action Buttons */} + {/* Card Content Body */}
- {report.fileUrl && ( - openPreview(report)} + style={{ + fontSize: 15, + fontWeight: 900, + color: '#FFFFFF', + lineHeight: 1.5, + margin: 0, + cursor: 'pointer', + minHeight: 48, + display: 'flex', + alignItems: 'center', + }} + title={isFa ? report.titleFa : (report.titleEn || report.titleFa)} + > + {isFa ? report.titleFa : (report.titleEn || report.titleFa)} + + + {/* Bottom Action Buttons */} +
+ {report.fileUrl && ( + + + {isFa ? 'دانلود PDF' : 'Download'} + + )} + + + + {isFa ? 'مشاهده' : 'View'} + +
-
- {/* Bottom Gold Accent Bar */} -
-
- ) - })} - - + {/* Bottom Gold Accent Bar */} +
+
+ ) + })} + + + + {/* Mobile Dot Indicators */} + {isMobile && reports.length > 1 && ( +
+ {reports.map((_, idx) => ( +
+ )} +
{/* ── Document Reader / Preview Modal ── */}