From 6a3b0db2ec3138ab592cc205c162fc6e3a303cef Mon Sep 17 00:00:00 2001 From: alireza Date: Sat, 27 Jun 2026 16:00:39 +0330 Subject: [PATCH] Remove unused sections: ReportsCarouselSection, SpecialSpreadSection, SustainabilitySection, and TeamSection --- .dockerignore => frontend/.dockerignore | 0 Dockerfile => frontend/Dockerfile | 0 eslint.config.js => frontend/eslint.config.js | 0 index.html => frontend/index.html | 0 liara.json => frontend/liara.json | 0 nginx.conf => frontend/nginx.conf | 0 .../package-lock.json | 0 package.json => frontend/package.json | 0 {public => frontend/public}/1.jpg | 0 {public => frontend/public}/10.jpg | 0 {public => frontend/public}/11.jpg | 0 {public => frontend/public}/12.jpg | 0 {public => frontend/public}/13.jpg | 0 {public => frontend/public}/2.jpg | 0 {public => frontend/public}/3.jpg | 0 {public => frontend/public}/4.jpg | 0 {public => frontend/public}/5.jpg | 0 {public => frontend/public}/6.jpg | 0 {public => frontend/public}/7.jpg | 0 {public => frontend/public}/8.jpg | 0 {public => frontend/public}/9.jpg | 0 .../public}/Frame 2095585206 (2).png | Bin .../public}/Horizontal_Book_Mockup_6 1.webp | Bin .../public}/about/analyst-a.png | Bin .../public}/about/analyst-b.png | Bin .../public}/about/analyst-c.png | Bin .../public}/about/analyst-d.png | Bin .../public}/about/board-zarandi.png | Bin .../public}/about/council-a.png | Bin .../public}/about/council-b.png | Bin .../public}/about/council-c.png | Bin .../public}/about/council-d.png | Bin {public => frontend/public}/about/team-1.png | Bin {public => frontend/public}/about/team-2.png | Bin {public => frontend/public}/about/team-3.png | Bin {public => frontend/public}/about/team-4.png | Bin .../public}/about/vision-1.png | Bin .../public}/about/vision-2.png | Bin .../public}/about/vision-3.png | Bin .../public}/about/vision-4.png | Bin .../public}/android-chrome-192x192.png | Bin .../public}/android-chrome-512x512.png | Bin .../public}/apple-touch-icon.png | Bin .../specialized-meetings/book_mockup.png | Bin .../specialized-meetings/past_event_1.png | Bin .../specialized-meetings/past_event_2.png | Bin .../specialized-meetings/past_event_3.png | Bin .../specialized-meetings/upcoming_speaker.png | Bin .../assets/specialized-meetings/vip_bg.png | Bin .../public}/avatar-placeholder.svg | 0 .../public}/competitor-news.json | 0 .../public}/factory/cover-1.png | Bin .../public}/factory/cover-2.png | Bin .../public}/factory/cover-3.png | Bin .../public}/factory/cover-4.png | Bin .../public}/factory/cover-5.png | Bin {public => frontend/public}/favicon-16x16.png | Bin {public => frontend/public}/favicon-32x32.png | Bin {public => frontend/public}/favicon.ico | Bin {public => frontend/public}/favicon.svg | 0 {public => frontend/public}/flag-iran.png | Bin {public => frontend/public}/flag-usa.png | Bin .../public}/fonts/Vazir-Black.woff2 | Bin .../public}/fonts/Vazir-Bold.woff2 | Bin .../public}/fonts/Vazir-Light.woff2 | Bin .../public}/fonts/Vazir-Medium.woff2 | Bin .../public}/fonts/Vazir-Regular.woff2 | Bin .../public}/globe-countries.geojson | 0 {public => frontend/public}/hero 1.webp | Bin {public => frontend/public}/hero-bg.mp4 | 0 {public => frontend/public}/hero2.webp | Bin {public => frontend/public}/hero3.webp | Bin {public => frontend/public}/icons.svg | 0 {public => frontend/public}/icons/Design.svg | 0 {public => frontend/public}/icons/Group 1.svg | 0 {public => frontend/public}/icons/Line 5.svg | 0 {public => frontend/public}/icons/Line 6.svg | 0 {public => frontend/public}/icons/Line 7.svg | 0 {public => frontend/public}/icons/Line 8.svg | 0 {public => frontend/public}/icons/Line 9.svg | 0 .../public}/icons/arrow-left-01-sharp.svg | 0 .../public}/icons/chevron/down-1.svg | 0 .../public}/icons/chevron/down.svg | 0 .../public}/icons/header/Group 1-1.svg | 0 .../public}/icons/header/Group.svg | 0 .../public}/icons/header/Vector.svg | 0 .../icons/header/vuesax/outline/sms.svg | 0 .../icons/vuesax/linear/arrow-down.svg | 0 .../icons/vuesax/linear/calendar-tick.svg | 0 .../public}/icons/vuesax/linear/calendar.svg | 0 .../public}/icons/vuesax/linear/export.svg | 0 .../public}/icons/vuesax/linear/location.svg | 0 .../icons/vuesax/outline/buildings.svg | 0 .../icons/vuesax/outline/search-normal.svg | 0 .../icons/vuesax/outline/security-safe.svg | 0 {public => frontend/public}/logo white.png | Bin {public => frontend/public}/logo.png | 0 .../news/photo-1451187580459-43490279c0fa.jpg | 0 .../news/photo-1466611653911-95081537e5b7.jpg | 0 .../news/photo-1473341304170-971dccb5ac1e.jpg | 0 .../news/photo-1485827404703-89b55fcc595e.jpg | 0 .../news/photo-1494412519320-aa613dfb7738.jpg | 0 .../news/photo-1504307651254-35680f356dfd.jpg | 0 .../news/photo-1504328345606-18bbc8c9d7d1.jpg | 0 .../news/photo-1518770660439-4636190af475.jpg | 0 .../news/photo-1540575467063-178a50c2df87.jpg | 0 .../news/photo-1551288049-bebda4e38f71.jpg | 0 .../news/photo-1558618666-fcd25c85cd64.jpg | 0 .../news/photo-1566873535350-6586b0b7a1f2.jpg | 0 .../news/photo-1567789884554-0b844b597180.jpg | 0 .../news/photo-1570168007204-dfb528c6958f.jpg | 0 .../news/photo-1578662996442-48f60103fc96.jpg | 0 .../news/photo-1581092160607-ee22731c9b4e.jpg | 0 .../news/photo-1587293852726-70cdb56c2866.jpg | 0 .../news/photo-1611273426858-450d8e3c9fce.jpg | 0 .../news/photo-1611974789855-9c2a0a7236a3.jpg | 0 .../news/photo-1613665813446-82a78c468a1d.jpg | 0 {public => frontend/public}/site.webmanifest | 0 {public => frontend/public}/steel-plants.json | 0 {public => frontend/public}/summit-banner.jpg | Bin {public => frontend/public}/sw.js | 0 .../public}/textures/earth-dark.jpg | Bin .../public}/textures/earth-topology.png | Bin {public => frontend/public}/trust-enamad.png | Bin .../public}/trust-etehadie.png | Bin .../public}/trust-samandehi.png | Bin {public => frontend/public}/world-map.webp | Bin .../public}/اندیشکده فولاد آینده_icon.zip | Bin .../scripts}/fetch-competitor-news.mjs | 0 {src => frontend/src}/app/layout/Footer.tsx | 0 {src => frontend/src}/app/layout/Header.tsx | 0 .../src}/app/layout/RootLayout.tsx | 0 {src => frontend/src}/app/router.tsx | 0 {src => frontend/src}/assets/react.svg | 0 {src => frontend/src}/assets/vite.svg | 0 .../src}/components/ui/DetailSheet.tsx | 0 .../src}/components/ui/StrategicEvents.tsx | 0 .../components/ui/animated-radial-chart.tsx | 0 .../src}/components/ui/border-beam.tsx | 0 .../components/ui/financial-score-cards.tsx | 0 {src => frontend/src}/components/ui/globe.tsx | 0 .../components/ui/liquid-glass-button.tsx | 0 .../src}/components/ui/liquid-glass-card.tsx | 0 .../src}/components/ui/magic-text.tsx | 0 .../src}/components/ui/progress.tsx | 0 .../src}/content/factoryReports.ts | 0 {src => frontend/src}/content/integrations.ts | 0 {src => frontend/src}/content/posts.ts | 0 {src => frontend/src}/content/radar.ts | 0 .../src}/content/radarCategories.ts | 0 {src => frontend/src}/content/radarFuture.ts | 0 {src => frontend/src}/context/AuthContext.tsx | 19 +- {src => frontend/src}/context/LangContext.tsx | 0 {src => frontend/src}/data/about.ts | 0 {src => frontend/src}/data/banners.ts | 0 {src => frontend/src}/data/events.ts | 0 {src => frontend/src}/data/market.ts | 0 {src => frontend/src}/data/media.ts | 0 {src => frontend/src}/data/plans.ts | 0 {src => frontend/src}/data/reports.ts | 0 {src => frontend/src}/data/risks.ts | 0 {src => frontend/src}/data/team.ts | 0 {src => frontend/src}/index.css | 0 {src => frontend/src}/lib/utils.ts | 0 {src => frontend/src}/main.tsx | 0 {src => frontend/src}/pages/About/About.tsx | 0 .../src}/pages/Archive/Archive.tsx | 0 .../src}/pages/AtAGlance/AtAGlance.tsx | 0 {src => frontend/src}/pages/Auth/AuthPage.tsx | 0 .../src}/pages/Contact/Contact.tsx | 0 {src => frontend/src}/pages/Events/Events.tsx | 0 .../src}/pages/Experts/Experts.tsx | 0 .../src}/pages/Geopolitics/Geopolitics.tsx | 0 {src => frontend/src}/pages/Home/Home.tsx | 0 .../pages/Home/sections/AtAGlanceSection.tsx | 0 .../Home/sections/FactoryReportsScroll.tsx | 0 .../Home/sections/FeaturedReportBanner.tsx | 0 .../Home/sections/IntegrationsSection.tsx | 0 .../Home/sections/InternalNewsSection.tsx | 0 .../pages/Home/sections/MapEventsSection.tsx | 0 .../pages/Home/sections/RadarTechSection.tsx | 0 .../src}/pages/Home/sections/RiskSection.tsx | 0 .../src}/pages/Home/sections/SnapshotBar.tsx | 0 .../pages/Home/sections/SpreadFeatures.tsx | 0 .../Home/sections/VideocastPodcastSection.tsx | 0 {src => frontend/src}/pages/Market/Market.tsx | 0 .../src}/pages/Membership/Membership.tsx | 0 .../src}/pages/Podcast/Podcast.tsx | 0 .../src}/pages/PostDetail/PostDetail.tsx | 0 .../src}/pages/Profile/ProfilePage.tsx | 0 {src => frontend/src}/pages/Pulse/Pulse.tsx | 0 {src => frontend/src}/pages/Radar/Radar.tsx | 0 .../pages/RadarCategory/RadarCategory.tsx | 0 .../src}/pages/RadarPost/RadarPost.tsx | 0 .../src}/pages/ReportDetail/ReportDetail.tsx | 0 {src => frontend/src}/pages/Risks/Risks.tsx | 0 .../src}/pages/Scanner/Scanner.tsx | 0 .../src}/pages/Search/SearchResults.tsx | 0 .../src}/pages/Special/Special.tsx | 0 .../SpecializedMeetings.tsx | 0 .../pages/SteelDashboard/SteelDashboard.tsx | 0 .../SteelDashboard/data/greenSteelProjects.ts | 0 .../sections/GreenSteelTracker.tsx | 0 .../sections/IndustrySection.tsx | 0 .../SteelDashboard/sections/SelfSection.tsx | 0 .../sections/SteelPlantsGlobe.tsx | 0 .../SteelDashboard/sections/WorldSection.tsx | 0 .../src}/pages/Subscribe/Subscribe.tsx | 0 .../pages/Sustainability/Sustainability.tsx | 0 {src => frontend/src}/pages/Team/Team.tsx | 0 .../src}/pages/Technology/Technology.tsx | 0 .../tsconfig.app.json | 0 tsconfig.json => frontend/tsconfig.json | 0 .../tsconfig.node.json | 0 vite.config.ts => frontend/vite.config.ts | 0 src/App.tsx | 4 - src/app/layout/MarketStrip.tsx | 194 ------- src/components/ui/Badge.tsx | 56 -- src/components/ui/Button.tsx | 111 ---- src/components/ui/EventCalendar.tsx | 156 ------ src/components/ui/PullQuote.tsx | 44 -- src/components/ui/Rule.tsx | 44 -- src/components/ui/SectionBanner.tsx | 91 ---- src/components/ui/Stamp.tsx | 37 -- src/components/ui/StatCard.tsx | 40 -- src/components/ui/animated-dashboard-card.tsx | 177 ------- src/components/ui/apple-cards-carousel.tsx | 495 ------------------ src/components/ui/bento-grid.tsx | 135 ----- src/components/ui/dock-beam.tsx | 37 -- src/components/ui/index.ts | 7 - src/components/ui/infinite-moving-cards.tsx | 156 ------ src/components/ui/mac-os-dock.tsx | 223 -------- src/components/ui/marquee.tsx | 52 -- src/components/ui/world-map.tsx | 193 ------- src/lib/api.ts | 19 - src/pages/Auth/LoginPage.tsx | 112 ---- src/pages/Auth/RegisterPage.tsx | 239 --------- src/pages/Home/sections/EventsSection.tsx | 146 ------ .../Home/sections/FactoryBentoSection.tsx | 179 ------- .../Home/sections/GlobalScannerSection.tsx | 174 ------ src/pages/Home/sections/HeroSection.tsx | 222 -------- .../Home/sections/LatestNewsBentoSection.tsx | 284 ---------- .../Home/sections/LatestReportsSection.tsx | 252 --------- src/pages/Home/sections/NewsletterMarquee.tsx | 113 ---- src/pages/Home/sections/NewsletterSection.tsx | 103 ---- src/pages/Home/sections/PartnersSection.tsx | 250 --------- src/pages/Home/sections/PlansSection.tsx | 350 ------------- .../Home/sections/ReportsCarouselSection.tsx | 330 ------------ .../Home/sections/SpecialSpreadSection.tsx | 165 ------ .../Home/sections/SustainabilitySection.tsx | 109 ---- src/pages/Home/sections/TeamSection.tsx | 194 ------- 251 files changed, 18 insertions(+), 5494 deletions(-) rename .dockerignore => frontend/.dockerignore (100%) rename Dockerfile => frontend/Dockerfile (100%) rename eslint.config.js => frontend/eslint.config.js (100%) rename index.html => frontend/index.html (100%) rename liara.json => frontend/liara.json (100%) rename nginx.conf => frontend/nginx.conf (100%) rename package-lock.json => frontend/package-lock.json (100%) rename package.json => frontend/package.json (100%) rename {public => frontend/public}/1.jpg (100%) rename {public => frontend/public}/10.jpg (100%) rename {public => frontend/public}/11.jpg (100%) rename {public => frontend/public}/12.jpg (100%) rename {public => frontend/public}/13.jpg (100%) rename {public => frontend/public}/2.jpg (100%) rename {public => frontend/public}/3.jpg (100%) rename {public => frontend/public}/4.jpg (100%) rename {public => frontend/public}/5.jpg (100%) rename {public => frontend/public}/6.jpg (100%) rename {public => frontend/public}/7.jpg (100%) rename {public => frontend/public}/8.jpg (100%) rename {public => frontend/public}/9.jpg (100%) rename {public => frontend/public}/Frame 2095585206 (2).png (100%) rename {public => frontend/public}/Horizontal_Book_Mockup_6 1.webp (100%) rename {public => frontend/public}/about/analyst-a.png (100%) rename {public => frontend/public}/about/analyst-b.png (100%) rename {public => frontend/public}/about/analyst-c.png (100%) rename {public => frontend/public}/about/analyst-d.png (100%) rename {public => frontend/public}/about/board-zarandi.png (100%) rename {public => frontend/public}/about/council-a.png (100%) rename {public => frontend/public}/about/council-b.png (100%) rename {public => frontend/public}/about/council-c.png (100%) rename {public => frontend/public}/about/council-d.png (100%) rename {public => frontend/public}/about/team-1.png (100%) rename {public => frontend/public}/about/team-2.png (100%) rename {public => frontend/public}/about/team-3.png (100%) rename {public => frontend/public}/about/team-4.png (100%) rename {public => frontend/public}/about/vision-1.png (100%) rename {public => frontend/public}/about/vision-2.png (100%) rename {public => frontend/public}/about/vision-3.png (100%) rename {public => frontend/public}/about/vision-4.png (100%) rename {public => frontend/public}/android-chrome-192x192.png (100%) rename {public => frontend/public}/android-chrome-512x512.png (100%) rename {public => frontend/public}/apple-touch-icon.png (100%) rename {public => frontend/public}/assets/specialized-meetings/book_mockup.png (100%) rename {public => frontend/public}/assets/specialized-meetings/past_event_1.png (100%) rename {public => frontend/public}/assets/specialized-meetings/past_event_2.png (100%) rename {public => frontend/public}/assets/specialized-meetings/past_event_3.png (100%) rename {public => frontend/public}/assets/specialized-meetings/upcoming_speaker.png (100%) rename {public => frontend/public}/assets/specialized-meetings/vip_bg.png (100%) rename {public => frontend/public}/avatar-placeholder.svg (100%) rename {public => frontend/public}/competitor-news.json (100%) rename {public => frontend/public}/factory/cover-1.png (100%) rename {public => frontend/public}/factory/cover-2.png (100%) rename {public => frontend/public}/factory/cover-3.png (100%) rename {public => frontend/public}/factory/cover-4.png (100%) rename {public => frontend/public}/factory/cover-5.png (100%) rename {public => frontend/public}/favicon-16x16.png (100%) rename {public => frontend/public}/favicon-32x32.png (100%) rename {public => frontend/public}/favicon.ico (100%) rename {public => frontend/public}/favicon.svg (100%) rename {public => frontend/public}/flag-iran.png (100%) rename {public => frontend/public}/flag-usa.png (100%) rename {public => frontend/public}/fonts/Vazir-Black.woff2 (100%) rename {public => frontend/public}/fonts/Vazir-Bold.woff2 (100%) rename {public => frontend/public}/fonts/Vazir-Light.woff2 (100%) rename {public => frontend/public}/fonts/Vazir-Medium.woff2 (100%) rename {public => frontend/public}/fonts/Vazir-Regular.woff2 (100%) rename {public => frontend/public}/globe-countries.geojson (100%) rename {public => frontend/public}/hero 1.webp (100%) rename {public => frontend/public}/hero-bg.mp4 (100%) rename {public => frontend/public}/hero2.webp (100%) rename {public => frontend/public}/hero3.webp (100%) rename {public => frontend/public}/icons.svg (100%) rename {public => frontend/public}/icons/Design.svg (100%) rename {public => frontend/public}/icons/Group 1.svg (100%) rename {public => frontend/public}/icons/Line 5.svg (100%) rename {public => frontend/public}/icons/Line 6.svg (100%) rename {public => frontend/public}/icons/Line 7.svg (100%) rename {public => frontend/public}/icons/Line 8.svg (100%) rename {public => frontend/public}/icons/Line 9.svg (100%) rename {public => frontend/public}/icons/arrow-left-01-sharp.svg (100%) rename {public => frontend/public}/icons/chevron/down-1.svg (100%) rename {public => frontend/public}/icons/chevron/down.svg (100%) rename {public => frontend/public}/icons/header/Group 1-1.svg (100%) rename {public => frontend/public}/icons/header/Group.svg (100%) rename {public => frontend/public}/icons/header/Vector.svg (100%) rename {public => frontend/public}/icons/header/vuesax/outline/sms.svg (100%) rename {public => frontend/public}/icons/vuesax/linear/arrow-down.svg (100%) rename {public => frontend/public}/icons/vuesax/linear/calendar-tick.svg (100%) rename {public => frontend/public}/icons/vuesax/linear/calendar.svg (100%) rename {public => frontend/public}/icons/vuesax/linear/export.svg (100%) rename {public => frontend/public}/icons/vuesax/linear/location.svg (100%) rename {public => frontend/public}/icons/vuesax/outline/buildings.svg (100%) rename {public => frontend/public}/icons/vuesax/outline/search-normal.svg (100%) rename {public => frontend/public}/icons/vuesax/outline/security-safe.svg (100%) rename {public => frontend/public}/logo white.png (100%) rename {public => frontend/public}/logo.png (100%) rename {public => frontend/public}/news/photo-1451187580459-43490279c0fa.jpg (100%) rename {public => frontend/public}/news/photo-1466611653911-95081537e5b7.jpg (100%) rename {public => frontend/public}/news/photo-1473341304170-971dccb5ac1e.jpg (100%) rename {public => frontend/public}/news/photo-1485827404703-89b55fcc595e.jpg (100%) rename {public => frontend/public}/news/photo-1494412519320-aa613dfb7738.jpg (100%) rename {public => frontend/public}/news/photo-1504307651254-35680f356dfd.jpg (100%) rename {public => frontend/public}/news/photo-1504328345606-18bbc8c9d7d1.jpg (100%) rename {public => frontend/public}/news/photo-1518770660439-4636190af475.jpg (100%) rename {public => frontend/public}/news/photo-1540575467063-178a50c2df87.jpg (100%) rename {public => frontend/public}/news/photo-1551288049-bebda4e38f71.jpg (100%) rename {public => frontend/public}/news/photo-1558618666-fcd25c85cd64.jpg (100%) rename {public => frontend/public}/news/photo-1566873535350-6586b0b7a1f2.jpg (100%) rename {public => frontend/public}/news/photo-1567789884554-0b844b597180.jpg (100%) rename {public => frontend/public}/news/photo-1570168007204-dfb528c6958f.jpg (100%) rename {public => frontend/public}/news/photo-1578662996442-48f60103fc96.jpg (100%) rename {public => frontend/public}/news/photo-1581092160607-ee22731c9b4e.jpg (100%) rename {public => frontend/public}/news/photo-1587293852726-70cdb56c2866.jpg (100%) rename {public => frontend/public}/news/photo-1611273426858-450d8e3c9fce.jpg (100%) rename {public => frontend/public}/news/photo-1611974789855-9c2a0a7236a3.jpg (100%) rename {public => frontend/public}/news/photo-1613665813446-82a78c468a1d.jpg (100%) rename {public => frontend/public}/site.webmanifest (100%) rename {public => frontend/public}/steel-plants.json (100%) rename {public => frontend/public}/summit-banner.jpg (100%) rename {public => frontend/public}/sw.js (100%) rename {public => frontend/public}/textures/earth-dark.jpg (100%) rename {public => frontend/public}/textures/earth-topology.png (100%) rename {public => frontend/public}/trust-enamad.png (100%) rename {public => frontend/public}/trust-etehadie.png (100%) rename {public => frontend/public}/trust-samandehi.png (100%) rename {public => frontend/public}/world-map.webp (100%) rename {public => frontend/public}/اندیشکده فولاد آینده_icon.zip (100%) rename {scripts => frontend/scripts}/fetch-competitor-news.mjs (100%) rename {src => frontend/src}/app/layout/Footer.tsx (100%) rename {src => frontend/src}/app/layout/Header.tsx (100%) rename {src => frontend/src}/app/layout/RootLayout.tsx (100%) rename {src => frontend/src}/app/router.tsx (100%) rename {src => frontend/src}/assets/react.svg (100%) rename {src => frontend/src}/assets/vite.svg (100%) rename {src => frontend/src}/components/ui/DetailSheet.tsx (100%) rename {src => frontend/src}/components/ui/StrategicEvents.tsx (100%) rename {src => frontend/src}/components/ui/animated-radial-chart.tsx (100%) rename {src => frontend/src}/components/ui/border-beam.tsx (100%) rename {src => frontend/src}/components/ui/financial-score-cards.tsx (100%) rename {src => frontend/src}/components/ui/globe.tsx (100%) rename {src => frontend/src}/components/ui/liquid-glass-button.tsx (100%) rename {src => frontend/src}/components/ui/liquid-glass-card.tsx (100%) rename {src => frontend/src}/components/ui/magic-text.tsx (100%) rename {src => frontend/src}/components/ui/progress.tsx (100%) rename {src => frontend/src}/content/factoryReports.ts (100%) rename {src => frontend/src}/content/integrations.ts (100%) rename {src => frontend/src}/content/posts.ts (100%) rename {src => frontend/src}/content/radar.ts (100%) rename {src => frontend/src}/content/radarCategories.ts (100%) rename {src => frontend/src}/content/radarFuture.ts (100%) rename {src => frontend/src}/context/AuthContext.tsx (88%) rename {src => frontend/src}/context/LangContext.tsx (100%) rename {src => frontend/src}/data/about.ts (100%) rename {src => frontend/src}/data/banners.ts (100%) rename {src => frontend/src}/data/events.ts (100%) rename {src => frontend/src}/data/market.ts (100%) rename {src => frontend/src}/data/media.ts (100%) rename {src => frontend/src}/data/plans.ts (100%) rename {src => frontend/src}/data/reports.ts (100%) rename {src => frontend/src}/data/risks.ts (100%) rename {src => frontend/src}/data/team.ts (100%) rename {src => frontend/src}/index.css (100%) rename {src => frontend/src}/lib/utils.ts (100%) rename {src => frontend/src}/main.tsx (100%) rename {src => frontend/src}/pages/About/About.tsx (100%) rename {src => frontend/src}/pages/Archive/Archive.tsx (100%) rename {src => frontend/src}/pages/AtAGlance/AtAGlance.tsx (100%) rename {src => frontend/src}/pages/Auth/AuthPage.tsx (100%) rename {src => frontend/src}/pages/Contact/Contact.tsx (100%) rename {src => frontend/src}/pages/Events/Events.tsx (100%) rename {src => frontend/src}/pages/Experts/Experts.tsx (100%) rename {src => frontend/src}/pages/Geopolitics/Geopolitics.tsx (100%) rename {src => frontend/src}/pages/Home/Home.tsx (100%) rename {src => frontend/src}/pages/Home/sections/AtAGlanceSection.tsx (100%) rename {src => frontend/src}/pages/Home/sections/FactoryReportsScroll.tsx (100%) rename {src => frontend/src}/pages/Home/sections/FeaturedReportBanner.tsx (100%) rename {src => frontend/src}/pages/Home/sections/IntegrationsSection.tsx (100%) rename {src => frontend/src}/pages/Home/sections/InternalNewsSection.tsx (100%) rename {src => frontend/src}/pages/Home/sections/MapEventsSection.tsx (100%) rename {src => frontend/src}/pages/Home/sections/RadarTechSection.tsx (100%) rename {src => frontend/src}/pages/Home/sections/RiskSection.tsx (100%) rename {src => frontend/src}/pages/Home/sections/SnapshotBar.tsx (100%) rename {src => frontend/src}/pages/Home/sections/SpreadFeatures.tsx (100%) rename {src => frontend/src}/pages/Home/sections/VideocastPodcastSection.tsx (100%) rename {src => frontend/src}/pages/Market/Market.tsx (100%) rename {src => frontend/src}/pages/Membership/Membership.tsx (100%) rename {src => frontend/src}/pages/Podcast/Podcast.tsx (100%) rename {src => frontend/src}/pages/PostDetail/PostDetail.tsx (100%) rename {src => frontend/src}/pages/Profile/ProfilePage.tsx (100%) rename {src => frontend/src}/pages/Pulse/Pulse.tsx (100%) rename {src => frontend/src}/pages/Radar/Radar.tsx (100%) rename {src => frontend/src}/pages/RadarCategory/RadarCategory.tsx (100%) rename {src => frontend/src}/pages/RadarPost/RadarPost.tsx (100%) rename {src => frontend/src}/pages/ReportDetail/ReportDetail.tsx (100%) rename {src => frontend/src}/pages/Risks/Risks.tsx (100%) rename {src => frontend/src}/pages/Scanner/Scanner.tsx (100%) rename {src => frontend/src}/pages/Search/SearchResults.tsx (100%) rename {src => frontend/src}/pages/Special/Special.tsx (100%) rename {src => frontend/src}/pages/SpecializedMeetings/SpecializedMeetings.tsx (100%) rename {src => frontend/src}/pages/SteelDashboard/SteelDashboard.tsx (100%) rename {src => frontend/src}/pages/SteelDashboard/data/greenSteelProjects.ts (100%) rename {src => frontend/src}/pages/SteelDashboard/sections/GreenSteelTracker.tsx (100%) rename {src => frontend/src}/pages/SteelDashboard/sections/IndustrySection.tsx (100%) rename {src => frontend/src}/pages/SteelDashboard/sections/SelfSection.tsx (100%) rename {src => frontend/src}/pages/SteelDashboard/sections/SteelPlantsGlobe.tsx (100%) rename {src => frontend/src}/pages/SteelDashboard/sections/WorldSection.tsx (100%) rename {src => frontend/src}/pages/Subscribe/Subscribe.tsx (100%) rename {src => frontend/src}/pages/Sustainability/Sustainability.tsx (100%) rename {src => frontend/src}/pages/Team/Team.tsx (100%) rename {src => frontend/src}/pages/Technology/Technology.tsx (100%) rename tsconfig.app.json => frontend/tsconfig.app.json (100%) rename tsconfig.json => frontend/tsconfig.json (100%) rename tsconfig.node.json => frontend/tsconfig.node.json (100%) rename vite.config.ts => frontend/vite.config.ts (100%) delete mode 100644 src/App.tsx delete mode 100644 src/app/layout/MarketStrip.tsx delete mode 100644 src/components/ui/Badge.tsx delete mode 100644 src/components/ui/Button.tsx delete mode 100644 src/components/ui/EventCalendar.tsx delete mode 100644 src/components/ui/PullQuote.tsx delete mode 100644 src/components/ui/Rule.tsx delete mode 100644 src/components/ui/SectionBanner.tsx delete mode 100644 src/components/ui/Stamp.tsx delete mode 100644 src/components/ui/StatCard.tsx delete mode 100644 src/components/ui/animated-dashboard-card.tsx delete mode 100644 src/components/ui/apple-cards-carousel.tsx delete mode 100644 src/components/ui/bento-grid.tsx delete mode 100644 src/components/ui/dock-beam.tsx delete mode 100644 src/components/ui/index.ts delete mode 100644 src/components/ui/infinite-moving-cards.tsx delete mode 100644 src/components/ui/mac-os-dock.tsx delete mode 100644 src/components/ui/marquee.tsx delete mode 100644 src/components/ui/world-map.tsx delete mode 100644 src/lib/api.ts delete mode 100644 src/pages/Auth/LoginPage.tsx delete mode 100644 src/pages/Auth/RegisterPage.tsx delete mode 100644 src/pages/Home/sections/EventsSection.tsx delete mode 100644 src/pages/Home/sections/FactoryBentoSection.tsx delete mode 100644 src/pages/Home/sections/GlobalScannerSection.tsx delete mode 100644 src/pages/Home/sections/HeroSection.tsx delete mode 100644 src/pages/Home/sections/LatestNewsBentoSection.tsx delete mode 100644 src/pages/Home/sections/LatestReportsSection.tsx delete mode 100644 src/pages/Home/sections/NewsletterMarquee.tsx delete mode 100644 src/pages/Home/sections/NewsletterSection.tsx delete mode 100644 src/pages/Home/sections/PartnersSection.tsx delete mode 100644 src/pages/Home/sections/PlansSection.tsx delete mode 100644 src/pages/Home/sections/ReportsCarouselSection.tsx delete mode 100644 src/pages/Home/sections/SpecialSpreadSection.tsx delete mode 100644 src/pages/Home/sections/SustainabilitySection.tsx delete mode 100644 src/pages/Home/sections/TeamSection.tsx diff --git a/.dockerignore b/frontend/.dockerignore similarity index 100% rename from .dockerignore rename to frontend/.dockerignore diff --git a/Dockerfile b/frontend/Dockerfile similarity index 100% rename from Dockerfile rename to frontend/Dockerfile diff --git a/eslint.config.js b/frontend/eslint.config.js similarity index 100% rename from eslint.config.js rename to frontend/eslint.config.js diff --git a/index.html b/frontend/index.html similarity index 100% rename from index.html rename to frontend/index.html diff --git a/liara.json b/frontend/liara.json similarity index 100% rename from liara.json rename to frontend/liara.json diff --git a/nginx.conf b/frontend/nginx.conf similarity index 100% rename from nginx.conf rename to frontend/nginx.conf diff --git a/package-lock.json b/frontend/package-lock.json similarity index 100% rename from package-lock.json rename to frontend/package-lock.json diff --git a/package.json b/frontend/package.json similarity index 100% rename from package.json rename to frontend/package.json diff --git a/public/1.jpg b/frontend/public/1.jpg similarity index 100% rename from public/1.jpg rename to frontend/public/1.jpg diff --git a/public/10.jpg b/frontend/public/10.jpg similarity index 100% rename from public/10.jpg rename to frontend/public/10.jpg diff --git a/public/11.jpg b/frontend/public/11.jpg similarity index 100% rename from public/11.jpg rename to frontend/public/11.jpg diff --git a/public/12.jpg b/frontend/public/12.jpg similarity index 100% rename from public/12.jpg rename to frontend/public/12.jpg diff --git a/public/13.jpg b/frontend/public/13.jpg similarity index 100% rename from public/13.jpg rename to frontend/public/13.jpg diff --git a/public/2.jpg b/frontend/public/2.jpg similarity index 100% rename from public/2.jpg rename to frontend/public/2.jpg diff --git a/public/3.jpg b/frontend/public/3.jpg similarity index 100% rename from public/3.jpg rename to frontend/public/3.jpg diff --git a/public/4.jpg b/frontend/public/4.jpg similarity index 100% rename from public/4.jpg rename to frontend/public/4.jpg diff --git a/public/5.jpg b/frontend/public/5.jpg similarity index 100% rename from public/5.jpg rename to frontend/public/5.jpg diff --git a/public/6.jpg b/frontend/public/6.jpg similarity index 100% rename from public/6.jpg rename to frontend/public/6.jpg diff --git a/public/7.jpg b/frontend/public/7.jpg similarity index 100% rename from public/7.jpg rename to frontend/public/7.jpg diff --git a/public/8.jpg b/frontend/public/8.jpg similarity index 100% rename from public/8.jpg rename to frontend/public/8.jpg diff --git a/public/9.jpg b/frontend/public/9.jpg similarity index 100% rename from public/9.jpg rename to frontend/public/9.jpg diff --git a/public/Frame 2095585206 (2).png b/frontend/public/Frame 2095585206 (2).png similarity index 100% rename from public/Frame 2095585206 (2).png rename to frontend/public/Frame 2095585206 (2).png diff --git a/public/Horizontal_Book_Mockup_6 1.webp b/frontend/public/Horizontal_Book_Mockup_6 1.webp similarity index 100% rename from public/Horizontal_Book_Mockup_6 1.webp rename to frontend/public/Horizontal_Book_Mockup_6 1.webp diff --git a/public/about/analyst-a.png b/frontend/public/about/analyst-a.png similarity index 100% rename from public/about/analyst-a.png rename to frontend/public/about/analyst-a.png diff --git a/public/about/analyst-b.png b/frontend/public/about/analyst-b.png similarity index 100% rename from public/about/analyst-b.png rename to frontend/public/about/analyst-b.png diff --git a/public/about/analyst-c.png b/frontend/public/about/analyst-c.png similarity index 100% rename from public/about/analyst-c.png rename to frontend/public/about/analyst-c.png diff --git a/public/about/analyst-d.png b/frontend/public/about/analyst-d.png similarity index 100% rename from public/about/analyst-d.png rename to frontend/public/about/analyst-d.png diff --git a/public/about/board-zarandi.png b/frontend/public/about/board-zarandi.png similarity index 100% rename from public/about/board-zarandi.png rename to frontend/public/about/board-zarandi.png diff --git a/public/about/council-a.png b/frontend/public/about/council-a.png similarity index 100% rename from public/about/council-a.png rename to frontend/public/about/council-a.png diff --git a/public/about/council-b.png b/frontend/public/about/council-b.png similarity index 100% rename from public/about/council-b.png rename to frontend/public/about/council-b.png diff --git a/public/about/council-c.png b/frontend/public/about/council-c.png similarity index 100% rename from public/about/council-c.png rename to frontend/public/about/council-c.png diff --git a/public/about/council-d.png b/frontend/public/about/council-d.png similarity index 100% rename from public/about/council-d.png rename to frontend/public/about/council-d.png diff --git a/public/about/team-1.png b/frontend/public/about/team-1.png similarity index 100% rename from public/about/team-1.png rename to frontend/public/about/team-1.png diff --git a/public/about/team-2.png b/frontend/public/about/team-2.png similarity index 100% rename from public/about/team-2.png rename to frontend/public/about/team-2.png diff --git a/public/about/team-3.png b/frontend/public/about/team-3.png similarity index 100% rename from public/about/team-3.png rename to frontend/public/about/team-3.png diff --git a/public/about/team-4.png b/frontend/public/about/team-4.png similarity index 100% rename from public/about/team-4.png rename to frontend/public/about/team-4.png diff --git a/public/about/vision-1.png b/frontend/public/about/vision-1.png similarity index 100% rename from public/about/vision-1.png rename to frontend/public/about/vision-1.png diff --git a/public/about/vision-2.png b/frontend/public/about/vision-2.png similarity index 100% rename from public/about/vision-2.png rename to frontend/public/about/vision-2.png diff --git a/public/about/vision-3.png b/frontend/public/about/vision-3.png similarity index 100% rename from public/about/vision-3.png rename to frontend/public/about/vision-3.png diff --git a/public/about/vision-4.png b/frontend/public/about/vision-4.png similarity index 100% rename from public/about/vision-4.png rename to frontend/public/about/vision-4.png diff --git a/public/android-chrome-192x192.png b/frontend/public/android-chrome-192x192.png similarity index 100% rename from public/android-chrome-192x192.png rename to frontend/public/android-chrome-192x192.png diff --git a/public/android-chrome-512x512.png b/frontend/public/android-chrome-512x512.png similarity index 100% rename from public/android-chrome-512x512.png rename to frontend/public/android-chrome-512x512.png diff --git a/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png similarity index 100% rename from public/apple-touch-icon.png rename to frontend/public/apple-touch-icon.png diff --git a/public/assets/specialized-meetings/book_mockup.png b/frontend/public/assets/specialized-meetings/book_mockup.png similarity index 100% rename from public/assets/specialized-meetings/book_mockup.png rename to frontend/public/assets/specialized-meetings/book_mockup.png diff --git a/public/assets/specialized-meetings/past_event_1.png b/frontend/public/assets/specialized-meetings/past_event_1.png similarity index 100% rename from public/assets/specialized-meetings/past_event_1.png rename to frontend/public/assets/specialized-meetings/past_event_1.png diff --git a/public/assets/specialized-meetings/past_event_2.png b/frontend/public/assets/specialized-meetings/past_event_2.png similarity index 100% rename from public/assets/specialized-meetings/past_event_2.png rename to frontend/public/assets/specialized-meetings/past_event_2.png diff --git a/public/assets/specialized-meetings/past_event_3.png b/frontend/public/assets/specialized-meetings/past_event_3.png similarity index 100% rename from public/assets/specialized-meetings/past_event_3.png rename to frontend/public/assets/specialized-meetings/past_event_3.png diff --git a/public/assets/specialized-meetings/upcoming_speaker.png b/frontend/public/assets/specialized-meetings/upcoming_speaker.png similarity index 100% rename from public/assets/specialized-meetings/upcoming_speaker.png rename to frontend/public/assets/specialized-meetings/upcoming_speaker.png diff --git a/public/assets/specialized-meetings/vip_bg.png b/frontend/public/assets/specialized-meetings/vip_bg.png similarity index 100% rename from public/assets/specialized-meetings/vip_bg.png rename to frontend/public/assets/specialized-meetings/vip_bg.png diff --git a/public/avatar-placeholder.svg b/frontend/public/avatar-placeholder.svg similarity index 100% rename from public/avatar-placeholder.svg rename to frontend/public/avatar-placeholder.svg diff --git a/public/competitor-news.json b/frontend/public/competitor-news.json similarity index 100% rename from public/competitor-news.json rename to frontend/public/competitor-news.json diff --git a/public/factory/cover-1.png b/frontend/public/factory/cover-1.png similarity index 100% rename from public/factory/cover-1.png rename to frontend/public/factory/cover-1.png diff --git a/public/factory/cover-2.png b/frontend/public/factory/cover-2.png similarity index 100% rename from public/factory/cover-2.png rename to frontend/public/factory/cover-2.png diff --git a/public/factory/cover-3.png b/frontend/public/factory/cover-3.png similarity index 100% rename from public/factory/cover-3.png rename to frontend/public/factory/cover-3.png diff --git a/public/factory/cover-4.png b/frontend/public/factory/cover-4.png similarity index 100% rename from public/factory/cover-4.png rename to frontend/public/factory/cover-4.png diff --git a/public/factory/cover-5.png b/frontend/public/factory/cover-5.png similarity index 100% rename from public/factory/cover-5.png rename to frontend/public/factory/cover-5.png diff --git a/public/favicon-16x16.png b/frontend/public/favicon-16x16.png similarity index 100% rename from public/favicon-16x16.png rename to frontend/public/favicon-16x16.png diff --git a/public/favicon-32x32.png b/frontend/public/favicon-32x32.png similarity index 100% rename from public/favicon-32x32.png rename to frontend/public/favicon-32x32.png diff --git a/public/favicon.ico b/frontend/public/favicon.ico similarity index 100% rename from public/favicon.ico rename to frontend/public/favicon.ico diff --git a/public/favicon.svg b/frontend/public/favicon.svg similarity index 100% rename from public/favicon.svg rename to frontend/public/favicon.svg diff --git a/public/flag-iran.png b/frontend/public/flag-iran.png similarity index 100% rename from public/flag-iran.png rename to frontend/public/flag-iran.png diff --git a/public/flag-usa.png b/frontend/public/flag-usa.png similarity index 100% rename from public/flag-usa.png rename to frontend/public/flag-usa.png diff --git a/public/fonts/Vazir-Black.woff2 b/frontend/public/fonts/Vazir-Black.woff2 similarity index 100% rename from public/fonts/Vazir-Black.woff2 rename to frontend/public/fonts/Vazir-Black.woff2 diff --git a/public/fonts/Vazir-Bold.woff2 b/frontend/public/fonts/Vazir-Bold.woff2 similarity index 100% rename from public/fonts/Vazir-Bold.woff2 rename to frontend/public/fonts/Vazir-Bold.woff2 diff --git a/public/fonts/Vazir-Light.woff2 b/frontend/public/fonts/Vazir-Light.woff2 similarity index 100% rename from public/fonts/Vazir-Light.woff2 rename to frontend/public/fonts/Vazir-Light.woff2 diff --git a/public/fonts/Vazir-Medium.woff2 b/frontend/public/fonts/Vazir-Medium.woff2 similarity index 100% rename from public/fonts/Vazir-Medium.woff2 rename to frontend/public/fonts/Vazir-Medium.woff2 diff --git a/public/fonts/Vazir-Regular.woff2 b/frontend/public/fonts/Vazir-Regular.woff2 similarity index 100% rename from public/fonts/Vazir-Regular.woff2 rename to frontend/public/fonts/Vazir-Regular.woff2 diff --git a/public/globe-countries.geojson b/frontend/public/globe-countries.geojson similarity index 100% rename from public/globe-countries.geojson rename to frontend/public/globe-countries.geojson diff --git a/public/hero 1.webp b/frontend/public/hero 1.webp similarity index 100% rename from public/hero 1.webp rename to frontend/public/hero 1.webp diff --git a/public/hero-bg.mp4 b/frontend/public/hero-bg.mp4 similarity index 100% rename from public/hero-bg.mp4 rename to frontend/public/hero-bg.mp4 diff --git a/public/hero2.webp b/frontend/public/hero2.webp similarity index 100% rename from public/hero2.webp rename to frontend/public/hero2.webp diff --git a/public/hero3.webp b/frontend/public/hero3.webp similarity index 100% rename from public/hero3.webp rename to frontend/public/hero3.webp diff --git a/public/icons.svg b/frontend/public/icons.svg similarity index 100% rename from public/icons.svg rename to frontend/public/icons.svg diff --git a/public/icons/Design.svg b/frontend/public/icons/Design.svg similarity index 100% rename from public/icons/Design.svg rename to frontend/public/icons/Design.svg diff --git a/public/icons/Group 1.svg b/frontend/public/icons/Group 1.svg similarity index 100% rename from public/icons/Group 1.svg rename to frontend/public/icons/Group 1.svg diff --git a/public/icons/Line 5.svg b/frontend/public/icons/Line 5.svg similarity index 100% rename from public/icons/Line 5.svg rename to frontend/public/icons/Line 5.svg diff --git a/public/icons/Line 6.svg b/frontend/public/icons/Line 6.svg similarity index 100% rename from public/icons/Line 6.svg rename to frontend/public/icons/Line 6.svg diff --git a/public/icons/Line 7.svg b/frontend/public/icons/Line 7.svg similarity index 100% rename from public/icons/Line 7.svg rename to frontend/public/icons/Line 7.svg diff --git a/public/icons/Line 8.svg b/frontend/public/icons/Line 8.svg similarity index 100% rename from public/icons/Line 8.svg rename to frontend/public/icons/Line 8.svg diff --git a/public/icons/Line 9.svg b/frontend/public/icons/Line 9.svg similarity index 100% rename from public/icons/Line 9.svg rename to frontend/public/icons/Line 9.svg diff --git a/public/icons/arrow-left-01-sharp.svg b/frontend/public/icons/arrow-left-01-sharp.svg similarity index 100% rename from public/icons/arrow-left-01-sharp.svg rename to frontend/public/icons/arrow-left-01-sharp.svg diff --git a/public/icons/chevron/down-1.svg b/frontend/public/icons/chevron/down-1.svg similarity index 100% rename from public/icons/chevron/down-1.svg rename to frontend/public/icons/chevron/down-1.svg diff --git a/public/icons/chevron/down.svg b/frontend/public/icons/chevron/down.svg similarity index 100% rename from public/icons/chevron/down.svg rename to frontend/public/icons/chevron/down.svg diff --git a/public/icons/header/Group 1-1.svg b/frontend/public/icons/header/Group 1-1.svg similarity index 100% rename from public/icons/header/Group 1-1.svg rename to frontend/public/icons/header/Group 1-1.svg diff --git a/public/icons/header/Group.svg b/frontend/public/icons/header/Group.svg similarity index 100% rename from public/icons/header/Group.svg rename to frontend/public/icons/header/Group.svg diff --git a/public/icons/header/Vector.svg b/frontend/public/icons/header/Vector.svg similarity index 100% rename from public/icons/header/Vector.svg rename to frontend/public/icons/header/Vector.svg diff --git a/public/icons/header/vuesax/outline/sms.svg b/frontend/public/icons/header/vuesax/outline/sms.svg similarity index 100% rename from public/icons/header/vuesax/outline/sms.svg rename to frontend/public/icons/header/vuesax/outline/sms.svg diff --git a/public/icons/vuesax/linear/arrow-down.svg b/frontend/public/icons/vuesax/linear/arrow-down.svg similarity index 100% rename from public/icons/vuesax/linear/arrow-down.svg rename to frontend/public/icons/vuesax/linear/arrow-down.svg diff --git a/public/icons/vuesax/linear/calendar-tick.svg b/frontend/public/icons/vuesax/linear/calendar-tick.svg similarity index 100% rename from public/icons/vuesax/linear/calendar-tick.svg rename to frontend/public/icons/vuesax/linear/calendar-tick.svg diff --git a/public/icons/vuesax/linear/calendar.svg b/frontend/public/icons/vuesax/linear/calendar.svg similarity index 100% rename from public/icons/vuesax/linear/calendar.svg rename to frontend/public/icons/vuesax/linear/calendar.svg diff --git a/public/icons/vuesax/linear/export.svg b/frontend/public/icons/vuesax/linear/export.svg similarity index 100% rename from public/icons/vuesax/linear/export.svg rename to frontend/public/icons/vuesax/linear/export.svg diff --git a/public/icons/vuesax/linear/location.svg b/frontend/public/icons/vuesax/linear/location.svg similarity index 100% rename from public/icons/vuesax/linear/location.svg rename to frontend/public/icons/vuesax/linear/location.svg diff --git a/public/icons/vuesax/outline/buildings.svg b/frontend/public/icons/vuesax/outline/buildings.svg similarity index 100% rename from public/icons/vuesax/outline/buildings.svg rename to frontend/public/icons/vuesax/outline/buildings.svg diff --git a/public/icons/vuesax/outline/search-normal.svg b/frontend/public/icons/vuesax/outline/search-normal.svg similarity index 100% rename from public/icons/vuesax/outline/search-normal.svg rename to frontend/public/icons/vuesax/outline/search-normal.svg diff --git a/public/icons/vuesax/outline/security-safe.svg b/frontend/public/icons/vuesax/outline/security-safe.svg similarity index 100% rename from public/icons/vuesax/outline/security-safe.svg rename to frontend/public/icons/vuesax/outline/security-safe.svg diff --git a/public/logo white.png b/frontend/public/logo white.png similarity index 100% rename from public/logo white.png rename to frontend/public/logo white.png diff --git a/public/logo.png b/frontend/public/logo.png similarity index 100% rename from public/logo.png rename to frontend/public/logo.png diff --git a/public/news/photo-1451187580459-43490279c0fa.jpg b/frontend/public/news/photo-1451187580459-43490279c0fa.jpg similarity index 100% rename from public/news/photo-1451187580459-43490279c0fa.jpg rename to frontend/public/news/photo-1451187580459-43490279c0fa.jpg diff --git a/public/news/photo-1466611653911-95081537e5b7.jpg b/frontend/public/news/photo-1466611653911-95081537e5b7.jpg similarity index 100% rename from public/news/photo-1466611653911-95081537e5b7.jpg rename to frontend/public/news/photo-1466611653911-95081537e5b7.jpg diff --git a/public/news/photo-1473341304170-971dccb5ac1e.jpg b/frontend/public/news/photo-1473341304170-971dccb5ac1e.jpg similarity index 100% rename from public/news/photo-1473341304170-971dccb5ac1e.jpg rename to frontend/public/news/photo-1473341304170-971dccb5ac1e.jpg diff --git a/public/news/photo-1485827404703-89b55fcc595e.jpg b/frontend/public/news/photo-1485827404703-89b55fcc595e.jpg similarity index 100% rename from public/news/photo-1485827404703-89b55fcc595e.jpg rename to frontend/public/news/photo-1485827404703-89b55fcc595e.jpg diff --git a/public/news/photo-1494412519320-aa613dfb7738.jpg b/frontend/public/news/photo-1494412519320-aa613dfb7738.jpg similarity index 100% rename from public/news/photo-1494412519320-aa613dfb7738.jpg rename to frontend/public/news/photo-1494412519320-aa613dfb7738.jpg diff --git a/public/news/photo-1504307651254-35680f356dfd.jpg b/frontend/public/news/photo-1504307651254-35680f356dfd.jpg similarity index 100% rename from public/news/photo-1504307651254-35680f356dfd.jpg rename to frontend/public/news/photo-1504307651254-35680f356dfd.jpg diff --git a/public/news/photo-1504328345606-18bbc8c9d7d1.jpg b/frontend/public/news/photo-1504328345606-18bbc8c9d7d1.jpg similarity index 100% rename from public/news/photo-1504328345606-18bbc8c9d7d1.jpg rename to frontend/public/news/photo-1504328345606-18bbc8c9d7d1.jpg diff --git a/public/news/photo-1518770660439-4636190af475.jpg b/frontend/public/news/photo-1518770660439-4636190af475.jpg similarity index 100% rename from public/news/photo-1518770660439-4636190af475.jpg rename to frontend/public/news/photo-1518770660439-4636190af475.jpg diff --git a/public/news/photo-1540575467063-178a50c2df87.jpg b/frontend/public/news/photo-1540575467063-178a50c2df87.jpg similarity index 100% rename from public/news/photo-1540575467063-178a50c2df87.jpg rename to frontend/public/news/photo-1540575467063-178a50c2df87.jpg diff --git a/public/news/photo-1551288049-bebda4e38f71.jpg b/frontend/public/news/photo-1551288049-bebda4e38f71.jpg similarity index 100% rename from public/news/photo-1551288049-bebda4e38f71.jpg rename to frontend/public/news/photo-1551288049-bebda4e38f71.jpg diff --git a/public/news/photo-1558618666-fcd25c85cd64.jpg b/frontend/public/news/photo-1558618666-fcd25c85cd64.jpg similarity index 100% rename from public/news/photo-1558618666-fcd25c85cd64.jpg rename to frontend/public/news/photo-1558618666-fcd25c85cd64.jpg diff --git a/public/news/photo-1566873535350-6586b0b7a1f2.jpg b/frontend/public/news/photo-1566873535350-6586b0b7a1f2.jpg similarity index 100% rename from public/news/photo-1566873535350-6586b0b7a1f2.jpg rename to frontend/public/news/photo-1566873535350-6586b0b7a1f2.jpg diff --git a/public/news/photo-1567789884554-0b844b597180.jpg b/frontend/public/news/photo-1567789884554-0b844b597180.jpg similarity index 100% rename from public/news/photo-1567789884554-0b844b597180.jpg rename to frontend/public/news/photo-1567789884554-0b844b597180.jpg diff --git a/public/news/photo-1570168007204-dfb528c6958f.jpg b/frontend/public/news/photo-1570168007204-dfb528c6958f.jpg similarity index 100% rename from public/news/photo-1570168007204-dfb528c6958f.jpg rename to frontend/public/news/photo-1570168007204-dfb528c6958f.jpg diff --git a/public/news/photo-1578662996442-48f60103fc96.jpg b/frontend/public/news/photo-1578662996442-48f60103fc96.jpg similarity index 100% rename from public/news/photo-1578662996442-48f60103fc96.jpg rename to frontend/public/news/photo-1578662996442-48f60103fc96.jpg diff --git a/public/news/photo-1581092160607-ee22731c9b4e.jpg b/frontend/public/news/photo-1581092160607-ee22731c9b4e.jpg similarity index 100% rename from public/news/photo-1581092160607-ee22731c9b4e.jpg rename to frontend/public/news/photo-1581092160607-ee22731c9b4e.jpg diff --git a/public/news/photo-1587293852726-70cdb56c2866.jpg b/frontend/public/news/photo-1587293852726-70cdb56c2866.jpg similarity index 100% rename from public/news/photo-1587293852726-70cdb56c2866.jpg rename to frontend/public/news/photo-1587293852726-70cdb56c2866.jpg diff --git a/public/news/photo-1611273426858-450d8e3c9fce.jpg b/frontend/public/news/photo-1611273426858-450d8e3c9fce.jpg similarity index 100% rename from public/news/photo-1611273426858-450d8e3c9fce.jpg rename to frontend/public/news/photo-1611273426858-450d8e3c9fce.jpg diff --git a/public/news/photo-1611974789855-9c2a0a7236a3.jpg b/frontend/public/news/photo-1611974789855-9c2a0a7236a3.jpg similarity index 100% rename from public/news/photo-1611974789855-9c2a0a7236a3.jpg rename to frontend/public/news/photo-1611974789855-9c2a0a7236a3.jpg diff --git a/public/news/photo-1613665813446-82a78c468a1d.jpg b/frontend/public/news/photo-1613665813446-82a78c468a1d.jpg similarity index 100% rename from public/news/photo-1613665813446-82a78c468a1d.jpg rename to frontend/public/news/photo-1613665813446-82a78c468a1d.jpg diff --git a/public/site.webmanifest b/frontend/public/site.webmanifest similarity index 100% rename from public/site.webmanifest rename to frontend/public/site.webmanifest diff --git a/public/steel-plants.json b/frontend/public/steel-plants.json similarity index 100% rename from public/steel-plants.json rename to frontend/public/steel-plants.json diff --git a/public/summit-banner.jpg b/frontend/public/summit-banner.jpg similarity index 100% rename from public/summit-banner.jpg rename to frontend/public/summit-banner.jpg diff --git a/public/sw.js b/frontend/public/sw.js similarity index 100% rename from public/sw.js rename to frontend/public/sw.js diff --git a/public/textures/earth-dark.jpg b/frontend/public/textures/earth-dark.jpg similarity index 100% rename from public/textures/earth-dark.jpg rename to frontend/public/textures/earth-dark.jpg diff --git a/public/textures/earth-topology.png b/frontend/public/textures/earth-topology.png similarity index 100% rename from public/textures/earth-topology.png rename to frontend/public/textures/earth-topology.png diff --git a/public/trust-enamad.png b/frontend/public/trust-enamad.png similarity index 100% rename from public/trust-enamad.png rename to frontend/public/trust-enamad.png diff --git a/public/trust-etehadie.png b/frontend/public/trust-etehadie.png similarity index 100% rename from public/trust-etehadie.png rename to frontend/public/trust-etehadie.png diff --git a/public/trust-samandehi.png b/frontend/public/trust-samandehi.png similarity index 100% rename from public/trust-samandehi.png rename to frontend/public/trust-samandehi.png diff --git a/public/world-map.webp b/frontend/public/world-map.webp similarity index 100% rename from public/world-map.webp rename to frontend/public/world-map.webp diff --git a/public/اندیشکده فولاد آینده_icon.zip b/frontend/public/اندیشکده فولاد آینده_icon.zip similarity index 100% rename from public/اندیشکده فولاد آینده_icon.zip rename to frontend/public/اندیشکده فولاد آینده_icon.zip diff --git a/scripts/fetch-competitor-news.mjs b/frontend/scripts/fetch-competitor-news.mjs similarity index 100% rename from scripts/fetch-competitor-news.mjs rename to frontend/scripts/fetch-competitor-news.mjs diff --git a/src/app/layout/Footer.tsx b/frontend/src/app/layout/Footer.tsx similarity index 100% rename from src/app/layout/Footer.tsx rename to frontend/src/app/layout/Footer.tsx diff --git a/src/app/layout/Header.tsx b/frontend/src/app/layout/Header.tsx similarity index 100% rename from src/app/layout/Header.tsx rename to frontend/src/app/layout/Header.tsx diff --git a/src/app/layout/RootLayout.tsx b/frontend/src/app/layout/RootLayout.tsx similarity index 100% rename from src/app/layout/RootLayout.tsx rename to frontend/src/app/layout/RootLayout.tsx diff --git a/src/app/router.tsx b/frontend/src/app/router.tsx similarity index 100% rename from src/app/router.tsx rename to frontend/src/app/router.tsx diff --git a/src/assets/react.svg b/frontend/src/assets/react.svg similarity index 100% rename from src/assets/react.svg rename to frontend/src/assets/react.svg diff --git a/src/assets/vite.svg b/frontend/src/assets/vite.svg similarity index 100% rename from src/assets/vite.svg rename to frontend/src/assets/vite.svg diff --git a/src/components/ui/DetailSheet.tsx b/frontend/src/components/ui/DetailSheet.tsx similarity index 100% rename from src/components/ui/DetailSheet.tsx rename to frontend/src/components/ui/DetailSheet.tsx diff --git a/src/components/ui/StrategicEvents.tsx b/frontend/src/components/ui/StrategicEvents.tsx similarity index 100% rename from src/components/ui/StrategicEvents.tsx rename to frontend/src/components/ui/StrategicEvents.tsx diff --git a/src/components/ui/animated-radial-chart.tsx b/frontend/src/components/ui/animated-radial-chart.tsx similarity index 100% rename from src/components/ui/animated-radial-chart.tsx rename to frontend/src/components/ui/animated-radial-chart.tsx diff --git a/src/components/ui/border-beam.tsx b/frontend/src/components/ui/border-beam.tsx similarity index 100% rename from src/components/ui/border-beam.tsx rename to frontend/src/components/ui/border-beam.tsx diff --git a/src/components/ui/financial-score-cards.tsx b/frontend/src/components/ui/financial-score-cards.tsx similarity index 100% rename from src/components/ui/financial-score-cards.tsx rename to frontend/src/components/ui/financial-score-cards.tsx diff --git a/src/components/ui/globe.tsx b/frontend/src/components/ui/globe.tsx similarity index 100% rename from src/components/ui/globe.tsx rename to frontend/src/components/ui/globe.tsx diff --git a/src/components/ui/liquid-glass-button.tsx b/frontend/src/components/ui/liquid-glass-button.tsx similarity index 100% rename from src/components/ui/liquid-glass-button.tsx rename to frontend/src/components/ui/liquid-glass-button.tsx diff --git a/src/components/ui/liquid-glass-card.tsx b/frontend/src/components/ui/liquid-glass-card.tsx similarity index 100% rename from src/components/ui/liquid-glass-card.tsx rename to frontend/src/components/ui/liquid-glass-card.tsx diff --git a/src/components/ui/magic-text.tsx b/frontend/src/components/ui/magic-text.tsx similarity index 100% rename from src/components/ui/magic-text.tsx rename to frontend/src/components/ui/magic-text.tsx diff --git a/src/components/ui/progress.tsx b/frontend/src/components/ui/progress.tsx similarity index 100% rename from src/components/ui/progress.tsx rename to frontend/src/components/ui/progress.tsx diff --git a/src/content/factoryReports.ts b/frontend/src/content/factoryReports.ts similarity index 100% rename from src/content/factoryReports.ts rename to frontend/src/content/factoryReports.ts diff --git a/src/content/integrations.ts b/frontend/src/content/integrations.ts similarity index 100% rename from src/content/integrations.ts rename to frontend/src/content/integrations.ts diff --git a/src/content/posts.ts b/frontend/src/content/posts.ts similarity index 100% rename from src/content/posts.ts rename to frontend/src/content/posts.ts diff --git a/src/content/radar.ts b/frontend/src/content/radar.ts similarity index 100% rename from src/content/radar.ts rename to frontend/src/content/radar.ts diff --git a/src/content/radarCategories.ts b/frontend/src/content/radarCategories.ts similarity index 100% rename from src/content/radarCategories.ts rename to frontend/src/content/radarCategories.ts diff --git a/src/content/radarFuture.ts b/frontend/src/content/radarFuture.ts similarity index 100% rename from src/content/radarFuture.ts rename to frontend/src/content/radarFuture.ts diff --git a/src/context/AuthContext.tsx b/frontend/src/context/AuthContext.tsx similarity index 88% rename from src/context/AuthContext.tsx rename to frontend/src/context/AuthContext.tsx index fd1bd9a..482deb4 100644 --- a/src/context/AuthContext.tsx +++ b/frontend/src/context/AuthContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useState, useCallback } from 'react' +import React, { createContext, useContext, useState, useCallback, useEffect } from 'react' const BASE = import.meta.env.VITE_PANEL_API ?? '' @@ -56,6 +56,23 @@ function cacheMember(m: Member | null) { export function AuthProvider({ children }: { children: React.ReactNode }) { const [state, setState] = useState(load) + // Re-validate the cached session against the server on load AND whenever the tab + // regains focus. A member blocked from the admin panel (is_active = 0) gets 401/403 + // from /api/members/me and is logged out client-side, so the membership gate returns. + useEffect(() => { + let cancelled = false + const validate = () => { + if (!load().member) return + authFetch('/api/members/me') + .then(res => { if (!cancelled && (res.status === 401 || res.status === 403)) { cacheMember(null); setState({ member: null }) } }) + .catch(() => {}) + } + validate() + const onFocus = () => validate() + window.addEventListener('focus', onFocus) + return () => { cancelled = true; window.removeEventListener('focus', onFocus) } + }, []) + const login = useCallback(async (email: string, password: string) => { const res = await fetch(`${BASE}/api/members/login`, { method: 'POST', diff --git a/src/context/LangContext.tsx b/frontend/src/context/LangContext.tsx similarity index 100% rename from src/context/LangContext.tsx rename to frontend/src/context/LangContext.tsx diff --git a/src/data/about.ts b/frontend/src/data/about.ts similarity index 100% rename from src/data/about.ts rename to frontend/src/data/about.ts diff --git a/src/data/banners.ts b/frontend/src/data/banners.ts similarity index 100% rename from src/data/banners.ts rename to frontend/src/data/banners.ts diff --git a/src/data/events.ts b/frontend/src/data/events.ts similarity index 100% rename from src/data/events.ts rename to frontend/src/data/events.ts diff --git a/src/data/market.ts b/frontend/src/data/market.ts similarity index 100% rename from src/data/market.ts rename to frontend/src/data/market.ts diff --git a/src/data/media.ts b/frontend/src/data/media.ts similarity index 100% rename from src/data/media.ts rename to frontend/src/data/media.ts diff --git a/src/data/plans.ts b/frontend/src/data/plans.ts similarity index 100% rename from src/data/plans.ts rename to frontend/src/data/plans.ts diff --git a/src/data/reports.ts b/frontend/src/data/reports.ts similarity index 100% rename from src/data/reports.ts rename to frontend/src/data/reports.ts diff --git a/src/data/risks.ts b/frontend/src/data/risks.ts similarity index 100% rename from src/data/risks.ts rename to frontend/src/data/risks.ts diff --git a/src/data/team.ts b/frontend/src/data/team.ts similarity index 100% rename from src/data/team.ts rename to frontend/src/data/team.ts diff --git a/src/index.css b/frontend/src/index.css similarity index 100% rename from src/index.css rename to frontend/src/index.css diff --git a/src/lib/utils.ts b/frontend/src/lib/utils.ts similarity index 100% rename from src/lib/utils.ts rename to frontend/src/lib/utils.ts diff --git a/src/main.tsx b/frontend/src/main.tsx similarity index 100% rename from src/main.tsx rename to frontend/src/main.tsx diff --git a/src/pages/About/About.tsx b/frontend/src/pages/About/About.tsx similarity index 100% rename from src/pages/About/About.tsx rename to frontend/src/pages/About/About.tsx diff --git a/src/pages/Archive/Archive.tsx b/frontend/src/pages/Archive/Archive.tsx similarity index 100% rename from src/pages/Archive/Archive.tsx rename to frontend/src/pages/Archive/Archive.tsx diff --git a/src/pages/AtAGlance/AtAGlance.tsx b/frontend/src/pages/AtAGlance/AtAGlance.tsx similarity index 100% rename from src/pages/AtAGlance/AtAGlance.tsx rename to frontend/src/pages/AtAGlance/AtAGlance.tsx diff --git a/src/pages/Auth/AuthPage.tsx b/frontend/src/pages/Auth/AuthPage.tsx similarity index 100% rename from src/pages/Auth/AuthPage.tsx rename to frontend/src/pages/Auth/AuthPage.tsx diff --git a/src/pages/Contact/Contact.tsx b/frontend/src/pages/Contact/Contact.tsx similarity index 100% rename from src/pages/Contact/Contact.tsx rename to frontend/src/pages/Contact/Contact.tsx diff --git a/src/pages/Events/Events.tsx b/frontend/src/pages/Events/Events.tsx similarity index 100% rename from src/pages/Events/Events.tsx rename to frontend/src/pages/Events/Events.tsx diff --git a/src/pages/Experts/Experts.tsx b/frontend/src/pages/Experts/Experts.tsx similarity index 100% rename from src/pages/Experts/Experts.tsx rename to frontend/src/pages/Experts/Experts.tsx diff --git a/src/pages/Geopolitics/Geopolitics.tsx b/frontend/src/pages/Geopolitics/Geopolitics.tsx similarity index 100% rename from src/pages/Geopolitics/Geopolitics.tsx rename to frontend/src/pages/Geopolitics/Geopolitics.tsx diff --git a/src/pages/Home/Home.tsx b/frontend/src/pages/Home/Home.tsx similarity index 100% rename from src/pages/Home/Home.tsx rename to frontend/src/pages/Home/Home.tsx diff --git a/src/pages/Home/sections/AtAGlanceSection.tsx b/frontend/src/pages/Home/sections/AtAGlanceSection.tsx similarity index 100% rename from src/pages/Home/sections/AtAGlanceSection.tsx rename to frontend/src/pages/Home/sections/AtAGlanceSection.tsx diff --git a/src/pages/Home/sections/FactoryReportsScroll.tsx b/frontend/src/pages/Home/sections/FactoryReportsScroll.tsx similarity index 100% rename from src/pages/Home/sections/FactoryReportsScroll.tsx rename to frontend/src/pages/Home/sections/FactoryReportsScroll.tsx diff --git a/src/pages/Home/sections/FeaturedReportBanner.tsx b/frontend/src/pages/Home/sections/FeaturedReportBanner.tsx similarity index 100% rename from src/pages/Home/sections/FeaturedReportBanner.tsx rename to frontend/src/pages/Home/sections/FeaturedReportBanner.tsx diff --git a/src/pages/Home/sections/IntegrationsSection.tsx b/frontend/src/pages/Home/sections/IntegrationsSection.tsx similarity index 100% rename from src/pages/Home/sections/IntegrationsSection.tsx rename to frontend/src/pages/Home/sections/IntegrationsSection.tsx diff --git a/src/pages/Home/sections/InternalNewsSection.tsx b/frontend/src/pages/Home/sections/InternalNewsSection.tsx similarity index 100% rename from src/pages/Home/sections/InternalNewsSection.tsx rename to frontend/src/pages/Home/sections/InternalNewsSection.tsx diff --git a/src/pages/Home/sections/MapEventsSection.tsx b/frontend/src/pages/Home/sections/MapEventsSection.tsx similarity index 100% rename from src/pages/Home/sections/MapEventsSection.tsx rename to frontend/src/pages/Home/sections/MapEventsSection.tsx diff --git a/src/pages/Home/sections/RadarTechSection.tsx b/frontend/src/pages/Home/sections/RadarTechSection.tsx similarity index 100% rename from src/pages/Home/sections/RadarTechSection.tsx rename to frontend/src/pages/Home/sections/RadarTechSection.tsx diff --git a/src/pages/Home/sections/RiskSection.tsx b/frontend/src/pages/Home/sections/RiskSection.tsx similarity index 100% rename from src/pages/Home/sections/RiskSection.tsx rename to frontend/src/pages/Home/sections/RiskSection.tsx diff --git a/src/pages/Home/sections/SnapshotBar.tsx b/frontend/src/pages/Home/sections/SnapshotBar.tsx similarity index 100% rename from src/pages/Home/sections/SnapshotBar.tsx rename to frontend/src/pages/Home/sections/SnapshotBar.tsx diff --git a/src/pages/Home/sections/SpreadFeatures.tsx b/frontend/src/pages/Home/sections/SpreadFeatures.tsx similarity index 100% rename from src/pages/Home/sections/SpreadFeatures.tsx rename to frontend/src/pages/Home/sections/SpreadFeatures.tsx diff --git a/src/pages/Home/sections/VideocastPodcastSection.tsx b/frontend/src/pages/Home/sections/VideocastPodcastSection.tsx similarity index 100% rename from src/pages/Home/sections/VideocastPodcastSection.tsx rename to frontend/src/pages/Home/sections/VideocastPodcastSection.tsx diff --git a/src/pages/Market/Market.tsx b/frontend/src/pages/Market/Market.tsx similarity index 100% rename from src/pages/Market/Market.tsx rename to frontend/src/pages/Market/Market.tsx diff --git a/src/pages/Membership/Membership.tsx b/frontend/src/pages/Membership/Membership.tsx similarity index 100% rename from src/pages/Membership/Membership.tsx rename to frontend/src/pages/Membership/Membership.tsx diff --git a/src/pages/Podcast/Podcast.tsx b/frontend/src/pages/Podcast/Podcast.tsx similarity index 100% rename from src/pages/Podcast/Podcast.tsx rename to frontend/src/pages/Podcast/Podcast.tsx diff --git a/src/pages/PostDetail/PostDetail.tsx b/frontend/src/pages/PostDetail/PostDetail.tsx similarity index 100% rename from src/pages/PostDetail/PostDetail.tsx rename to frontend/src/pages/PostDetail/PostDetail.tsx diff --git a/src/pages/Profile/ProfilePage.tsx b/frontend/src/pages/Profile/ProfilePage.tsx similarity index 100% rename from src/pages/Profile/ProfilePage.tsx rename to frontend/src/pages/Profile/ProfilePage.tsx diff --git a/src/pages/Pulse/Pulse.tsx b/frontend/src/pages/Pulse/Pulse.tsx similarity index 100% rename from src/pages/Pulse/Pulse.tsx rename to frontend/src/pages/Pulse/Pulse.tsx diff --git a/src/pages/Radar/Radar.tsx b/frontend/src/pages/Radar/Radar.tsx similarity index 100% rename from src/pages/Radar/Radar.tsx rename to frontend/src/pages/Radar/Radar.tsx diff --git a/src/pages/RadarCategory/RadarCategory.tsx b/frontend/src/pages/RadarCategory/RadarCategory.tsx similarity index 100% rename from src/pages/RadarCategory/RadarCategory.tsx rename to frontend/src/pages/RadarCategory/RadarCategory.tsx diff --git a/src/pages/RadarPost/RadarPost.tsx b/frontend/src/pages/RadarPost/RadarPost.tsx similarity index 100% rename from src/pages/RadarPost/RadarPost.tsx rename to frontend/src/pages/RadarPost/RadarPost.tsx diff --git a/src/pages/ReportDetail/ReportDetail.tsx b/frontend/src/pages/ReportDetail/ReportDetail.tsx similarity index 100% rename from src/pages/ReportDetail/ReportDetail.tsx rename to frontend/src/pages/ReportDetail/ReportDetail.tsx diff --git a/src/pages/Risks/Risks.tsx b/frontend/src/pages/Risks/Risks.tsx similarity index 100% rename from src/pages/Risks/Risks.tsx rename to frontend/src/pages/Risks/Risks.tsx diff --git a/src/pages/Scanner/Scanner.tsx b/frontend/src/pages/Scanner/Scanner.tsx similarity index 100% rename from src/pages/Scanner/Scanner.tsx rename to frontend/src/pages/Scanner/Scanner.tsx diff --git a/src/pages/Search/SearchResults.tsx b/frontend/src/pages/Search/SearchResults.tsx similarity index 100% rename from src/pages/Search/SearchResults.tsx rename to frontend/src/pages/Search/SearchResults.tsx diff --git a/src/pages/Special/Special.tsx b/frontend/src/pages/Special/Special.tsx similarity index 100% rename from src/pages/Special/Special.tsx rename to frontend/src/pages/Special/Special.tsx diff --git a/src/pages/SpecializedMeetings/SpecializedMeetings.tsx b/frontend/src/pages/SpecializedMeetings/SpecializedMeetings.tsx similarity index 100% rename from src/pages/SpecializedMeetings/SpecializedMeetings.tsx rename to frontend/src/pages/SpecializedMeetings/SpecializedMeetings.tsx diff --git a/src/pages/SteelDashboard/SteelDashboard.tsx b/frontend/src/pages/SteelDashboard/SteelDashboard.tsx similarity index 100% rename from src/pages/SteelDashboard/SteelDashboard.tsx rename to frontend/src/pages/SteelDashboard/SteelDashboard.tsx diff --git a/src/pages/SteelDashboard/data/greenSteelProjects.ts b/frontend/src/pages/SteelDashboard/data/greenSteelProjects.ts similarity index 100% rename from src/pages/SteelDashboard/data/greenSteelProjects.ts rename to frontend/src/pages/SteelDashboard/data/greenSteelProjects.ts diff --git a/src/pages/SteelDashboard/sections/GreenSteelTracker.tsx b/frontend/src/pages/SteelDashboard/sections/GreenSteelTracker.tsx similarity index 100% rename from src/pages/SteelDashboard/sections/GreenSteelTracker.tsx rename to frontend/src/pages/SteelDashboard/sections/GreenSteelTracker.tsx diff --git a/src/pages/SteelDashboard/sections/IndustrySection.tsx b/frontend/src/pages/SteelDashboard/sections/IndustrySection.tsx similarity index 100% rename from src/pages/SteelDashboard/sections/IndustrySection.tsx rename to frontend/src/pages/SteelDashboard/sections/IndustrySection.tsx diff --git a/src/pages/SteelDashboard/sections/SelfSection.tsx b/frontend/src/pages/SteelDashboard/sections/SelfSection.tsx similarity index 100% rename from src/pages/SteelDashboard/sections/SelfSection.tsx rename to frontend/src/pages/SteelDashboard/sections/SelfSection.tsx diff --git a/src/pages/SteelDashboard/sections/SteelPlantsGlobe.tsx b/frontend/src/pages/SteelDashboard/sections/SteelPlantsGlobe.tsx similarity index 100% rename from src/pages/SteelDashboard/sections/SteelPlantsGlobe.tsx rename to frontend/src/pages/SteelDashboard/sections/SteelPlantsGlobe.tsx diff --git a/src/pages/SteelDashboard/sections/WorldSection.tsx b/frontend/src/pages/SteelDashboard/sections/WorldSection.tsx similarity index 100% rename from src/pages/SteelDashboard/sections/WorldSection.tsx rename to frontend/src/pages/SteelDashboard/sections/WorldSection.tsx diff --git a/src/pages/Subscribe/Subscribe.tsx b/frontend/src/pages/Subscribe/Subscribe.tsx similarity index 100% rename from src/pages/Subscribe/Subscribe.tsx rename to frontend/src/pages/Subscribe/Subscribe.tsx diff --git a/src/pages/Sustainability/Sustainability.tsx b/frontend/src/pages/Sustainability/Sustainability.tsx similarity index 100% rename from src/pages/Sustainability/Sustainability.tsx rename to frontend/src/pages/Sustainability/Sustainability.tsx diff --git a/src/pages/Team/Team.tsx b/frontend/src/pages/Team/Team.tsx similarity index 100% rename from src/pages/Team/Team.tsx rename to frontend/src/pages/Team/Team.tsx diff --git a/src/pages/Technology/Technology.tsx b/frontend/src/pages/Technology/Technology.tsx similarity index 100% rename from src/pages/Technology/Technology.tsx rename to frontend/src/pages/Technology/Technology.tsx diff --git a/tsconfig.app.json b/frontend/tsconfig.app.json similarity index 100% rename from tsconfig.app.json rename to frontend/tsconfig.app.json diff --git a/tsconfig.json b/frontend/tsconfig.json similarity index 100% rename from tsconfig.json rename to frontend/tsconfig.json diff --git a/tsconfig.node.json b/frontend/tsconfig.node.json similarity index 100% rename from tsconfig.node.json rename to frontend/tsconfig.node.json diff --git a/vite.config.ts b/frontend/vite.config.ts similarity index 100% rename from vite.config.ts rename to frontend/vite.config.ts diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 52bf47d..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,4 +0,0 @@ -// App.tsx is no longer the entry point. -// Routing is handled by src/app/router.tsx → src/main.tsx -// This file is kept as a placeholder. -export default function App() { return null } diff --git a/src/app/layout/MarketStrip.tsx b/src/app/layout/MarketStrip.tsx deleted file mode 100644 index cac21fc..0000000 --- a/src/app/layout/MarketStrip.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import { useEffect, useState } from 'react'; -import { marketPrices as fallbackPrices } from '@/data/market'; -import type { MarketPrice, PriceTrend } from '@/data/market'; - -const PANEL_API = - (import.meta as ImportMeta & { env?: Record }).env - ?.VITE_PANEL_API || ''; - -type ApiPrice = { - symbol: string; - name: string; - value: number | null; - unit: string | null; - changeValue: number | null; - changePct: number | null; - sourceUrl: string | null; -}; - -function defaultUnitFor(sourceUrl: string | null): string { - if (!sourceUrl) return ''; - if (sourceUrl.includes('gold-global')) return 'دلار/اونس'; - if (sourceUrl.includes('basemetal')) return 'دلار/تن'; - return ''; -} - -function apiToMarketPrices(items: ApiPrice[]): MarketPrice[] { - return items - .filter((p) => typeof p.value === 'number' && p.value > 0) - .map((p): MarketPrice => { - const pct = p.changePct ?? 0; - const trend: PriceTrend = pct > 0 ? 'up' : pct < 0 ? 'down' : 'flat'; - return { - id: p.symbol, - name: p.name, - value: p.value as number, - unit: p.unit || defaultUnitFor(p.sourceUrl), - change: p.changeValue ?? 0, - changePercent: pct, - trend, - }; - }); -} - -function formatValue(item: MarketPrice): string { - return item.value.toLocaleString('fa-IR'); -} - -function formatChange(item: MarketPrice): string { - const abs = Math.abs(item.change).toLocaleString('fa-IR'); - return `${abs} (${Math.abs(item.changePercent).toFixed(1)}٪)`; -} - -function TickerItem({ item }: { item: MarketPrice }) { - const changeColor = - item.trend === 'up' - ? '#7dbf8a' - : item.trend === 'down' - ? '#e07070' - : 'rgba(255,255,255,0.5)'; - - const arrow = - item.trend === 'up' ? '↑' : item.trend === 'down' ? '↓' : '—'; - - return ( - - - {item.name} - - - {formatValue(item)}{' '} - - {item.unit} - - - - {arrow} {formatChange(item)} - - - ); -} - -export function MarketStrip() { - const [marketPrices, setMarketPrices] = useState(fallbackPrices); - - useEffect(() => { - let cancelled = false; - const load = () => { - fetch(`${PANEL_API}/api/prices`) - .then((r) => (r.ok ? r.json() : null)) - .then((data: ApiPrice[] | null) => { - if (cancelled || !Array.isArray(data) || data.length === 0) return; - const mapped = apiToMarketPrices(data); - if (mapped.length > 0) setMarketPrices(mapped); - }) - .catch(() => { /* keep fallback */ }); - }; - load(); - const id = setInterval(load, 2 * 60 * 1000); - return () => { cancelled = true; clearInterval(id); }; - }, []); - - return ( -
- {/* Live tag */} -
- بازار زنده -
- - {/* Ticker area */} -
-
- {/* Original set */} - {marketPrices.map((item, i) => ( - - ))} - {/* Duplicate for seamless loop */} - {marketPrices.map((item, i) => ( - - ))} -
-
-
- ); -} diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx deleted file mode 100644 index 3f1cff3..0000000 --- a/src/components/ui/Badge.tsx +++ /dev/null @@ -1,56 +0,0 @@ - -type BadgeVariant = 'default' | 'red' | 'amber' | 'green' | 'outline'; - -interface BadgeProps { - children: React.ReactNode; - variant?: BadgeVariant; - className?: string; -} - -const variantStyles: Record = { - default: { - backgroundColor: 'var(--ink)', - color: 'var(--paper)', - border: '1px solid var(--ink)', - }, - red: { - backgroundColor: 'var(--red)', - color: 'var(--paper)', - border: '1px solid var(--red)', - }, - amber: { - backgroundColor: 'var(--amber)', - color: 'var(--ink)', - border: '1px solid var(--amber)', - }, - green: { - backgroundColor: 'var(--green-ink)', - color: 'var(--paper)', - border: '1px solid var(--green-ink)', - }, - outline: { - backgroundColor: 'transparent', - color: 'var(--ink)', - border: '1px solid var(--ink)', - }, -}; - -export function Badge({ children, variant = 'default', className = '' }: BadgeProps) { - return ( - - {children} - - ); -} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx deleted file mode 100644 index f35eda6..0000000 --- a/src/components/ui/Button.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { useState } from 'react' -import type { CSSProperties, ReactNode, MouseEvent } from 'react' - -type ButtonVariant = 'filled' | 'outline' | 'paper' | 'ghost'; -type ButtonSize = 'sm' | 'md' | 'lg'; - -interface ButtonProps { - children: ReactNode; - variant?: ButtonVariant; - size?: ButtonSize; - className?: string; - onClick?: (e: MouseEvent) => void; - type?: 'button' | 'submit' | 'reset'; - disabled?: boolean; - asChild?: boolean; -} - -const sizeStyles: Record = { - sm: { padding: '9px 20px', fontSize: '9px' }, - md: { padding: '11px 24px', fontSize: '11px' }, - lg: { padding: '12px 28px', fontSize: '12px' }, -}; - -const baseStyle: CSSProperties = { - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - gap: '6px', - borderRadius: 0, - fontFamily: 'Vazir, sans-serif', - fontWeight: 700, - letterSpacing: '0.5px', - textTransform: 'uppercase', - cursor: 'pointer', - transition: 'background-color 200ms, color 200ms, border-color 200ms', - outline: 'none', - border: '1px solid transparent', - whiteSpace: 'nowrap', -}; - -const variantBase: Record = { - filled: { - backgroundColor: 'var(--ink)', - color: 'var(--paper)', - borderColor: 'var(--ink)', - }, - outline: { - backgroundColor: 'transparent', - color: 'var(--ink)', - borderColor: 'var(--ink)', - }, - paper: { - backgroundColor: 'var(--paper)', - color: 'var(--ink)', - borderColor: 'var(--paper)', - }, - ghost: { - backgroundColor: 'transparent', - color: 'var(--ink)', - borderColor: 'transparent', - }, -}; - -export function Button({ - children, - variant = 'filled', - size = 'md', - className = '', - onClick, - type = 'button', - disabled = false, -}: ButtonProps) { - const [hovered, setHovered] = useState(false); - - const getHoverStyle = (): CSSProperties => { - if (!hovered || disabled) return {}; - switch (variant) { - case 'filled': - return { backgroundColor: 'var(--ink-2)' }; - case 'outline': - return { backgroundColor: 'var(--ink)', color: 'var(--paper)' }; - case 'paper': - return { backgroundColor: 'var(--paper-2)' }; - case 'ghost': - return { backgroundColor: 'var(--paper-2)' }; - default: - return {}; - } - }; - - return ( - - ); -} diff --git a/src/components/ui/EventCalendar.tsx b/src/components/ui/EventCalendar.tsx deleted file mode 100644 index cecffe6..0000000 --- a/src/components/ui/EventCalendar.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useState } from 'react' - -const NAVY = '#032340' -const GOLD = '#CD9E53' -const NEON = '#d4ff2e' // neon yellow for conference-date markers - -type EventItem = { - g: Date // Gregorian date of the event - title: string - city: string - time: string - type: string - color: string -} - -/* steel conferences during اردیبهشت ۱۴۰۵ (anchor month below) */ -const EVENTS: EventItem[] = [ - { g: new Date(2026, 3, 28), title: 'کنفرانس فولاد خاورمیانه', city: 'دبی، امارات', time: '۰۹:۰۰ – ۱۷:۰۰', type: 'کنفرانس بین‌المللی', color: '#9b1c1c' }, - { g: new Date(2026, 4, 5), title: 'نمایشگاه متالکس ۱۴۰۵', city: 'تهران، ایران', time: '۱۰:۰۰ – ۱۸:۰۰', type: 'نمایشگاه', color: GOLD }, - { g: new Date(2026, 4, 12), title: 'نشست تخصصی زنجیره فولاد', city: 'اصفهان، ایران', time: '۱۴:۰۰ – ۱۸:۰۰', type: 'نشست تخصصی', color: '#1e8449' }, - { g: new Date(2026, 4, 18), title: 'وبینار فولاد سبز و کربن‌زدایی', city: 'آنلاین', time: '۱۶:۰۰ – ۱۸:۰۰', type: 'وبینار', color: '#2471a3' }, -] - -const ANCHOR = new Date(2026, 4, 10) // a day inside اردیبهشت ۱۴۰۵ -const J_MONTHS = ['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'] -const WEEKDAYS = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'] // شنبه … جمعه -const faDigits = (n: number) => n.toLocaleString('fa-IR', { useGrouping: false }) - -/* Jalali year/month/day for a Gregorian Date, via the browser's Persian calendar */ -function jParts(d: Date) { - const parts = new Intl.DateTimeFormat('en-US-u-ca-persian', { day: 'numeric', month: 'numeric', year: 'numeric' }).formatToParts(d) - const get = (t: string) => Number(parts.find((p) => p.type === t)?.value) - return { day: get('day'), month: get('month'), year: get('year') } -} - -const sameDay = (a: Date, b: Date) => - a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate() - -function buildMonth(anchor: Date) { - const { month, year } = jParts(anchor) - // walk back to the 1st of this Jalali month - const first = new Date(anchor) - while (jParts(first).day !== 1) first.setDate(first.getDate() - 1) - // collect every day until the Jalali month rolls over - const days: Date[] = [] - const cur = new Date(first) - while (jParts(cur).month === month && jParts(cur).year === year) { - days.push(new Date(cur)) - cur.setDate(cur.getDate() + 1) - } - const firstCol = (first.getDay() + 1) % 7 // Sat→0 … Fri→6 - return { month, year, days, firstCol } -} - -export default function EventCalendar() { - const { month, year, days, firstCol } = buildMonth(ANCHOR) - const [hover, setHover] = useState(null) - - const eventFor = (d: Date) => EVENTS.findIndex((e) => sameDay(e.g, d)) - - return ( -
- {/* header */} -
-
تقویم رویدادها
-
{J_MONTHS[month - 1]} {faDigits(year)}
-
- - {/* weekday row */} -
- {WEEKDAYS.map((w, i) => ( -
{w}
- ))} -
- - {/* day grid */} -
- {Array.from({ length: firstCol }).map((_, i) =>
)} - {days.map((d, i) => { - const j = jParts(d) - const ev = eventFor(d) - const marked = ev >= 0 - const isHover = hover === ev && marked - return ( -
- - - {/* breathing neon ring on conference days */} - {marked && ( - - )} - - {/* hover card */} - {isHover && ( -
-
- {EVENTS[ev].type} -
-
{EVENTS[ev].title}
-
-
📅 {faDigits(j.day)} {J_MONTHS[month - 1]} {faDigits(year)}
-
📍 {EVENTS[ev].city}
-
🕐 {EVENTS[ev].time}
-
- {/* arrow */} - -
- )} -
- ) - })} -
- - {/* legend */} -
- - روزهای دارای رویداد — نشانگر را نگه دارید -
- - -
- ) -} diff --git a/src/components/ui/PullQuote.tsx b/src/components/ui/PullQuote.tsx deleted file mode 100644 index 4fe9e09..0000000 --- a/src/components/ui/PullQuote.tsx +++ /dev/null @@ -1,44 +0,0 @@ - -interface PullQuoteProps { - text: React.ReactNode; - source?: string; - className?: string; -} - -export function PullQuote({ text, source, className = '' }: PullQuoteProps) { - return ( -
-

- {text} -

- {source && ( -
- {source} -
- )} -
- ); -} diff --git a/src/components/ui/Rule.tsx b/src/components/ui/Rule.tsx deleted file mode 100644 index 5cba6fb..0000000 --- a/src/components/ui/Rule.tsx +++ /dev/null @@ -1,44 +0,0 @@ - -type RuleWeight = 'heavy' | 'mid' | 'light' | 'red'; - -interface RuleProps { - weight?: RuleWeight; - className?: string; -} - -const styles: Record = { - heavy: { - borderTop: '3px solid var(--ink)', - borderBottom: 'none', - margin: 0, - width: '100%', - }, - mid: { - borderTop: '1px solid var(--ink)', - borderBottom: 'none', - margin: 0, - width: '100%', - }, - light: { - borderTop: '1px solid var(--rule-thin)', - borderBottom: 'none', - margin: 0, - width: '100%', - }, - red: { - borderTop: '3px solid var(--red)', - borderBottom: 'none', - margin: 0, - width: '100%', - }, -}; - -export function Rule({ weight = 'mid', className = '' }: RuleProps) { - return ( - - ); -} diff --git a/src/components/ui/SectionBanner.tsx b/src/components/ui/SectionBanner.tsx deleted file mode 100644 index 41401eb..0000000 --- a/src/components/ui/SectionBanner.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { ArrowLeft } from 'lucide-react'; - -type LabelVariant = 'ink' | 'red'; - -interface SectionBannerLink { - text: string; - to: string; -} - -interface SectionBannerProps { - label: string; - labelVariant?: LabelVariant; - link?: SectionBannerLink; -} - -export function SectionBanner({ - label, - labelVariant = 'ink', - link, -}: SectionBannerProps) { - const labelBg = labelVariant === 'red' ? 'var(--red)' : 'var(--ink)'; - - return ( -
- {/* Label block */} - - {label} - - - {/* Rule fill */} - - ); -} diff --git a/src/components/ui/Stamp.tsx b/src/components/ui/Stamp.tsx deleted file mode 100644 index c88bc0e..0000000 --- a/src/components/ui/Stamp.tsx +++ /dev/null @@ -1,37 +0,0 @@ - -type StampColor = 'red' | 'ink'; - -interface StampProps { - children: React.ReactNode; - color?: StampColor; - tilt?: boolean; - className?: string; -} - -export function Stamp({ - children, - color = 'red', - tilt = true, - className = '', -}: StampProps) { - const colorValue = color === 'red' ? 'var(--red)' : 'var(--ink)'; - - return ( - - {children} - - ); -} diff --git a/src/components/ui/StatCard.tsx b/src/components/ui/StatCard.tsx deleted file mode 100644 index b1c5118..0000000 --- a/src/components/ui/StatCard.tsx +++ /dev/null @@ -1,40 +0,0 @@ - -interface StatCardProps { - number: string; - label: string; - className?: string; -} - -export function StatCard({ number, label, className = '' }: StatCardProps) { - return ( -
-
- {number} -
-
- {label} -
-
- ); -} diff --git a/src/components/ui/animated-dashboard-card.tsx b/src/components/ui/animated-dashboard-card.tsx deleted file mode 100644 index ca159b9..0000000 --- a/src/components/ui/animated-dashboard-card.tsx +++ /dev/null @@ -1,177 +0,0 @@ -"use client"; - -import { motion, useReducedMotion } from "framer-motion"; - -interface BonusesIncentivesCardProps { - bonusText?: string; - incentivesText?: string; - bonusesValue?: number; - incentivesValue?: number; - borderColor?: string; - backgroundColor?: string; - outerDotsCount?: number; - innerDotsCount?: number; - enableAnimations?: boolean; - onMoreDetails?: () => void; - /** Label for the left value (default "Bonuses") */ - leftLabel?: string; - /** Label for the right value (default "Incentives") */ - rightLabel?: string; - /** Label for the total (default "TOTAL") */ - totalLabel?: string; - /** CTA button text */ - ctaLabel?: string; - /** Left growth badge */ - leftGrowth?: string; - /** Right growth badge */ - rightGrowth?: string; -} - -const defaults: BonusesIncentivesCardProps = { - bonusesValue: 1250, - incentivesValue: 875, - borderColor: "border-border/30", - backgroundColor: "bg-muted/20", - outerDotsCount: 48, - innerDotsCount: 36, - enableAnimations: true, - leftLabel: "Bonuses", - rightLabel: "Incentives", - totalLabel: "TOTAL", - ctaLabel: "More Details", - leftGrowth: "+15.2%", - rightGrowth: "+8.7%", -}; - -export function BonusesIncentivesCard(props: BonusesIncentivesCardProps) { - const p = { ...defaults, ...props }; - const shouldReduceMotion = useReducedMotion(); - const anim = p.enableAnimations && !shouldReduceMotion; - - const generateDots = (count: number, radius: number, cx: number, cy: number) => - Array.from({ length: count! }, (_, i) => { - const angle = (i / count!) * 2 * Math.PI; - return { - x: Math.round((cx + radius * Math.cos(angle)) * 1000) / 1000, - y: Math.round((cy + radius * Math.sin(angle)) * 1000) / 1000, - }; - }); - - const outerDots = generateDots(p.outerDotsCount!, 185, 203, 200); - const innerDots = generateDots(p.innerDotsCount!, 155, 203, 200); - - const containerVariants = { - hidden: { opacity: 0, y: 20, scale: 0.95 }, - visible: { - opacity: 1, y: 0, scale: 1, - transition: { type: "spring" as const, stiffness: 300, damping: 30, staggerChildren: 0.08, delayChildren: 0.1 }, - }, - }; - - const dotVariants = { - hidden: { opacity: 0, scale: 0 }, - visible: { opacity: 0.6, scale: 1, transition: { duration: 0.5, ease: "easeOut" as const } }, - }; - - const total = (p.bonusesValue! + p.incentivesValue!).toLocaleString(); - - return ( - - -
-
- - {/* Dot rings */} -
- - {outerDots.map((d, i) => ( - - ))} - {innerDots.map((d, i) => ( - - ))} - - - {/* Center text */} -
-
- {p.totalLabel} - ${total} -
-
-
- - {/* Bottom fade + stats */} -
- -
-
- {[ - { label: p.leftLabel!, value: p.bonusesValue!, growth: p.leftGrowth!, color: '#CD9E53', delay: 0.5 }, - { label: p.rightLabel!, value: p.incentivesValue!, growth: p.rightGrowth!, color: '#032340', delay: 0.8 }, - ].map((item) => ( -
-
- - {item.label} -
-
- ${item.value.toLocaleString()} - {item.growth} -
-
- ))} -
- - {p.ctaLabel} -
-
- - - ); -} diff --git a/src/components/ui/apple-cards-carousel.tsx b/src/components/ui/apple-cards-carousel.tsx deleted file mode 100644 index f14dc4b..0000000 --- a/src/components/ui/apple-cards-carousel.tsx +++ /dev/null @@ -1,495 +0,0 @@ -import { - useEffect, - useRef, - useState, - createContext, - useContext, -} from 'react' -import type { ReactNode } from 'react' -import { motion, AnimatePresence } from 'framer-motion' -import { X } from 'lucide-react' -import { clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' - -function cn(...inputs: Parameters) { - return twMerge(clsx(inputs)) -} - -/* ─── types ─────────────────────────────────────────── */ -export interface CardData { - src: string - title: string - category: string - content: ReactNode - /** Optional thumbnail body fields (MUI-style card layout) */ - summary?: string - author?: string - date?: string - pages?: number -} - -interface CarouselContextType { - onCardClose: (index: number) => void - currentIndex: number -} - -/* ─── context ───────────────────────────────────────── */ -const CarouselContext = createContext({ - onCardClose: () => {}, - currentIndex: 0, -}) - -/* ─── Carousel ──────────────────────────────────────── */ -export function Carousel({ - items, - initialScroll = 0, -}: { - items: ReactNode[] - initialScroll?: number -}) { - const carouselRef = useRef(null) - const [currentIndex, setCurrentIndex] = useState(0) - const [paused, setPaused] = useState(false) - - useEffect(() => { - if (carouselRef.current) { - carouselRef.current.scrollLeft = initialScroll - } - }, [initialScroll]) - - /* Auto-advance: scroll one card-width every 4s. Pauses on hover. */ - useEffect(() => { - if (paused) return - const el = carouselRef.current - if (!el) return - const id = window.setInterval(() => { - if (!el) return - const step = window.innerWidth < 768 ? 280 : 360 // card width + gap - const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 4 - el.scrollTo({ - left: atEnd ? 0 : el.scrollLeft + step, - behavior: 'smooth', - }) - }, 4000) - return () => window.clearInterval(id) - }, [paused]) - - function handleCardClose(index: number) { - if (!carouselRef.current) return - const cardWidth = window.innerWidth < 768 ? 230 : 350 - carouselRef.current.scrollTo({ left: (cardWidth + 16) * (index + 1), behavior: 'smooth' }) - setCurrentIndex(index) - } - - function scrollByStep(direction: -1 | 1) { - if (!carouselRef.current) return - const step = window.innerWidth < 768 ? 280 : 360 - carouselRef.current.scrollBy({ left: direction * step, behavior: 'smooth' }) - } - - return ( - -
setPaused(true)} - onMouseLeave={() => setPaused(false)} - > - {/* Scrollable track */} -
setPaused(true)} - onTouchEnd={() => setPaused(false)} - > -
- {items.map((item, index) => ( - - {item} - - ))} -
-
- - {/* ── Glass pill controls — prev/next + auto state ── */} -
- {/* Prev */} - - - {/* Auto state pill */} -
- - {paused ? 'PAUSED' : 'AUTO'} -
- - {/* Next */} - -
- -
-
- ) -} - -/* ─── Card ──────────────────────────────────────────── */ -export function Card({ - card, - index, - layout = false, -}: { - card: CardData - index: number - layout?: boolean -}) { - const [open, setOpen] = useState(false) - const { onCardClose } = useContext(CarouselContext) - - useEffect(() => { - function onKey(e: KeyboardEvent) { if (e.key === 'Escape') handleClose() } - document.body.style.overflow = open ? 'hidden' : 'auto' - window.addEventListener('keydown', onKey) - return () => window.removeEventListener('keydown', onKey) - }, [open]) - - function handleOpen() { setOpen(true) } - function handleClose() { setOpen(false); onCardClose(index) } - - return ( - <> - {/* ── Modal ── */} - - {open && ( - /* Outer: scroll host */ -
- {/* Backdrop — click to close */} - - - {/* Centering wrapper */} -
- e.stopPropagation()} - > - {/* Close button */} - - - {/* Hero image strip */} -
- {card.title} - {/* Stronger gradient — fully opaque at bottom for readable text */} -
- - {/* Category badge — red, top */} -
- - {card.category} - -
- - {/* Title — bottom, bright white with shadow for clarity */} -
-

- {card.title} -

-
-
- - {/* Body */} -
- {card.content} -
- -
-
- )} - - - {/* ── Card thumbnail — MUI ActionAreaCard pattern, editorial palette ── */} - { - e.currentTarget.style.boxShadow = '0 12px 32px rgba(26,23,18,0.12)' - e.currentTarget.style.borderColor = 'var(--ink-6)' - }} - onMouseLeave={(e) => { - e.currentTarget.style.boxShadow = '0 1px 2px rgba(26,23,18,0.04)' - e.currentTarget.style.borderColor = 'var(--rule-thin)' - }} - > - {/* Media — image strip */} -
- - {/* Category badge — overlaid bottom-right of image (RTL: visual start) */} -
- - {card.category} - -
-
- - {/* Content — title + summary + meta */} -
- {/* Title + summary group — vertically centered in remaining space */} -
- {/* Title */} -

- {card.title} -

- - {/* Summary */} - {card.summary && ( -

- {card.summary} -

- )} -
- - {/* Meta footer — tight below summary */} - {(card.author || card.date || card.pages) && ( -
-
- {card.author && ( - - {card.author} - - )} - {card.date && ( - - {card.date} - - )} -
- {card.pages && ( - - {card.pages.toLocaleString('fa-IR')} ص ← - - )} -
- )} -
-
- - ) -} - -/* ─── BlurImage ─────────────────────────────────────── */ -function BlurImage({ src, alt, className }: { src: string; alt: string; className?: string }) { - const [loaded, setLoaded] = useState(false) - return ( - {alt} setLoaded(true)} - className={cn('transition-[filter] duration-500', loaded ? 'blur-0' : 'blur-md', className)} - /> - ) -} - -/* ─── useOutsideClick ───────────────────────────────── */ -export function useOutsideClick( - ref: React.RefObject, - callback: () => void, -) { - useEffect(() => { - function listener(e: MouseEvent | TouchEvent) { - if (!ref.current || ref.current.contains(e.target as Node)) return - callback() - } - document.addEventListener('mousedown', listener) - document.addEventListener('touchstart', listener) - return () => { - document.removeEventListener('mousedown', listener) - document.removeEventListener('touchstart', listener) - } - }, [ref, callback]) -} diff --git a/src/components/ui/bento-grid.tsx b/src/components/ui/bento-grid.tsx deleted file mode 100644 index 9124ba4..0000000 --- a/src/components/ui/bento-grid.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import type { ReactNode, CSSProperties } from 'react' -import { clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' - -function cn(...inputs: Parameters) { - return twMerge(clsx(inputs)) -} - -/* ─── BentoGrid ─────────────────────────────────────────── */ -export function BentoGrid({ - children, - className, -}: { - children: ReactNode - className?: string -}) { - return ( -
- {children} -
- ) -} - -/* ─── BentoGridItem ──────────────────────────────────────── */ -export interface BentoGridItemProps { - title: string - description?: string - header?: ReactNode /* visual block above content */ - footer?: ReactNode /* slot below description */ - icon?: ReactNode - tag?: string - tagColor?: string /* CSS color value */ - accentLine?: boolean /* left/top colored accent rule */ - className?: string - style?: CSSProperties - onClick?: () => void -} - -export function BentoGridItem({ - title, - description, - header, - footer, - icon, - tag, - tagColor = 'var(--red)', - accentLine = false, - className, - style, - onClick, -}: BentoGridItemProps) { - return ( -
- {/* Accent bar */} - {accentLine && ( -
- )} - - {/* Optional visual header block */} - {header &&
{header}
} - - {/* Tag */} - {tag && ( -
- {tag} -
- )} - - {/* Icon + body */} -
- {icon &&
{icon}
} - -

- {title} -

- - {description && ( -

- {description} -

- )} - - {footer &&
{footer}
} -
-
- ) -} diff --git a/src/components/ui/dock-beam.tsx b/src/components/ui/dock-beam.tsx deleted file mode 100644 index 806a0b6..0000000 --- a/src/components/ui/dock-beam.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useEffect, useRef } from 'react' - -export function DockBeam() { - const ref = useRef(null) - - useEffect(() => { - let angle = 0 - let raf: number - const tick = () => { - angle = (angle + 1.2) % 360 - if (ref.current) { - ref.current.style.background = - `conic-gradient(from ${angle}deg at 50% 50%, #CD9E53 0%, transparent 30%, #032340 50%, transparent 70%, #CD9E53 100%)` - } - raf = requestAnimationFrame(tick) - } - raf = requestAnimationFrame(tick) - return () => cancelAnimationFrame(raf) - }, []) - - return ( -
- ) -} diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts deleted file mode 100644 index 67fffda..0000000 --- a/src/components/ui/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { Rule } from './Rule'; -export { Stamp } from './Stamp'; -export { Badge } from './Badge'; -export { Button } from './Button'; -export { SectionBanner } from './SectionBanner'; -export { PullQuote } from './PullQuote'; -export { StatCard } from './StatCard'; diff --git a/src/components/ui/infinite-moving-cards.tsx b/src/components/ui/infinite-moving-cards.tsx deleted file mode 100644 index 56674be..0000000 --- a/src/components/ui/infinite-moving-cards.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useEffect, useRef, useState } from 'react' -import { clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' - -function cn(...inputs: Parameters) { - return twMerge(clsx(inputs)) -} - -export interface MovingCardItem { - quote: string - name: string - title: string - level?: 'critical' | 'high' | 'medium' | 'low' | 'opportunity' -} - -interface InfiniteMovingCardsProps { - items: MovingCardItem[] - direction?: 'left' | 'right' - speed?: 'fast' | 'normal' | 'slow' - pauseOnHover?: boolean - className?: string -} - -const levelConfig = { - critical: { dot: '#7f1d1d', label: 'بحرانی', text: '#7f1d1d', bg: 'rgba(127,29,29,0.08)' }, - high: { dot: '#9b1c1c', label: 'بالا', text: '#9b1c1c', bg: 'rgba(155,28,28,0.07)' }, - medium: { dot: '#92400e', label: 'متوسط', text: '#92400e', bg: 'rgba(146,64,14,0.07)' }, - low: { dot: '#166534', label: 'پایین', text: '#166534', bg: 'rgba(22,101,52,0.07)' }, - opportunity: { dot: '#1e40af', label: 'فرصت', text: '#1e40af', bg: 'rgba(30,64,175,0.07)' }, -} - -export function InfiniteMovingCards({ - items, - direction = 'left', - speed = 'slow', - pauseOnHover = true, - className, -}: InfiniteMovingCardsProps) { - const containerRef = useRef(null) - const scrollerRef = useRef(null) - const [ready, setReady] = useState(false) - - useEffect(() => { - if (!containerRef.current || !scrollerRef.current) return - - /* Clone items for seamless loop */ - const items = Array.from(scrollerRef.current.children) - items.forEach(item => { - const clone = item.cloneNode(true) - scrollerRef.current!.appendChild(clone) - }) - - /* Set CSS custom properties */ - const dur = speed === 'fast' ? '25s' : speed === 'normal' ? '45s' : '70s' - containerRef.current.style.setProperty('--animation-duration', dur) - containerRef.current.style.setProperty('--animation-direction', - direction === 'right' ? 'reverse' : 'forwards') - - setReady(true) - }, [direction, speed]) - - return ( -
-
    - {items.map((item, idx) => { - const cfg = item.level ? levelConfig[item.level] : levelConfig.high - return ( -
  • - {/* Level badge */} -
    -
    - - {cfg.label} - -
    - - {/* Quote / description */} -

    - {item.quote} -

    - - {/* Footer */} -
    -
    -
    - {item.name} -
    -
    - {item.title} -
    -
    -
    - {cfg.label} -
    -
    -
  • - ) - })} -
-
- ) -} diff --git a/src/components/ui/mac-os-dock.tsx b/src/components/ui/mac-os-dock.tsx deleted file mode 100644 index 3a416ed..0000000 --- a/src/components/ui/mac-os-dock.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import React, { useState, useRef, useCallback, useEffect } from 'react'; -import { BorderBeam } from './border-beam'; - -interface DockApp { - id: string; - name: string; - icon: string; -} - -interface MacOSDockProps { - apps: DockApp[]; - onAppClick: (appId: string) => void; - openApps?: string[]; - className?: string; - spacingMultiplier?: number; - showBeam?: boolean; -} - -const MacOSDock: React.FC = ({ - apps, - onAppClick, - openApps = [], - className = '', - spacingMultiplier = 1, - showBeam = false -}) => { - const [mouseX, setMouseX] = useState(null); - const [currentScales, setCurrentScales] = useState(apps.map(() => 1)); - const [currentPositions, setCurrentPositions] = useState([]); - const dockRef = useRef(null); - const iconRefs = useRef<(HTMLDivElement | null)[]>([]); - const animationFrameRef = useRef(undefined); - const lastMouseMoveTime = useRef(0); - - const getResponsiveConfig = useCallback(() => { - if (typeof window === 'undefined') { - return { baseIconSize: 64, maxScale: 1.6, effectWidth: 240 }; - } - const smallerDimension = Math.min(window.innerWidth, window.innerHeight); - if (smallerDimension < 480) { - return { baseIconSize: Math.max(40, smallerDimension * 0.08), maxScale: 1.4, effectWidth: smallerDimension * 0.4 }; - } else if (smallerDimension < 768) { - return { baseIconSize: Math.max(48, smallerDimension * 0.07), maxScale: 1.5, effectWidth: smallerDimension * 0.35 }; - } else if (smallerDimension < 1024) { - return { baseIconSize: Math.max(56, smallerDimension * 0.06), maxScale: 1.6, effectWidth: smallerDimension * 0.3 }; - } else { - return { baseIconSize: Math.max(64, Math.min(80, smallerDimension * 0.05)), maxScale: 1.8, effectWidth: 300 }; - } - }, []); - - const [config, setConfig] = useState(getResponsiveConfig); - const { baseIconSize, maxScale, effectWidth } = config; - const minScale = 1.0; - const baseSpacing = Math.max(4, baseIconSize * 0.08) * spacingMultiplier; - - useEffect(() => { - const handleResize = () => setConfig(getResponsiveConfig()); - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, [getResponsiveConfig]); - - const calculateTargetMagnification = useCallback((mousePosition: number | null) => { - if (mousePosition === null) return apps.map(() => minScale); - return apps.map((_, index) => { - const normalIconCenter = (index * (baseIconSize + baseSpacing)) + (baseIconSize / 2); - const minX = mousePosition - (effectWidth / 2); - const maxX = mousePosition + (effectWidth / 2); - if (normalIconCenter < minX || normalIconCenter > maxX) return minScale; - const theta = ((normalIconCenter - minX) / effectWidth) * 2 * Math.PI; - const cappedTheta = Math.min(Math.max(theta, 0), 2 * Math.PI); - const scaleFactor = (1 - Math.cos(cappedTheta)) / 2; - return minScale + (scaleFactor * (maxScale - minScale)); - }); - }, [apps, baseIconSize, baseSpacing, effectWidth, maxScale, minScale]); - - const calculatePositions = useCallback((scales: number[]) => { - let currentX = 0; - return scales.map((scale) => { - const scaledWidth = baseIconSize * scale; - const centerX = currentX + (scaledWidth / 2); - currentX += scaledWidth + baseSpacing; - return centerX; - }); - }, [baseIconSize, baseSpacing]); - - useEffect(() => { - const initialScales = apps.map(() => minScale); - setCurrentScales(initialScales); - setCurrentPositions(calculatePositions(initialScales)); - }, [apps, calculatePositions, minScale, config]); - - const animateToTarget = useCallback(() => { - const targetScales = calculateTargetMagnification(mouseX); - const targetPositions = calculatePositions(targetScales); - const lerpFactor = mouseX !== null ? 0.2 : 0.12; - - setCurrentScales(prevScales => - prevScales.map((currentScale, index) => currentScale + ((targetScales[index] - currentScale) * lerpFactor)) - ); - setCurrentPositions(prevPositions => - prevPositions.map((currentPos, index) => currentPos + ((targetPositions[index] - currentPos) * lerpFactor)) - ); - - const scalesNeedUpdate = currentScales.some((scale, index) => Math.abs(scale - targetScales[index]) > 0.002); - const positionsNeedUpdate = currentPositions.some((pos, index) => Math.abs(pos - targetPositions[index]) > 0.1); - - if (scalesNeedUpdate || positionsNeedUpdate || mouseX !== null) { - animationFrameRef.current = requestAnimationFrame(animateToTarget); - } - }, [mouseX, calculateTargetMagnification, calculatePositions, currentScales, currentPositions]); - - useEffect(() => { - if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current); - animationFrameRef.current = requestAnimationFrame(animateToTarget); - return () => { if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current); }; - }, [animateToTarget]); - - const handleMouseMove = useCallback((e: React.MouseEvent) => { - const now = performance.now(); - if (now - lastMouseMoveTime.current < 16) return; - lastMouseMoveTime.current = now; - if (dockRef.current) { - const rect = dockRef.current.getBoundingClientRect(); - const padding = Math.max(8, baseIconSize * 0.12); - setMouseX(e.clientX - rect.left - padding); - } - }, [baseIconSize]); - - const handleMouseLeave = useCallback(() => setMouseX(null), []); - - const handleAppClick = (appId: string, index: number) => { - const el = iconRefs.current[index]; - if (el) { - const bounceHeight = -baseIconSize * 0.15; - el.style.transition = 'transform 0.2s ease-out'; - el.style.transform = `translateY(${bounceHeight}px)`; - setTimeout(() => { el.style.transform = 'translateY(0px)'; }, 200); - } - onAppClick(appId); - }; - - const contentWidth = currentPositions.length > 0 - ? Math.max(...currentPositions.map((pos, index) => pos + (baseIconSize * currentScales[index]) / 2)) - : (apps.length * (baseIconSize + baseSpacing)) - baseSpacing; - - const padding = Math.max(8, baseIconSize * 0.12); - - return ( -
-
- {apps.map((app, index) => { - const scale = currentScales[index]; - const position = currentPositions[index] || 0; - const scaledSize = baseIconSize * scale; - return ( -
{ iconRefs.current[index] = el; }} - className="absolute cursor-pointer flex flex-col items-center justify-end" - title={app.name} - onClick={() => handleAppClick(app.id, index)} - style={{ - left: `${position - scaledSize / 2}px`, - bottom: '0px', - width: `${scaledSize}px`, - height: `${scaledSize}px`, - transformOrigin: 'bottom center', - zIndex: Math.round(scale * 10) - }} - > - {app.name} 1.2 ? Math.max(2, baseIconSize * 0.05) : Math.max(1, baseIconSize * 0.03)}px ${scale > 1.2 ? Math.max(4, baseIconSize * 0.1) : Math.max(2, baseIconSize * 0.06)}px rgba(0,0,0,${0.2 + (scale - 1) * 0.15}))` - }} - /> - {openApps.includes(app.id) && ( -
- )} -
- ); - })} -
- {showBeam && ( - - )} -
- ); -}; - -export default MacOSDock; diff --git a/src/components/ui/marquee.tsx b/src/components/ui/marquee.tsx deleted file mode 100644 index ba9408c..0000000 --- a/src/components/ui/marquee.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { CSSProperties, ReactNode } from 'react' -import { clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' - -function cn(...inputs: Parameters) { - return twMerge(clsx(inputs)) -} - -interface MarqueeProps { - children: ReactNode - reverse?: boolean - pauseOnHover?: boolean - className?: string - style?: CSSProperties -} - -/** - * Horizontal infinite marquee. Children duplicated 2x for seamless loop. - * Speed via CSS var --duration. Gap via CSS var --gap (default 1.5rem). - */ -export function Marquee({ - children, - reverse = false, - pauseOnHover = false, - className, - style, -}: MarqueeProps) { - return ( -
- {[0, 1].map((i) => ( -
- {children} -
- ))} -
- ) -} diff --git a/src/components/ui/world-map.tsx b/src/components/ui/world-map.tsx deleted file mode 100644 index a56df9f..0000000 --- a/src/components/ui/world-map.tsx +++ /dev/null @@ -1,193 +0,0 @@ -/** - * WorldMap — Aceternity-style dotted world map with animated arc routes. - * - * DottedMap (height=60, grid='diagonal') emits viewBox="0 0 119 60". - * We project lat/lng into that same coordinate space so the arcs sit - * exactly on the map dots. - */ -import { useRef, useMemo, memo } from 'react' -import { motion } from 'framer-motion' -import DottedMap from 'dotted-map' - -/* ─── Public types ───────────────────────────────────── */ -export interface MapDot { - start: { lat: number; lng: number; label?: string } - end: { lat: number; lng: number; label?: string } -} - -interface WorldMapProps { - dots?: MapDot[] - lineColor?: string - dotColor?: string -} - -/* ─── Map geometry constants ─────────────────────────── */ -const VB_W = 119 // DottedMap viewBox width (height=60 → width=119) -const VB_H = 60 // DottedMap viewBox height - -/* Equirectangular projection matching DottedMap's output */ -function project(lat: number, lng: number) { - const x = (lng + 180) / 360 * VB_W - const y = (90 - lat) / 180 * VB_H - return { x, y } -} - -/* Quadratic bezier — control point lifted proportional to distance */ -function arcPath( - s: { x: number; y: number }, - e: { x: number; y: number }, -) { - const dx = e.x - s.x - const dy = e.y - s.y - const dist = Math.sqrt(dx * dx + dy * dy) - const mx = (s.x + e.x) / 2 - const my = (s.y + e.y) / 2 - dist * 0.35 // lift proportional to length - return `M ${s.x} ${s.y} Q ${mx} ${my} ${e.x} ${e.y}` -} - -/* ─── Memoised base map (expensive, only computed once) ─ */ -const BaseMap = memo(function BaseMap({ dotColor }: { dotColor: string }) { - const map = new DottedMap({ height: 60, grid: 'diagonal' }) - const svg = map.getSVG({ - color: dotColor, - radius: 0.22, - backgroundColor: 'transparent', - }) - return ( -
- ) -}) - -/* ─── Component ──────────────────────────────────────── */ -export default function WorldMap({ - dots = [], - lineColor = '#9b1c1c', // var(--red) - dotColor = '#d1cdc7', // muted cream-grey -}: WorldMapProps) { - const svgRef = useRef(null) - - /* Pre-compute projected coordinates */ - const projected = useMemo( - () => - dots.map(d => ({ - start: project(d.start.lat, d.start.lng), - end: project(d.end.lat, d.end.lng), - startLabel: d.start.label, - endLabel: d.end.label, - })), - [dots], - ) - - return ( - /* aspect-ratio 119:60 ≈ 2:1 keeps the map undistorted */ -
- {/* Dotted world map base */} - - - {/* Arc + dot overlay — same viewBox as DottedMap */} - - - {projected.map((_, i) => ( - - - - - - - ))} - - - {projected.map((p, i) => { - const d = arcPath(p.start, p.end) - return ( - - {/* Ghost — very faint, instant, gives the "track" feel */} - - - {/* Animated arc */} - - - {/* Start endpoint */} - - {/* End endpoint */} - - - ) - })} - -
- ) -} - -/* ─── Pulsing dot ────────────────────────────────────── */ -function PulsingDot({ - cx, cy, color, delay, -}: { - cx: number; cy: number; color: string; delay: number -}) { - return ( - - {/* Outer ring — pulses outward */} - - {/* Solid centre */} - - - - ) -} diff --git a/src/lib/api.ts b/src/lib/api.ts deleted file mode 100644 index 890de5c..0000000 --- a/src/lib/api.ts +++ /dev/null @@ -1,19 +0,0 @@ -const BASE = import.meta.env.VITE_PANEL_API ?? '' - -async function get(path: string, params?: Record): Promise { - const url = new URL(`${BASE}${path}`) - if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v)) - const res = await fetch(url.toString(), { credentials: 'include' }) - if (!res.ok) throw new Error(`API ${path} → ${res.status}`) - return res.json() as Promise -} - -export const api = { - articles: (params?: Record) => get('/api/articles', params), - article: (id: string) => get(`/api/articles/${id}`), - risks: () => get('/api/risks'), - prices: () => get('/api/prices'), - events: () => get('/api/events'), - team: (expert?: boolean) => get('/api/team', expert !== undefined ? { expert: expert ? '1' : '0' } : undefined), - plans: () => get('/api/plans'), -} diff --git a/src/pages/Auth/LoginPage.tsx b/src/pages/Auth/LoginPage.tsx deleted file mode 100644 index dedc63b..0000000 --- a/src/pages/Auth/LoginPage.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useState } from 'react' -import { useNavigate, Link } from 'react-router-dom' -import { useAuth } from '@/context/AuthContext' -import { useLang } from '@/context/LangContext' - -const GOLD = '#CD9E53' -const NAVY = '#032340' - -export default function LoginPage() { - const { lang } = useLang() - const { login } = useAuth() - const navigate = useNavigate() - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') - const [error, setError] = useState('') - const [loading, setLoading] = useState(false) - - const T = { - title: lang === 'fa' ? 'ورود به حساب' : 'Sign In', - email: lang === 'fa' ? 'ایمیل' : 'Email', - pass: lang === 'fa' ? 'رمز عبور' : 'Password', - submit: lang === 'fa' ? 'ورود' : 'Login', - noAcc: lang === 'fa' ? 'حساب ندارید؟' : "Don't have an account?", - register: lang === 'fa' ? 'ثبت‌نام کنید' : 'Register', - } - - async function handleSubmit(e: React.FormEvent) { - e.preventDefault() - setError('') - setLoading(true) - try { - await login(email, password) - navigate('/profile') - } catch (err: unknown) { - setError(err instanceof Error ? err.message : 'خطا در ورود') - } finally { - setLoading(false) - } - } - - return ( -
-
-
-
- - - -
-

{T.title}

-
- -
-
- - setEmail(e.target.value)} - style={inputStyle} - placeholder="example@email.com" - dir="ltr" - /> -
-
- - setPassword(e.target.value)} - style={inputStyle} - dir="ltr" - /> -
- - {error && ( -

{error}

- )} - - -
- -

- {T.noAcc}{' '} - {T.register} -

-
-
- ) -} - -const inputStyle: React.CSSProperties = { - width: '100%', padding: '0.75rem 1rem', borderRadius: 8, - border: '1px solid var(--rule-thin)', background: 'var(--paper)', - color: 'var(--ink)', fontSize: '0.95rem', outline: 'none', boxSizing: 'border-box', -} - -const btnStyle = (loading: boolean): React.CSSProperties => ({ - width: '100%', padding: '0.85rem', borderRadius: 8, - background: loading ? 'var(--ink-5)' : GOLD, - color: NAVY, fontWeight: 700, fontSize: '1rem', - border: 'none', cursor: loading ? 'not-allowed' : 'pointer', - transition: 'opacity 0.2s', -}) diff --git a/src/pages/Auth/RegisterPage.tsx b/src/pages/Auth/RegisterPage.tsx deleted file mode 100644 index 65b4d3b..0000000 --- a/src/pages/Auth/RegisterPage.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import React, { useState } from 'react' -import { useNavigate, Link } from 'react-router-dom' -import { useAuth } from '@/context/AuthContext' -import { useLang } from '@/context/LangContext' - -const GOLD = '#CD9E53' -const NAVY = '#032340' - -export default function RegisterPage() { - const { lang } = useLang() - const { register } = useAuth() - const navigate = useNavigate() - const [name, setName] = useState('') - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') - const [showPass, setShowPass] = useState(false) - const [error, setError] = useState('') - const [loading, setLoading] = useState(false) - - const isRtl = lang === 'fa' - - async function handleSubmit(e: React.FormEvent) { - e.preventDefault() - setError('') - setLoading(true) - try { - await register(name, email, password) - navigate('/profile') - } catch (err: unknown) { - setError(err instanceof Error ? err.message : 'خطا در ثبت‌نام') - } finally { - setLoading(false) - } - } - - return ( -
- {/* ── Brand Panel ── */} -
- - - - - - - - - - - {[0,1,2,3,4].map(i => ( - - ))} - - -
- - -
-
اندیشکده فولاد آینده
-
The Steel Futures Institute
-
- -
- -
-
- عضویت رایگان -
-

- عضو اندیشکده شوید
- و به تحلیل‌ها دسترسی داشته باشید -

-

- با ثبت‌نام رایگان به آرشیو گزارش‌های تخصصی صنعت فولاد دسترسی پیدا کنید -

-
- -
- {['دسترسی به گزارش‌های تخصصی', 'رصد بازار و قیمت‌ها', 'شبکه خبرگان صنعت'].map((item, i) => ( -
-
- -
- {item} -
- ))} -
-
- - {/* ── Form Panel ── */} -
-
-
-

ثبت‌نام

-

یک حساب کاربری جدید بسازید

-
- -
-
- - setName(e.target.value)} - placeholder="علی محمدی" - icon={ - - - - } - /> -
- -
- - setEmail(e.target.value)} - placeholder="example@email.com" - icon={ - - - - } - /> -
- -
- -
- setPassword(e.target.value)} - dir="ltr" - style={inputStyle} - onFocus={e => { e.target.style.borderColor = GOLD }} - onBlur={e => { e.target.style.borderColor = '#e0e0e0' }} - /> - - - - -
-

حداقل ۶ کاراکتر

-
- - {error && ( -
- {error} -
- )} - - -
- -
- حساب دارید؟ - وارد شوید -
- -
- - - - - بازگشت به سایت - -
-
-
-
- ) -} - -/* helpers */ -const labelStyle: React.CSSProperties = { - display: 'block', color: '#444', fontSize: '0.82rem', fontWeight: 600, marginBottom: 6, -} - -const inputStyle: React.CSSProperties = { - width: '100%', padding: '0.85rem 1rem 0.85rem 2.75rem', borderRadius: 10, - border: '1.5px solid #e0e0e0', background: '#fff', - color: '#1a1a1a', fontSize: '0.95rem', outline: 'none', - boxSizing: 'border-box', transition: 'border-color 0.2s', fontFamily: 'inherit', -} - -function InputWithIcon({ icon, ...props }: React.InputHTMLAttributes & { icon: React.ReactNode }) { - return ( -
- { e.target.style.borderColor = GOLD }} - onBlur={e => { e.target.style.borderColor = '#e0e0e0' }} - /> - - {icon} - -
- ) -} diff --git a/src/pages/Home/sections/EventsSection.tsx b/src/pages/Home/sections/EventsSection.tsx deleted file mode 100644 index e91ae6b..0000000 --- a/src/pages/Home/sections/EventsSection.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { Link } from 'react-router-dom' -import { events } from '@/data/events' -import type { Event } from '@/data/events' -import { useLang } from '@/context/LangContext' - -const typeLabel: Record = { - conference: { fa: 'کنگره', en: 'Conference' }, - exhibition: { fa: 'نمایشگاه', en: 'Exhibition' }, - seminar: { fa: 'سمینار', en: 'Seminar' }, - international: { fa: 'جهانی', en: 'International' }, -} - -const T = { - fa: { - overline: 'تقویم رویداد · ۱۴۰۳–۱۴۰۴', - heading: 'رویدادهای پیش‌رو', - sub: 'همایش‌ها، نمایشگاه‌ها و نشست‌های تخصصی صنعت فولاد در ماه‌های آینده — برای حضور یا حمایت رسانه‌ای.', - all: 'تقویم کامل ←', - register: 'ثبت‌نام ←', - soon: 'به‌زودی', - }, - en: { - overline: 'EVENT CALENDAR · 1403–1404', - heading: 'Upcoming Events', - sub: 'Conferences, exhibitions and specialized steel industry meetings — for attendance or media sponsorship.', - all: 'Full Calendar →', - register: 'Register →', - soon: 'Coming Soon', - }, -} - -const typeBorderColor: Record = { - conference: 'var(--ink)', - exhibition: '#6b4a00', - seminar: '#1a4a2a', - international: 'var(--red)', -} - -const typeTextColor: Record = { - conference: 'var(--ink)', - exhibition: '#6b4a00', - seminar: '#1a4a2a', - international: 'var(--red)', -} - -export default function EventsSection() { - const { lang } = useLang() - const t = T[lang] - const displayed = events.slice(0, 4) - - return ( -
-
- {/* ── Editorial header ── */} -
-
-
-
- - {t.overline} - -
-

- {t.heading} -

-

- {t.sub} -

-
- - {t.all} - -
- - {/* 4-col grid */} -
- {displayed.map((event) => ( -
- {/* Date block */} -
-
- {event.date.toLocaleString('fa-IR')} -
-
- {event.month} {event.year.toLocaleString('fa-IR')} -
-
- - {/* Type pill */} -
- - {typeLabel[event.type][lang]} - -
- - {/* Title */} -
{event.title}
- - {/* Location */} -
{event.location} · {event.city}
- - {/* CTA */} - {event.registrationOpen ? ( - - {t.register} - - ) : ( - - {t.soon} - - )} -
- ))} -
-
-
- ) -} diff --git a/src/pages/Home/sections/FactoryBentoSection.tsx b/src/pages/Home/sections/FactoryBentoSection.tsx deleted file mode 100644 index 41b6a19..0000000 --- a/src/pages/Home/sections/FactoryBentoSection.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { Download } from 'lucide-react' -import { BentoGrid, BentoGridItem } from '@/components/ui/bento-grid' -import { useLang } from '@/context/LangContext' - -const MSC_URL = 'https://www.msc.ir/fa-IR/Portal/8029/page/%D9%85%D8%A7%D9%87%D9%86%D8%A7%D9%85%D9%87-%D8%AA%D8%AD%D9%84%DB%8C%D9%84%DB%8C-%D8%B1%D8%A7%D9%87%D8%A8%D8%B1%D8%AF%DB%8C-%DA%A9%D8%A7%D8%B1%D8%AE%D8%A7%D9%86%D9%87' - -type FactoryReport = { - publisher: string - publisherEn: string - title: string - titleEn: string - description: string - descriptionEn: string - date: string - dateEn: string - accentColor: string - imgBg: string -} - -const REPORTS: FactoryReport[] = [ - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'تحلیل عملکرد تولید و صادرات — بهمن ۱۴۰۳', - titleEn: 'Production & Export Performance — Feb 2025', - description: 'بررسی آمار تولید ماهانه، مقایسه با بودجه سالانه و تحلیل روند صادرات فولاد تخت در بازارهای منطقه‌ای', - descriptionEn: 'Monthly production figures, annual budget comparison, and flat steel export trend analysis across regional markets.', - date: 'بهمن ۱۴۰۳', dateEn: 'Feb 2025', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.13) 0%, rgba(180,20,20,0.04) 100%)', - }, - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'وضعیت بازار و قیمت‌گذاری — دی ۱۴۰۳', - titleEn: 'Market Conditions & Pricing — Jan 2025', - description: 'تحلیل نوسانات قیمت فولاد تخت در بازار داخلی، بررسی اثر نرخ ارز و چشم‌انداز بازار در فصل زمستان', - descriptionEn: 'Flat steel price fluctuations in the domestic market, exchange rate impact analysis, and winter market outlook.', - date: 'دی ۱۴۰۳', dateEn: 'Jan 2025', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.11) 0%, rgba(180,20,20,0.03) 100%)', - }, - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'گزارش پایداری و محیط زیست — آذر ۱۴۰۳', - titleEn: 'Sustainability & Environment Report — Dec 2024', - description: 'شاخص‌های زیست‌محیطی ماهانه؛ مصرف انرژی، کاهش انتشار کربن و عملکرد سیستم بازیافت پساب صنعتی', - descriptionEn: 'Monthly environmental KPIs: energy consumption, carbon emission reductions, and industrial wastewater recycling performance.', - date: 'آذر ۱۴۰۳', dateEn: 'Dec 2024', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.09) 0%, rgba(180,20,20,0.03) 100%)', - }, - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'تحلیل نیروی انسانی و بهره‌وری — آبان ۱۴۰۳', - titleEn: 'Workforce & Productivity Analysis — Nov 2024', - description: 'بررسی شاخص‌های بهره‌وری نیروی کار، آموزش‌های ماهانه و آمار ایمنی شغلی و حوادث محیط کار', - descriptionEn: 'Workforce productivity indicators, monthly training hours, occupational safety statistics, and workplace incident rates.', - date: 'آبان ۱۴۰۳', dateEn: 'Nov 2024', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.13) 0%, rgba(180,20,20,0.04) 100%)', - }, - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'عملکرد مالی و سرمایه‌گذاری — مهر ۱۴۰۳', - titleEn: 'Financial Performance & Investment — Oct 2024', - description: 'خلاصه عملکرد مالی، پروژه‌های جاری سرمایه‌گذاری و پیشرفت طرح توسعه تا پایان مهرماه ۱۴۰۳', - descriptionEn: 'Financial performance summary, active investment projects, and development plan progress through end of October 2024.', - date: 'مهر ۱۴۰۳', dateEn: 'Oct 2024', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.10) 0%, rgba(180,20,20,0.03) 100%)', - }, - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'فناوری و نوآوری در فرآیند تولید — شهریور ۱۴۰۳', - titleEn: 'Technology & Innovation in Production — Sep 2024', - description: 'گزارش پیشرفت پروژه‌های دیجیتال‌سازی، بهره‌گیری از هوش مصنوعی در کنترل کیفیت و اتوماسیون خط تولید', - descriptionEn: 'Digitalization project progress, AI adoption in quality control, and production line automation milestones.', - date: 'شهریور ۱۴۰۳', dateEn: 'Sep 2024', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.12) 0%, rgba(180,20,20,0.04) 100%)', - }, - { - publisher: 'فولاد مبارکه', publisherEn: 'Mobarakeh Steel', - title: 'زنجیره تأمین و مواد اولیه — مرداد ۱۴۰۳', - titleEn: 'Supply Chain & Raw Materials — Aug 2024', - description: 'تحلیل وضعیت تأمین سنگ‌آهن، آهن اسفنجی و کُک؛ بررسی ریسک‌های زنجیره تأمین در فصل تابستان', - descriptionEn: 'Iron ore, DRI, and coke supply status; supply chain risk assessment for the summer season.', - date: 'مرداد ۱۴۰۳', dateEn: 'Aug 2024', - accentColor: 'var(--red)', - imgBg: 'linear-gradient(135deg, rgba(180,20,20,0.08) 0%, rgba(180,20,20,0.03) 100%)', - }, -] - -const T = { - fa: { overline: 'MOBARAKEH STEEL', heading: 'ماهنامه تحلیلی کارخانه', all: 'مشاهده همه', download: 'دریافت گزارش' }, - en: { overline: 'MOBARAKEH STEEL', heading: 'Monthly Industry Briefing', all: 'View All', download: 'Download Report' }, -} - -function DownloadCTA({ label }: { label: string }) { - return ( - - - {label} - - ) -} - -function ReportCover({ report, lang }: { report: FactoryReport; lang: 'fa' | 'en' }) { - return ( -
-
- {report.publisherEn.toUpperCase()} -
-
- {lang === 'fa' ? report.publisher : report.publisherEn} -
-
- ) -} - -export default function FactoryBentoSection() { - const { lang } = useLang() - const t = T[lang] - - return ( -
-
-
-
-
{t.overline}
-

{t.heading}

-
- {t.all} -
- - - {REPORTS.map((report, i) => ( - } - tag={`${lang === 'fa' ? report.date : report.dateEn} · ${lang === 'fa' ? 'ماهنامه تحلیلی کارخانه' : 'Monthly Factory Analysis'}`} - tagColor={report.accentColor} - title={lang === 'fa' ? report.title : report.titleEn} - description={lang === 'fa' ? report.description : report.descriptionEn} - footer={} - accentLine - className={i === 3 || i === 6 ? 'md:col-span-2' : ''} - /> - ))} - -
-
- ) -} diff --git a/src/pages/Home/sections/GlobalScannerSection.tsx b/src/pages/Home/sections/GlobalScannerSection.tsx deleted file mode 100644 index 5e41dd6..0000000 --- a/src/pages/Home/sections/GlobalScannerSection.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import { motion } from 'framer-motion' -import WorldMap from '@/components/ui/world-map' -import { useLang } from '@/context/LangContext' - -const T = { - fa: { - h2a: 'تمام رویدادهای فولادی دنیا', h2b: ' را رصد می‌کنیم', - sub: 'از بورس دالیان چین تا گمرک بصره عراق، از تصمیمات کمیته فولاد OECD تا ظرفیت‌سازی‌های هند — هر سیگنالی که بر رقابت‌پذیری ایران اثر می‌گذارد، پیش از بقیه به دست شما می‌رسد.', - stats: [ - { n: '۱۸۰+', label: 'بازار رصد‌شده', sub: 'در ۵ قاره' }, - { n: '۷/۲۴', label: 'پایش لحظه‌ای', sub: 'قیمت و حجم' }, - { n: '۴۰+', label: 'منبع داده', sub: 'بورس‌ها و گمرک‌ها' }, - { n: '۱۵', label: 'کشور رقیب', sub: 'زیر ذره‌بین' }, - ], - }, - en: { - h2a: 'Every Global Steel Event,', h2b: ' Monitored', - sub: "From the Dalian Commodity Exchange to the Basra customs gate, from OECD Steel Committee decisions to India's capacity build-up — every signal affecting Iran's competitiveness reaches you first.", - stats: [ - { n: '180+', label: 'Markets Tracked', sub: 'Across 5 continents' }, - { n: '24/7', label: 'Live Monitoring', sub: 'Price & volume' }, - { n: '40+', label: 'Data Sources', sub: 'Exchanges & customs' }, - { n: '15', label: 'Rival Countries', sub: 'Under the lens' }, - ], - }, -} - -/* ─── Steel trade routes ───────────────────────────────────────────── - Arcs show geopolitically relevant steel flows affecting Iran's market -──────────────────────────────────────────────────────────────────── */ -const ROUTES = [ - // ── Iran's trade flows (2 routes) ─────────────────────────────── - // Iran → Iraq (main export market) - { start: { lat: 35.69, lng: 51.39 }, end: { lat: 33.31, lng: 44.36 } }, - // Iran → Central Asia (Uzbekistan) - { start: { lat: 35.69, lng: 51.39 }, end: { lat: 41.30, lng: 69.24 } }, - - // ── China's global reach (3 routes) ───────────────────────────── - // Shanghai → Rotterdam (European market dumping) - { start: { lat: 31.23, lng: 121.47 }, end: { lat: 51.92, lng: 4.47 } }, - // Shanghai → Lagos (China–Africa infrastructure push) - { start: { lat: 31.23, lng: 121.47 }, end: { lat: 6.45, lng: 3.39 } }, - // Shanghai → Singapore (SE Asia dominance) - { start: { lat: 31.23, lng: 121.47 }, end: { lat: 1.35, lng: 103.82 } }, - - // ── Russia flows (2 routes) ────────────────────────────────────── - // Moscow → Beijing (raw material / billet supply) - { start: { lat: 55.75, lng: 37.62 }, end: { lat: 39.91, lng: 116.39 } }, - // Moscow → Tehran (billet supply to Iran) - { start: { lat: 55.75, lng: 37.62 }, end: { lat: 35.69, lng: 51.39 } }, - - // ── India's competing routes (2 routes) ────────────────────────── - // Mumbai → Dubai (competing with Iran in Gulf) - { start: { lat: 19.07, lng: 72.88 }, end: { lat: 25.20, lng: 55.27 } }, - // Mumbai → Mombasa (East Africa — directly competes with Iran) - { start: { lat: 19.07, lng: 72.88 }, end: { lat: -4.05, lng: 39.67 } }, - - // ── Americas → Asia (iron ore flows) ──────────────────────────── - // São Paulo → Shanghai (Brazilian iron ore) - { start: { lat: -23.55, lng: -46.63 }, end: { lat: 31.23, lng: 121.47 } }, - - // ── South Korea → SE Asia ─────────────────────────────────────── - { start: { lat: 37.56, lng: 126.97 }, end: { lat: 1.35, lng: 103.82 } }, - - // ── Turkey → Europe ────────────────────────────────────────────── - // Istanbul → Berlin (Turkey as EU steel supplier) - { start: { lat: 41.01, lng: 28.97 }, end: { lat: 52.52, lng: 13.40 } }, - - // ── Australia → China (iron ore) ──────────────────────────────── - { start: { lat: -33.87, lng: 151.21 }, end: { lat: 31.23, lng: 121.47 } }, -] - -/* ─── Section ────────────────────────────────────────── */ -export default function GlobalScannerSection() { - const { lang } = useLang() - const t = T[lang] - return ( -
-
- {/* ── Text header ────────────────────────────────── */} -
- {/* Headline */} -

- {t.h2a}{' '} - - {t.h2b} - -

- - {/* Subhead */} -

- {t.sub} -

-
- - {/* ── World map (full width within container) ─── */} -
- -
- - {/* ── Stats strip ──────────────────────────────── */} -
- {t.stats.map((s, i) => ( - -
- {s.n} -
-
- {s.label} -
-
- {s.sub} -
-
- ))} -
-
-
- ) -} diff --git a/src/pages/Home/sections/HeroSection.tsx b/src/pages/Home/sections/HeroSection.tsx deleted file mode 100644 index b0b42bb..0000000 --- a/src/pages/Home/sections/HeroSection.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { useRef, useEffect, useLayoutEffect, useState } from 'react' -import { gsap } from 'gsap' -import { ScrollTrigger } from 'gsap/ScrollTrigger' -import { ArrowRight } from 'lucide-react' -import { Link } from 'react-router-dom' -import { useLang } from '@/context/LangContext' - -gsap.registerPlugin(ScrollTrigger) -// v2 -const NAVY = '#032340' -const GOLD = '#CD9E53' -const PAPER = '#faf7f4' - -const CONTENT = { - fa: { - title: 'بینشی نو برای توسعه‌ی صنعتی', - desc: 'تصمیم‌سازی هوشمند در جهانی در حال تحول\n+۱۰۰ منبع داده جهانی، رصد ۲۴/۷ بازارها', - cta: 'درباره ما', ctaHref: '/about', - }, - en: { - title: 'A New Vision for Industrial Development', - desc: 'Smart decision-making in a world in transformation\n100+ global data sources, 24/7 market monitoring', - cta: 'About Us', ctaHref: '/about', - }, -} - -type Sat = { img: string; x: number; y: number; w: number; depth: number; big?: boolean } -const SATELLITES: Sat[] = [ - { img: '/news/photo-1567789884554-0b844b597180.jpg', x: -38, y: -26, w: 200, depth: 1.0, big: true }, - { img: '/news/photo-1570168007204-dfb528c6958f.jpg', x: 39, y: 24, w: 200, depth: 1.0, big: true }, - { img: '/news/photo-1504307651254-35680f356dfd.jpg', x: -44, y: 20, w: 150, depth: 1.35 }, - { img: '/news/photo-1558618666-fcd25c85cd64.jpg', x: 43, y: -28, w: 140, depth: 1.4 }, - { img: '/news/photo-1518770660439-4636190af475.jpg', x: -26, y: 34, w: 120, depth: 1.7 }, - { img: '/news/photo-1611273426858-450d8e3c9fce.jpg', x: 27, y: -36, w: 120, depth: 1.7 }, - { img: '/news/photo-1540575467063-178a50c2df87.jpg', x: -30, y: -38, w: 110, depth: 1.9 }, - { img: '/news/photo-1581092160607-ee22731c9b4e.jpg', x: 31, y: 37, w: 110, depth: 1.9 }, -] - -export default function HeroSection() { - const { lang } = useLang() - const t = CONTENT[lang] - - const sectionRef = useRef(null) - const pinRef = useRef(null) - const centerRef = useRef(null) - const overlayRef = useRef(null) - const headlineRef = useRef(null) - const hintRef = useRef(null) - const satRefs = useRef<(HTMLDivElement | null)[]>([]) - const [isMobile, setIsMobile] = useState(false) - - useEffect(() => { - const mq = window.matchMedia('(max-width: 767px)') - const update = () => setIsMobile(mq.matches) - update() - mq.addEventListener('change', update) - return () => mq.removeEventListener('change', update) - }, []) - - // Force ScrollTrigger to recalculate after fonts/images settle - useEffect(() => { - const t = setTimeout(() => ScrollTrigger.refresh(), 300) - return () => clearTimeout(t) - }, []) - - useLayoutEffect(() => { - const section = sectionRef.current - if (!section) return - - const ctx = gsap.context(() => { - const tl = gsap.timeline({ - scrollTrigger: { - trigger: section, - start: 'top top', - end: 'bottom bottom', - scrub: true, - }, - }) - - tl.fromTo(pinRef.current, { backgroundColor: NAVY }, { backgroundColor: PAPER, ease: 'none' }, 0) - - tl.fromTo(centerRef.current, - { scale: 2.55, borderRadius: 0 }, - { scale: 1, borderRadius: 18, ease: 'none' }, 0) - - tl.fromTo(overlayRef.current, { opacity: 0.42 }, { opacity: 0, ease: 'none', duration: 0.4 }, 0) - - tl.fromTo(headlineRef.current, - { yPercent: 0, opacity: 1, filter: 'blur(0px)' }, - { yPercent: -10, opacity: 0, filter: 'blur(8px)', ease: 'none', duration: 0.35 }, 0) - - tl.fromTo(hintRef.current, { opacity: 1 }, { opacity: 0, ease: 'none', duration: 0.08 }, 0) - - satRefs.current.forEach((el, i) => { - if (!el) return - const s = SATELLITES[i] - tl.fromTo(el, - { xPercent: -50, yPercent: -50, x: `${s.x * 0.12}vw`, y: `${s.y * 0.12}vh`, scale: 0.4, opacity: 0 }, - { x: `${s.x * s.depth}vw`, y: `${s.y * s.depth}vh`, scale: 1, opacity: 1, ease: 'none' }, 0) - }) - }, section) - - return () => ctx.revert() - }, [isMobile]) - - return ( -
- {/* pinned stage */} -
- - {/* satellite scatter */} - {SATELLITES.map((sat, i) => { - const ratio = sat.big ? 1.25 : 0.82 - return ( -
{ satRefs.current[i] = el }} - style={{ - position: 'absolute', left: '50%', top: '50%', - width: sat.w, height: sat.w * ratio, - marginLeft: -sat.w / 2, marginTop: -(sat.w * ratio) / 2, - borderRadius: 14, overflow: 'hidden', - boxShadow: '0 24px 60px -18px rgba(7,29,73,0.28), 0 4px 14px rgba(7,29,73,0.10)', - willChange: 'transform, opacity', opacity: 0, - }} - className="max-md:hidden" - > - -
- ) - })} - - {/* center video — de-zooms from full-bleed cover to a centered card */} -
-
- ) -} diff --git a/src/pages/Home/sections/LatestNewsBentoSection.tsx b/src/pages/Home/sections/LatestNewsBentoSection.tsx deleted file mode 100644 index 3059b56..0000000 --- a/src/pages/Home/sections/LatestNewsBentoSection.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import { useEffect, useState, type CSSProperties } from 'react' -import { motion } from 'framer-motion' -import { reports } from '@/data/reports' - -const featured = reports.find(r => r.featured)! -const flashReport = reports.find(r => r.type === 'flash')! - -const PANEL_API = - (import.meta as ImportMeta & { env?: Record }).env - ?.VITE_PANEL_API || '' - -type PriceRow = { label: string; value: string; delta: string; up: boolean } - -const FALLBACK_PRICES: PriceRow[] = [ - { label: 'HRC آسیا', value: '۵۹۰', delta: '+۳.۲٪', up: true }, - { label: 'میلگرد ایران', value: '۲۱,۸۰۰', delta: '+۰.۸٪', up: true }, - { label: 'سنگ‌آهن دالیان', value: '۱۰۸', delta: '+۱۸٪', up: true }, - { label: 'HRC اروپا', value: '۶۲۴', delta: '−۱.۴٪', up: false }, -] - -type ApiPrice = { - symbol: string - name: string - value: number | null - changePct: number | null -} - -function toPersianDigits(s: string): string { - return s.replace(/[0-9]/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[Number(d)]) -} - -function formatNumber(n: number): string { - const fixed = Math.abs(n) >= 1000 - ? n.toLocaleString('en-US') - : n.toFixed(Math.abs(n) >= 10 ? 0 : 2) - return toPersianDigits(fixed) -} - -function mapApiToRows(items: ApiPrice[]): PriceRow[] { - return items - .filter((p) => typeof p.value === 'number') - .slice(0, 4) - .map((p) => { - const pct = p.changePct ?? 0 - const sign = pct > 0 ? '+' : pct < 0 ? '−' : '' - const magnitude = toPersianDigits(Math.abs(pct).toFixed(1)) - return { - label: p.name, - value: formatNumber(p.value as number), - delta: `${sign}${magnitude}٪`, - up: pct >= 0, - } - }) -} - -/* ─── Shared card style (NO gridColumn here — set via className) */ -const CARD: CSSProperties = { - background: 'var(--paper)', - padding: '28px 28px 24px', - display: 'flex', - flexDirection: 'column', - direction: 'rtl', - fontFamily: 'Vazir, sans-serif', - position: 'relative', -} - -function Tag({ text, color = 'var(--red)' }: { text: string; color?: string }) { - return ( -
- {text} -
- ) -} - -function Accent({ color = 'var(--red)' }: { color?: string }) { - return ( -
- ) -} - -/* ─── Section ────────────────────────────────────────────── */ -export default function LatestNewsBentoSection() { - const [prices, setPrices] = useState(FALLBACK_PRICES) - - useEffect(() => { - let cancelled = false - const load = () => { - fetch(`${PANEL_API}/api/prices`) - .then((r) => (r.ok ? r.json() : null)) - .then((data: ApiPrice[] | null) => { - if (cancelled || !Array.isArray(data) || data.length === 0) return - const rows = mapApiToRows(data) - if (rows.length > 0) setPrices(rows) - }) - .catch(() => { /* keep fallback */ }) - } - load() - const id = setInterval(load, 2 * 60 * 1000) - return () => { cancelled = true; clearInterval(id) } - }, []) - - return ( -
- - {/* ── Header ── */} -
- رصد روزانه بازار و صنعت - بهمن ۱۴۰۳ -
- - {/* ── Bento grid ── */} - - {/* - 3-col grid on desktop, 1-col on mobile. - Items ① and ⑥ use col-span-1 md:col-span-2. - NO gridColumn in inline style — inline style overrides Tailwind. - */} -
- - {/* ① Featured report — col-span-2 on desktop ─── */} -
- - - {/* Ghost number */} -
01
-

- {featured.title} -

-

- {featured.summary} -

- {/* Footer */} -
-
- {featured.authorInitial} -
-
-
{featured.author}
-
{featured.publishDate} · {featured.pages} صفحه
-
-
- خواندن ← -
-
- - {/* ② Market prices ─────────────────────────────── */} -
- - -
- آخرین نرخ‌های بازار -
-
- {prices.map(p => ( -
- {p.label} -
- {p.value} - {p.delta} -
-
- ))} -
-
- - {/* ③ Flash report ──────────────────────────────── */} -
- - -
- +۱۸٪ -
-

- جهش قیمت سنگ‌آهن در بورس دالیان -

-

- برآورد اثر بر هزینه تمام‌شده تولیدکنندگان DRI ایران -

-
- - {/* ④ Event ─────────────────────────────────────── */} -
- - -
-
-
۲۵
-
بهمن
-
-
-
تهران
-
هتل اسپیناس پالاس
-
-
-

- همایش ملی فولاد ایران ۱۴۰۳ -

-

- نشست سالانه تولیدکنندگان، صادرکنندگان و سیاست‌گذاران -

-
- - {/* ⑤ Risk alert ────────────────────────────────── */} -
- -
-
- - هشدار ریسک · بحرانی - -
-
- دی ۱۴۰۳ -
-

- مسدود شدن ۳ مسیر پرداخت صادراتی -

-

- بسته تحریمی جدید مسیرهای بانکی صادرات فولاد را محدود کرده است -

-
- - {/* ⑥ Expert quote — col-span-2 on desktop ────── */} -
- - -
"
-

- فولاد ایران در آستانه تصمیمی تاریخی است: یا باید بازارهای جایگزین را با - جدیت دنبال کند، یا شاهد کاهش تدریجی سهم صادراتی خود در خاورمیانه باشد. -

-
-
م
-
-
دکتر علی محمدی
-
مدیر ارشد پژوهش · اندیشکده فولاد آینده
-
-
-
- - {/* ⑦ Key stat ──────────────────────────────────── */} -
- - -
- ۵۴M -
-
- تن — صادرات فولاد چین -
-

- در ۶ ماه اول ۲۰۲۴ — رکوردی که قیمت‌های آسیای غربی را ۱۲٪ کاهش داد -

-
- -
- -
- ) -} diff --git a/src/pages/Home/sections/LatestReportsSection.tsx b/src/pages/Home/sections/LatestReportsSection.tsx deleted file mode 100644 index 81e3ec0..0000000 --- a/src/pages/Home/sections/LatestReportsSection.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import { useState } from 'react' -import { Link } from 'react-router-dom' -import { SectionBanner } from '@/components/ui/SectionBanner' -import { reports, type Report } from '@/data/reports' - -const displayReports = reports.filter(r => !r.featured).slice(0, 6) - -const typeLabel: Record = { - quarterly: 'گزارش فصلی', - flash: 'فوری', - risk: 'ریسک', - free: 'رایگان', - special: 'ویژه', -} - -const dotColor: Record = { - quarterly: 'var(--ink)', - flash: 'var(--red)', - risk: 'var(--red)', - free: 'var(--green-ink)', - special: 'var(--amber)', -} - -function formatPrice(n: number) { - return n.toLocaleString('fa-IR') -} - -interface ReportCardProps { - report: Report - colIndex: number - rowIndex: number - totalRows: number -} - -function ReportCard({ report, colIndex, rowIndex, totalRows }: ReportCardProps) { - const [hovered, setHovered] = useState(false) - const isLastRow = rowIndex === totalRows - 1 - const isRightCol = colIndex === 0 // RTL: right col is index 0 - - return ( -
setHovered(true)} - onMouseLeave={() => setHovered(false)} - > - {/* Type badge */} -
-
- - {typeLabel[report.type] ?? report.type} · {report.category} - -
- - {/* Title */} - -

- {report.title} -

- - - {/* Deck */} -

- {report.summary.slice(0, 140)} - {report.summary.length > 140 ? '…' : ''} -

- - {/* Divider */} -
- - {/* Footer row */} -
- {/* Price block */} -
- {report.isFree ? ( - - دانلود رایگان - - ) : ( - - {formatPrice(report.price)} - - تومان - - - )} -
- {report.pages} صفحه · {report.publishDate} -
-
- - {/* Buy link */} - - (e.currentTarget.style.textDecoration = 'underline') - } - onMouseLeave={e => (e.currentTarget.style.textDecoration = 'none')} - > - {report.isFree ? 'دانلود' : 'خرید گزارش'} ← - -
-
- ) -} - -export default function LatestReportsSection() { - const rows = Math.ceil(displayReports.length / 2) - - return ( -
- {/* Section header */} -
- -
- - {/* 2-col grid */} -
- {displayReports.map((report, idx) => { - const colIndex = idx % 2 - const rowIndex = Math.floor(idx / 2) - return ( - - ) - })} -
-
- ) -} diff --git a/src/pages/Home/sections/NewsletterMarquee.tsx b/src/pages/Home/sections/NewsletterMarquee.tsx deleted file mode 100644 index 7174f44..0000000 --- a/src/pages/Home/sections/NewsletterMarquee.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { useLang } from '@/context/LangContext' - -const NAVY = '#032340' -const GOLD = '#CD9E53' - -/* خبرنامه فولاد — recent issue titles, scrolled as a continuous marquee. */ -const ISSUES = { - fa: [ - { num: '۱۲', title: 'فولاد و تحریم‌های جدید: راهکارهای صادراتی ۱۴۰۴', to: '#newsletter-12' }, - { num: '۱۱', title: 'قیمت‌گذاری فولاد در بازار داخلی: چالش‌ها و فرصت‌ها', to: '#newsletter-11' }, - { num: '۱۰', title: 'آمار تولید فولاد ایران در ۹ ماهه ۱۴۰۳', to: '#newsletter-10' }, - { num: '۰۹', title: 'فولاد سبز؛ چشم‌انداز ایران در برابر رقبای منطقه‌ای', to: '#newsletter-09' }, - { num: '۰۸', title: 'تحلیل بازار عراق: فرصت‌ها و تهدیدها برای صادرکنندگان ایرانی', to: '#newsletter-08' }, - { num: '۰۷', title: 'هوش مصنوعی در کنترل کیفیت فولاد: کاربردها و چالش‌ها', to: '#newsletter-07' }, - ], - en: [ - { num: '12', title: 'Steel & New Sanctions: Export Strategies for 1404', to: '#newsletter-12' }, - { num: '11', title: 'Steel Pricing in the Domestic Market: Challenges & Opportunities', to: '#newsletter-11' }, - { num: '10', title: 'Iran Steel Production Stats — First 9 Months of 1403', to: '#newsletter-10' }, - { num: '09', title: "Green Steel: Iran's Outlook Against Regional Rivals", to: '#newsletter-09' }, - { num: '08', title: 'Iraq Market Analysis: Opportunities & Threats for Exporters', to: '#newsletter-08' }, - { num: '07', title: 'AI in Steel Quality Control: Applications & Challenges', to: '#newsletter-07' }, - ], -} - -const T = { - fa: { overline: 'STEEL NEWSLETTER', heading: 'خبرنامه فولاد', live: 'به‌روزرسانی زنده' }, - en: { overline: 'STEEL NEWSLETTER', heading: 'Steel Newsletter', live: 'Live updates' }, -} - -function Item({ num, title, to }: { num: string; title: string; to: string }) { - return ( - { (e.currentTarget.querySelector('.nm-title') as HTMLElement).style.color = GOLD }} - onMouseLeave={(e) => { (e.currentTarget.querySelector('.nm-title') as HTMLElement).style.color = 'rgba(255,255,255,0.9)' }} - > - {num} - {title} - {/* blinking live dot on the (right) trailing side of each item */} - - - ) -} - -export default function NewsletterMarquee() { - const { lang } = useLang() - const isFa = lang === 'fa' - const t = T[lang] - const items = ISSUES[lang] - // duplicate the list so the scroll loops seamlessly - const loop = [...items, ...items] - - return ( -
- {/* heading */} -
-
- {t.overline} - -

{t.heading}

- {/* live pulse */} - - - {t.live} - -
-
- - {/* moving track with edge fades */} -
-
-
-
- {loop.map((it, i) => )} -
-
- - -
- ) -} diff --git a/src/pages/Home/sections/NewsletterSection.tsx b/src/pages/Home/sections/NewsletterSection.tsx deleted file mode 100644 index 8dd5ee2..0000000 --- a/src/pages/Home/sections/NewsletterSection.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { useState } from 'react' - -export default function NewsletterSection() { - const [email, setEmail] = useState('') - const [submitted, setSubmitted] = useState(false) - - function handleSubmit(e: React.FormEvent) { - e.preventDefault() - if (email.trim()) { - setSubmitted(true) - setEmail('') - } - } - - return ( -
- {/* Outer grid — strip inline padding, use Tailwind */} -
- {/* Left: description */} -
-
- خبرنامه اندیشکده -
-

- هر هفته: خلاصه تحولات بازار فولاد، گزارش‌های جدید و هشدارهای فوری — مستقیم در صندوق ورودی شما -

-
- ↑ بیش از ۲,۸۰۰ مدیر، مقام دولتی و کارشناس عضو -
-
- - {/* Divider */} -
- - {/* Right: form */} -
- {submitted ? ( -
- ✓ عضویت شما با موفقیت ثبت شد. از همراهی شما متشکریم. -
- ) : ( -
- - setEmail(e.target.value)} - placeholder="ایمیل سازمانی شما" - required - autoComplete="email" - style={{ - flex: 1, - padding: '12px 14px', - border: '1px solid var(--ink)', - borderLeft: 'none', - fontFamily: 'Vazir, sans-serif', - fontSize: 13, - background: 'var(--paper)', - color: 'var(--ink)', - outline: 'none', - borderRadius: 0, - minWidth: 0, - }} - /> - -
- )} -

اطلاعات شما محرمانه است و فروخته نمی‌شود

-
-
-
- ) -} diff --git a/src/pages/Home/sections/PartnersSection.tsx b/src/pages/Home/sections/PartnersSection.tsx deleted file mode 100644 index 46a5862..0000000 --- a/src/pages/Home/sections/PartnersSection.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import { Marquee } from '@/components/ui/marquee' - -interface Partner { - short: string - name: string - sector: string -} - -const partners: Partner[] = [ - { short: 'MSC', name: 'فولاد مبارکه اصفهان', sector: 'تولید · اصفهان' }, - { short: 'KSC', name: 'فولاد خوزستان', sector: 'تولید · اهواز' }, - { short: 'ESC', name: 'ذوب آهن اصفهان', sector: 'تولید · اصفهان' }, - { short: 'HSC', name: 'فولاد هرمزگان', sector: 'تولید · بندرعباس' }, - { short: 'KJK', name: 'فولاد کاوه جنوب کیش', sector: 'تولید · کیش' }, - { short: 'CMG', name: 'چادرملو', sector: 'معدن · یزد' }, - { short: 'GEG', name: 'گل گهر سیرجان', sector: 'معدن · سیرجان' }, - { short: 'IMD', name: 'ایمیدرو', sector: 'هلدینگ دولتی' }, - { short: 'MIM', name: 'وزارت صمت', sector: 'سیاست‌گذاری' }, - { short: 'ICC', name: 'اتاق بازرگانی ایران', sector: 'بخش خصوصی' }, - { short: 'BIM', name: 'بانک صنعت و معدن', sector: 'تأمین مالی' }, - { short: 'NDF', name: 'صندوق توسعه ملی', sector: 'سرمایه‌گذاری' }, -] - -const half = Math.ceil(partners.length / 2) -const rowA = partners.slice(0, half) -const rowB = partners.slice(half) - -function PartnerCard({ p }: { p: Partner }) { - return ( -
{ - e.currentTarget.style.borderColor = 'var(--ink)' - e.currentTarget.style.background = 'var(--paper-2)' - }} - onMouseLeave={(e) => { - e.currentTarget.style.borderColor = 'var(--rule-thin)' - e.currentTarget.style.background = 'var(--paper)' - }} - > -
- {p.short} -
- -
-
- {p.name} -
-
- {p.sector} -
-
-
- ) -} - -export default function PartnersSection() { - return ( -
-
-
-
-
-
- - همراهان و مشتریان - -
-

- کسانی که با ما کار می‌کنند -

-

- تولیدکنندگان فولاد، شرکت‌های معدنی، نهادهای سیاست‌گذار و - مؤسسات مالی که از گزارش‌های راهبردی ما استفاده می‌کنند. -

-
- - - {partners.length.toLocaleString('fa-IR')} · NETWORK - -
- -
- - {rowA.map((p) => ( - - ))} - - - - {rowB.map((p) => ( - - ))} - - - -
- ) -} diff --git a/src/pages/Home/sections/PlansSection.tsx b/src/pages/Home/sections/PlansSection.tsx deleted file mode 100644 index 3dab0c2..0000000 --- a/src/pages/Home/sections/PlansSection.tsx +++ /dev/null @@ -1,350 +0,0 @@ -import { useState } from 'react' -import { plans } from '@/data/plans' - -/* ─── Star icon (for "popular" badge) ──────────────── */ -function Star() { - return ( - - - - ) -} - -/* ─── Check icon ──────────────────────────────────── */ -function Check({ featured }: { featured: boolean }) { - return ( - - - - ) -} - -/* ─── Plan tagline lookup ─────────────────────────── */ -const TAGLINE: Record = { - individual: 'برای کارشناسان و محققان', - organizational: 'برای شرکت‌ها و سازمان‌ها', - ministry: 'برای نهادهای دولتی', -} - -/* ─── Individual plan card ────────────────────────── */ -function PlanCard({ plan, isMiddle }: { plan: (typeof plans)[0]; isMiddle: boolean }) { - const [hovered, setHovered] = useState(false) - const featured = plan.featured - - return ( -
- {/* "Popular" badge — top right */} - {plan.badge && ( -
- - {plan.badge} -
- )} - - {/* Header */} -
-
- {plan.name} -
-

- {TAGLINE[plan.id]} -

-
- - {plan.price === 0 ? 'تماس' : plan.price.toLocaleString('fa-IR')} - - {plan.price > 0 ? ( - - تومان / {plan.period} - - ) : ( - - · {plan.period} - - )} -
-
- - {/* Features */} -
- {plan.features.map(f => ( -
- - - {f} - -
- ))} -
- - {/* CTA */} -
- -
-
- ) -} - -/* ─── Enterprise bottom card ─────────────────────── */ -const enterpriseFeatures = [ - 'پروژه‌های نامحدود', - 'پشتیبانی تیمی اختصاصی', - 'فاکتور رسمی سازمانی', - 'بدون سقف توکن یا داده', - 'دعوت اعضای نامحدود', - 'قرارداد SLA معتبر', - 'نقش‌های سفارشی کاربری', - 'امنیت پیشرفته سازمانی', - 'API اتصال به سیستم داخلی', - 'گزارش‌دهی اختصاصی', - 'کارگاه‌های تخصصی دوره‌ای', - 'مشاوره راهبردی مستقیم', -] - -function EnterpriseCard() { - const [hov, setHov] = useState(false) - return ( -
- {/* Left: description */} -
-
- وزارتخانه / سازمان دولتی -
-

- راهکار سازمانی کامل -

-

- به راهکارهای سفارشی، پشتیبانی اختصاصی یا قیمت‌گذاری حجمی نیاز دارید؟ - با کارشناسان ما درباره پلن منحصربه‌فرد سازمانتان صحبت کنید. -

- -
- - {/* Right: feature grid */} -
- {enterpriseFeatures.map(f => ( -
- - - {f} - -
- ))} -
-
- ) -} - -/* ─── Section ────────────────────────────────────── */ -export default function PlansSection() { - return ( -
-
- - {/* Header */} -
-
- اشتراک و دسترسی -
-

- پلن مناسب خود را انتخاب کنید -

-

- از دسترسی پایه برای پژوهشگران تا راهکار کامل برای وزارتخانه‌ها — - هر سطحی از نیاز را پوشش می‌دهیم. -

-
- - {/* 3-col plan cards */} -
- {plans.map((plan, idx) => ( - - ))} -
- - {/* Enterprise bottom card */} - - - {/* Money-back note */} -
- ✓   ضمانت بازگشت وجه ۱۴ روزه  ·  بدون قرارداد اجباری -
-
-
- ) -} diff --git a/src/pages/Home/sections/ReportsCarouselSection.tsx b/src/pages/Home/sections/ReportsCarouselSection.tsx deleted file mode 100644 index 0199f45..0000000 --- a/src/pages/Home/sections/ReportsCarouselSection.tsx +++ /dev/null @@ -1,330 +0,0 @@ -import { Link } from 'react-router-dom' -import { Carousel, Card } from '@/components/ui/apple-cards-carousel' -import type { CardData } from '@/components/ui/apple-cards-carousel' -import { reports } from '@/data/reports' -import type { Report } from '@/data/reports' - -/* ─── Per-report images (industrial, steel-relevant) ─ */ -const reportImage: Record = { - 'iran-steel-global-trade-scenarios-1404': - '/news/photo-1578662996442-48f60103fc96.jpg', - 'steel-energy-consumption-efficiency-1403': - '/news/photo-1473341304170-971dccb5ac1e.jpg', - 'global-steel-market-q3-1403': - '/news/photo-1504328345606-18bbc8c9d7d1.jpg', - 'iran-steel-export-sanctions-impact-1403': - '/news/photo-1494412519320-aa613dfb7738.jpg', - 'flash-iron-ore-price-surge-1403': - '/news/photo-1611974789855-9c2a0a7236a3.jpg', - 'domestic-production-dri-outlook-1403': - '/news/photo-1567789884554-0b844b597180.jpg', - 'esg-carbon-emissions-steel-iran-1403': - '/news/photo-1466611653911-95081537e5b7.jpg', - 'policy-steel-pricing-reform-1403': - '/news/photo-1551288049-bebda4e38f71.jpg', - 'flash-china-steel-overcapacity-1403': - '/news/photo-1570168007204-dfb528c6958f.jpg', -} - -const fallbackImage = - '/news/photo-1558618666-fcd25c85cd64.jpg' - -const typeLabel: Record = { - quarterly: 'گزارش فصلی', - flash: 'گزارش فوری', - risk: 'گزارش ریسک', - free: 'رایگان', - special: 'گزارش ویژه', -} - -const typeColor: Record = { - flash: 'var(--red)', - risk: '#92400e', - free: '#166534', - quarterly: 'var(--ink-4)', - special: 'var(--ink)', -} - -/* ─── Key findings per category (mock but plausible) ─ */ -const keyFindings: Record = { - 'بازار جهانی': [ - 'کاهش ۱۲٪ تقاضای چین در نیمه دوم ۲۰۲۴ فشار قیمتی جهانی ایجاد کرده', - 'هند با رشد ۱۸٪ ظرفیت تولید، رقیب اصلی ایران در بازارهای منطقه شده', - 'پیش‌بینی بازگشت تقاضا در Q2 2025 با ریکاوری بخش ساختمان آمریکا', - ], - 'انرژی و ESG': [ - 'شکاف مصرف انرژی واحدهای ایرانی ۲۵٪ بیشتر از میانگین جهانی است', - 'اجرای CBAM از ۲۰۲۶ می‌تواند ۱۵٪ رقابت‌پذیری صادرات را کاهش دهد', - 'مسیر هیدروژن سبز برای ایران تا ۱۴۱۰ اقتصادی نخواهد بود', - ], - 'صادرات': [ - 'عراق، افغانستان و پاکستان ۶۵٪ صادرات فولاد ایران را تشکیل می‌دهند', - 'مسیرهای جایگزین آفریقای شرقی پتانسیل ۲ میلیون تن اضافی دارد', - 'تحریم‌های جدید ۳ مسیر اصلی پرداخت را مسدود کرده است', - ], - 'تولید داخلی': [ - 'ایران با ۳۰ میلیون تن DRI رتبه اول جهانی را حفظ کرده است', - 'کمبود گاز زمستانی می‌تواند تا ۲۰٪ ظرفیت تولید را کاهش دهد', - '۸ پروژه در دست احداث تا ۱۴۰۵ به ظرفیت می‌رسند', - ], - 'سیاست‌گذاری': [ - 'قیمت‌گذاری دستوری هزینه ۱.۲ میلیارد دلار در سال به صنعت تحمیل می‌کند', - 'تجربه ترکیه نشان می‌دهد آزادسازی تدریجی قیمت بهترین مسیر است', - 'اصلاح تعرفه‌های سنگ‌آهن می‌تواند ۸٪ به حاشیه سود اضافه کند', - ], - 'ریسک و بحران': [ - 'احتمال تشدید تحریم‌ها در ۶ ماه آینده ۶۵٪ برآورد می‌شود', - 'ریسک کمبود ارز برای واردات مواد اولیه در سطح بالا قرار دارد', - 'شکست مذاکرات هسته‌ای می‌تواند ۳۰٪ صادرات را تحت تأثیر قرار دهد', - ], -} - -/* ─── Report contents (TOC-style) ───────────────────── */ -const reportContents: Record = { - quarterly: ['مروری بر بازار فصل', 'تحلیل قیمت‌ها و حجم معاملات', 'رویدادهای کلیدی', 'پیش‌بینی فصل آینده', 'داده‌های خام ضمیمه'], - flash: ['خلاصه اجرایی', 'رویداد و محرک', 'تأثیر بر بازار داخلی', 'توصیه‌های فوری'], - risk: ['ارزیابی ریسک', 'تحلیل سناریو', 'احتمال‌سنجی', 'راهکارهای پوشش ریسک', 'پیوست داده'], - free: ['خلاصه یافته‌ها', 'روش‌شناسی', 'مقایسه بین‌المللی', 'توصیه‌های سیاستی'], - special: ['تحلیل جامع', 'سناریوسازی کمی', 'مقایسه تطبیقی', 'توصیه‌های راهبردی', 'مدل Excel پیوست'], -} - -/* ─── Expanded card body ─────────────────────────────── */ -function ReportCardContent({ report }: { report: Report }) { - const findings = keyFindings[report.category] ?? [] - const contents = reportContents[report.type] ?? [] - - return ( -
- - {/* Tags */} -
- {report.tags.map(tag => ( - - {tag} - - ))} -
- - {/* Summary */} -

- {report.summary} -

- - {/* Key findings */} - {findings.length > 0 && ( -
-
- یافته‌های کلیدی -
-
- {findings.map((f, i) => ( -
-
- {f} -
- ))} -
-
- )} - - {/* Report contents */} - {contents.length > 0 && ( -
-
- محتوای این گزارش -
-
- {contents.map((item, i) => ( -
- - {(i + 1).toLocaleString('fa-IR')} - - {item} -
- ))} -
-
- )} - - {/* Meta grid */} -
- {[ - { label: 'نویسنده', value: report.author }, - { label: 'صفحات', value: `${report.pages} صفحه` }, - { label: 'تاریخ انتشار', value: report.publishDate }, - ].map(item => ( -
-
- {item.label} -
-
{item.value}
-
- ))} -
- - {/* Author */} -
-
- {report.authorInitial} -
-
-
{report.author}
-
{report.authorRole}
-
-
- - {/* CTA */} -
- {report.isFree ? ( - - دانلود رایگان - - ) : ( - <> - - - مشاهده جزئیات ← - - - )} -
-
- ) -} - -/* ─── Section ────────────────────────────────────────── */ -export default function ReportsCarouselSection() { - const carouselReports = reports.filter(r => !r.featured) - - const cards: CardData[] = carouselReports.map(report => ({ - src: reportImage[report.id] ?? fallbackImage, - title: report.title, - category: typeLabel[report.type] ?? report.type, - summary: report.summary, - author: report.author, - date: report.publishDate, - pages: report.pages, - content: , - })) - - const cardElements = cards.map((card, index) => ( - - )) - - return ( -
- {/* ── Editorial header ── */} -
-
-
-
-
- - آرشیو پژوهش · ۱۴۰۳ - -
-

- تحلیل‌های اخیر -

-

- مجموعه‌ای از گزارش‌های راهبردی، تحلیل‌های فصلی و توصیه‌های سیاستی - منتشرشده در ماه‌های اخیر. -

-
- - - همه گزارش‌ها ← - -
- - {/* Legend row */} -
- {Object.entries(typeLabel).map(([key, label]) => ( -
-
- {label} -
- ))} -
-
- -
- -
-
- ) -} diff --git a/src/pages/Home/sections/SpecialSpreadSection.tsx b/src/pages/Home/sections/SpecialSpreadSection.tsx deleted file mode 100644 index 3533fb6..0000000 --- a/src/pages/Home/sections/SpecialSpreadSection.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { useState } from 'react' -import { reports } from '@/data/reports' - -const featured = reports.find(r => r.featured) ?? reports[0] - -const items = [ - { num: '۱', title: 'تحلیل ۴ سناریوی اصلی', desc: 'از بدبینانه تا خوش‌بینانه با احتمال‌سنجی کمی و مدل‌سازی قیمتی' }, - { num: '۲', title: 'نقشه بازارهای هدف جدید', desc: 'جایگزین‌های اروپا: آفریقای شرقی، آسیای مرکزی، عراق و افغانستان' }, - { num: '۳', title: '۱۲ توصیه سیاستی مستقیم', desc: 'برای وزارت صمت، گمرک، سازمان بنادر و بانک مرکزی' }, - { num: '۴', title: 'مقایسه تطبیقی با رقبا', desc: 'ایران در برابر ترکیه، هند، روسیه و برزیل در هر سناریو' }, - { num: '۵', title: 'مدل Excel پیوست', desc: 'داده‌های خام و مدل قابل شخصی‌سازی برای تیم‌های تحلیلی' }, -] - -function BuyButton({ label, dark }: { label: string; dark?: boolean }) { - const [hov, setHov] = useState(false) - return ( - - ) -} - -export default function SpecialSpreadSection() { - return ( -
- {/* LEFT */} -
-
- {/* Overline */} -
-
- - گزارش استراتژیک · زمستان ۱۴۰۳ - -
- - {/* Headline */} -

- ایران در نقشه
- جدید تجارت
- فولاد جهان:
- چهار سناریو
- برای ۱۴۰۴ -

- - {/* Description */} -

- {featured.summary} -

-
- -
- {/* Author */} -
-
- {featured.authorInitial} -
-
-
{featured.author}
-
{featured.authorRole}
-
-
- - {/* Price + CTA */} -
-
-
قیمت گزارش
-
- {featured.price.toLocaleString('fa-IR')} تومان -
-
-
- - -
-
-
-
- - {/* RIGHT */} -
-
- آنچه در این گزارش می‌یابید -
- - {items.map((item, i) => ( -
-
- {item.num} -
-
-
{item.title}
-
{item.desc}
-
-
- ))} -
-
- ) -} diff --git a/src/pages/Home/sections/SustainabilitySection.tsx b/src/pages/Home/sections/SustainabilitySection.tsx deleted file mode 100644 index eec188b..0000000 --- a/src/pages/Home/sections/SustainabilitySection.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { motion } from 'framer-motion' - -const REPORTS = [ - { - year: '۱۴۰۲', - title: 'گزارش پایداری فولاد مبارکه', - tags: ['کاهش کربن', 'مصرف آب', 'ایمنی شغلی'], - summary: 'کاهش ۱۸٪ انتشار CO₂ نسبت به سال پایه، بازیافت ۹۴٪ پساب صنعتی', - }, - { - year: '۱۴۰۲', - title: 'گزارش مسئولیت اجتماعی ذوب‌آهن اصفهان', - tags: ['جامعه محلی', 'آموزش', 'محیط زیست'], - summary: 'سرمایه‌گذاری ۲۴۰ میلیارد تومانی در پروژه‌های اجتماعی منطقه', - }, - { - year: '۱۴۰۱', - title: 'استراتژی خنثی‌سازی کربن صنعت فولاد ایران', - tags: ['ESG', 'هیدروژن سبز', 'نقشه راه'], - summary: 'نقشه راه کاهش انتشار تا ۴۵٪ تا سال ۱۴۱۰', - }, -] - -const MSTID_STATS = [ - { label: 'شاخص پایش‌شده', value: '۵۰۰+' }, - { label: 'کارخانه عضو', value: '۴۸' }, - { label: 'به‌روزرسانی داده', value: 'روزانه' }, -] - -export default function SustainabilitySection() { - return ( -
-
- - {/* گزارش‌های پایداری */} -
-
-
-
SUSTAINABILITY
-

گزارش‌های پایداری

-
- همه گزارش‌ها ← -
- -
- {REPORTS.map((r, i) => ( - -
-
- گزارش پایداری · {r.year} -
-

{r.title}

-

{r.summary}

-
- {r.tags.map(tag => ( - - {tag} - - ))} -
- - ))} -
-
- - {/* MSTid.com banner */} - -
-
POWERED BY
-
MSTid.com
-
- مرجع جامع آمار و اطلاعات صنعت فولاد — داده‌های لحظه‌ای از کارخانه‌ها، بازارها و شاخص‌های اقتصادی -
-
-
- {MSTID_STATS.map(s => ( -
-
{s.value}
-
{s.label}
-
- ))} -
- - ورود به سامانه ← - -
- -
-
- ) -} diff --git a/src/pages/Home/sections/TeamSection.tsx b/src/pages/Home/sections/TeamSection.tsx deleted file mode 100644 index f755bf4..0000000 --- a/src/pages/Home/sections/TeamSection.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import { useState } from 'react' -import { Link } from 'react-router-dom' -import { team } from '@/data/team' - -function ProfileCard({ member }: { member: (typeof team)[0] }) { - const [hovered, setHovered] = useState(false) - - return ( -
setHovered(true)} - onMouseLeave={() => setHovered(false)} - style={{ - background: hovered ? 'var(--paper)' : 'transparent', - border: '1px solid var(--rule-thin)', - padding: '24px 18px 18px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - textAlign: 'center', - transition: 'background 160ms, transform 160ms', - transform: hovered ? 'translateY(-2px)' : 'none', - cursor: 'pointer', - position: 'relative', - }} - > - {/* Reports count badge — top-end */} -
- {member.reportCount.toLocaleString('fa-IR')} - گزارش -
- - {/* Avatar */} -
- {member.initial} -
- - {/* Name */} -

- {member.name} -

- - {/* Role */} - - {member.role} - - - {/* Top expertise chip */} - {member.expertise[0] && ( -
- {member.expertise[0]} -
- )} - - {/* Email footer */} - {member.email && ( - e.stopPropagation()} - style={{ - fontSize: 10, - color: 'var(--ink-5)', - textDecoration: 'none', - fontWeight: 500, - direction: 'ltr', - fontFamily: 'ui-monospace, monospace', - paddingTop: 10, - borderTop: '1px solid var(--rule-thin)', - width: '100%', - textAlign: 'center', - }} - > - {member.email} - - )} -
- ) -} - -export default function TeamSection() { - return ( -
-
- {/* Editorial sub-bar */} -
- - هیئت تحریریه و متخصصان راهبردی - - - همه کارشناسان ← - -
- - {/* Profile grid — 4 col desktop, 2 col tablet, 1 col mobile */} -
- {team.slice(0, 4).map(member => ( - - ))} -
-
-
- ) -}