From db57a22a0999e1cd0ec56cc47aa7e50e5dffdcc2 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:50:27 +0700 Subject: [PATCH] feat: add product section components and integrate them into the homepage landing page --- apps/product-site/src/app/[locale]/page.tsx | 792 +----------------- .../src/components/sections/Contact.tsx | 80 ++ .../src/components/sections/Dashboard.tsx | 101 +++ .../src/components/sections/Destinations.tsx | 59 ++ .../src/components/sections/Ecosystem.tsx | 194 +++++ .../src/components/sections/Footer.tsx | 67 ++ .../src/components/sections/Hero.tsx | 85 ++ .../src/components/sections/Navbar.tsx | 70 ++ .../src/components/sections/Pricing.tsx | 71 ++ .../src/components/sections/Why.tsx | 49 ++ 10 files changed, 796 insertions(+), 772 deletions(-) create mode 100644 apps/product-site/src/components/sections/Contact.tsx create mode 100644 apps/product-site/src/components/sections/Dashboard.tsx create mode 100644 apps/product-site/src/components/sections/Destinations.tsx create mode 100644 apps/product-site/src/components/sections/Ecosystem.tsx create mode 100644 apps/product-site/src/components/sections/Footer.tsx create mode 100644 apps/product-site/src/components/sections/Hero.tsx create mode 100644 apps/product-site/src/components/sections/Navbar.tsx create mode 100644 apps/product-site/src/components/sections/Pricing.tsx create mode 100644 apps/product-site/src/components/sections/Why.tsx diff --git a/apps/product-site/src/app/[locale]/page.tsx b/apps/product-site/src/app/[locale]/page.tsx index 7b9ab47..1a903d8 100644 --- a/apps/product-site/src/app/[locale]/page.tsx +++ b/apps/product-site/src/app/[locale]/page.tsx @@ -1,794 +1,42 @@ -'use client'; - -import { useTranslations } from 'next-intl'; -import { LanguageSwitcher } from '@/components/LanguageSwitcher'; -import { useEffect, useState } from 'react'; -import { DestinationsCarousel } from '@/components/DestinationsCarousel'; +import { Navbar } from '@/components/sections/Navbar'; +import { Hero } from '@/components/sections/Hero'; +import { Why } from '@/components/sections/Why'; +import { Ecosystem } from '@/components/sections/Ecosystem'; +import { Destinations } from '@/components/sections/Destinations'; +import { Dashboard } from '@/components/sections/Dashboard'; +import { Pricing } from '@/components/sections/Pricing'; +import { Contact } from '@/components/sections/Contact'; +import { Footer } from '@/components/sections/Footer'; export default function Page() { - const t = useTranslations(); - const [isScrolled, setIsScrolled] = useState(false); - const [isMenuOpen, setIsMenuOpen] = useState(false); - - useEffect(() => { - const handleScroll = () => { - const floating = window.scrollY > 60; - setIsScrolled(floating); - document.body.classList.toggle('nav-is-floating', floating); - }; - window.addEventListener('scroll', handleScroll, { passive: true }); - handleScroll(); - - const yearSpan = document.getElementById('current-year'); - if (yearSpan) yearSpan.textContent = new Date().getFullYear().toString(); - - // Reveal animation: report bars + access ring - const grid = document.getElementById('bentoGrid'); - let obs: IntersectionObserver; - if (grid) { - const bars = grid.querySelectorAll('.report-bar'); - const ring = grid.querySelector('.ring-progress'); - let done = false; - obs = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting && !done) { - done = true; - bars.forEach((bar, i) => { - setTimeout(() => { - if (bar.dataset.w) bar.style.width = bar.dataset.w; - }, i * 150); - }); - if (ring && ring.dataset.dash) { - setTimeout(() => { - ring.style.strokeDasharray = ring.dataset.dash!; - }, 200); - } - obs.disconnect(); - } - }); - }, - { threshold: 0.35 }, - ); - obs.observe(grid); - } - - return () => { - window.removeEventListener('scroll', handleScroll); - if (obs) obs.disconnect(); - }; - }, []); - return (
{/* ============ FIXED NAV ============ */} -
-
- -
-
+ -
-
- -
-

- {t('Hero.title1')} -
- {t('Hero.title2')} -

- -
-
-
- -
-
{t('Hero.stats.timeAccess')}
-
- 0.4{t('Hero.stats.seconds')} -
-
{t('Hero.stats.avgValidation')}
-
-
-
- -
-
{t('Hero.stats.ticketsSold')}
-
1.249
-
{t('Hero.stats.todayAllChannels')}
-
-
-
- -
-
{t('Hero.stats.sync')}
-
12
-
{t('Hero.stats.txSynced')}
-
-
-
- -
-
{t('Hero.stats.visitorOps')}
-
- - - - -
-
{t('Hero.stats.activeZones')}
-
-
- -
- - - {t('Hero.subtitle')} - -

{t('Hero.desc')}

- - {t('Hero.cta')} - - -
-
-
-
+ {/* ============ HERO ============ */} + {/* ============ WHY SECTION ============ */} -
-
-
-

{t('Why.title')}

-

{t('Why.desc')}

-
- -
-
- -
-
-
- -
-

{t('Why.items.offline.title')}

-

{t('Why.items.offline.desc')}

-
-
-
- -
-

{t('Why.items.speed.title')}

-

{t('Why.items.speed.desc')}

-
-
-
- -
-

{t('Why.items.flexibility.title')}

-

{t('Why.items.flexibility.desc')}

-
-
-
-
+ {/* ============ ECOSYSTEM / BENTO ============ */} -
-
-
-

{t('Ecosystem.title')}

-

{t('Ecosystem.desc')}

-
- -
- {/* Ticketing */} -
-

{t('Ecosystem.ticketing.title')}

-

{t('Ecosystem.ticketing.desc')}

-
-
-
-
- TX24409198-8798 - TX24409198-8797 - TX24409198-8798 - TX24409198-8901 - TX24409198-8811 -
-
-
-
- TX24409198-8801 - TX24409198-8787 - TX24409198-8786 - TX24409198-8901 - TX24409198-8881 -
-
-
-
- TX24409198-8902 - TX24409198-8788 - TX24409198-8790 - TX24409198-8905 - TX24409198-8812 -
-
-
-
-
- - {/* Access Management */} -
-
-
-
- {t('Ecosystem.stats.qr')} - -
-
- {t('Ecosystem.stats.gate')} - -
-
- {t('Ecosystem.stats.rfid')} - -
-
-
- - - - - - - - - - - -
- 0.4s - {t('Ecosystem.stats.avg')} -
-
-
-

{t('Ecosystem.access.title')}

-

{t('Ecosystem.access.desc')}

-
- - {/* Visitor Operations */} -
-
-
-
-
-
-
-
-
- {t('Ecosystem.stats.queue')} -
-
-
-
-
-
-
-
-
-
-
-
-

{t('Ecosystem.visitor.title')}

-

{t('Ecosystem.visitor.desc')}

-
- - {/* Report */} -
-

{t('Ecosystem.report.title')}

-

{t('Ecosystem.report.desc')}

-
-
-
-
- Rp32.5M - {t('Ecosystem.stats.revenue')} -
-
-
-
-
- 1,284 - {t('Ecosystem.stats.visitors')} -
-
-
-
-
- 1,120,240 - {t('Ecosystem.stats.checkin')} -
-
-
-
-
-
-
+ {/* ============ DESTINATIONS (carousel) ============ */} -
-
- -

{t('Destinations.title')}

-

{t('Destinations.desc')}

-
- } - > -
-

{t('Destinations.items.themePark.title')}

-

{t('Destinations.items.themePark.desc')}

-
- -
-
-
-

{t('Destinations.items.event.title')}

-

{t('Destinations.items.event.desc')}

-
- -
-
-
-

{t('Destinations.items.museum.title')}

-

{t('Destinations.items.museum.desc')}

-
- -
-
-
-

{t('Destinations.items.stadium.title')}

-

{t('Destinations.items.stadium.desc')}

-
- -
-
-
-

{t('Destinations.items.exhibition.title')}

-

{t('Destinations.items.exhibition.desc')}

-
- -
-
- - -
+ -
-
-
-

- {t('Destinations.title21')} -
- {t('Destinations.title22')} -

-

{t('Destinations.desc2')}

-
- - - - - - - - - - -
-
-
-
- -
-

{t('Destinations.apps.online.title')}

-

{t('Destinations.apps.online.desc')}

-
- -
-
- -
-

{t('Destinations.apps.cashier.title')}

-

{t('Destinations.apps.cashier.desc')}

-
-
- -
- - - - {/* */} - - - - -
- -
-
- -
-

{t('Destinations.apps.pos.title')}

-

{t('Destinations.apps.pos.desc')}

-
- -
- - - {/* */} - - - - -
- -
-
- -
-

{t('Destinations.apps.queue.title')}

-

{t('Destinations.apps.queue.desc')}

-
- -
- - - {/* */} - - - - -
- -
-
- -
-

{t('Destinations.apps.admin.title')}

-

{t('Destinations.apps.admin.desc')}

-
-
-
-
+ {/* ============ DASHBOARD ============ */} + {/* ============ PRICING ============ */} -
-
-
-

- {t('Pricing.title1')} -
- {t('Pricing.title2')} -

-

{t('Pricing.desc')}

-
- -
-
-

{t('Pricing.rental.title')}

-
- {t('Pricing.rental.tags.0')} - {t('Pricing.rental.tags.1')} - {t('Pricing.rental.tags.2')} -
-

{t('Pricing.rental.desc')}

- - {t('Pricing.contactUs')} - - {t('Pricing.whatYouGet')} -
    -
  • - - {t('Pricing.rental.f1')} -
  • -
  • - - {t('Pricing.rental.f2')} -
  • -
  • - - {t('Pricing.rental.f3')} -
  • -
  • - - {t('Pricing.rental.f4')} -
  • -
  • - - {t('Pricing.rental.f5')} -
  • -
-
- -
-

{t('Pricing.subscription.title')}

-
- {t('Pricing.subscription.tags.0')} - {t('Pricing.subscription.tags.1')} -
-

{t('Pricing.subscription.desc')}

- - {t('Pricing.contactUs')} - - {t('Pricing.whatYouGet')} -
    -
  • - - {t('Pricing.subscription.f1')} -
  • -
  • - - {t('Pricing.subscription.f2')} -
  • -
  • - - {t('Pricing.subscription.f3')} -
  • -
  • - - {t('Pricing.subscription.f4')} -
  • -
  • - - {t('Pricing.subscription.f5')} -
  • -
-
- -
-

{t('Pricing.onPremise.title')}

-
- {t('Pricing.onPremise.tags.0')} - {t('Pricing.onPremise.tags.1')} -
-

{t('Pricing.onPremise.desc')}

- - {t('Pricing.contactUs')} - - {t('Pricing.whatYouGet')} -
    -
  • - - {t('Pricing.onPremise.f1')} -
  • -
  • - - {t('Pricing.onPremise.f2')} -
  • -
  • - - {t('Pricing.onPremise.f3')} -
  • -
  • - - {t('Pricing.onPremise.f4')} -
  • -
  • - - {t('Pricing.onPremise.f5')} -
  • -
-
- -
-

{t('Pricing.buyOut.title')}

-
- {t('Pricing.buyOut.tags.0')} - {t('Pricing.buyOut.tags.1')} - {t('Pricing.buyOut.tags.2')} -
-

{t('Pricing.buyOut.desc')}

- - {t('Pricing.contactUs')} - - {t('Pricing.whatYouGet')} -
    -
  • - - {t('Pricing.buyOut.f1')} -
  • -
  • - - {t('Pricing.buyOut.f2')} -
  • -
  • - - {t('Pricing.buyOut.f3')} -
  • -
  • - - {t('Pricing.buyOut.f4')} -
  • -
  • - - {t('Pricing.buyOut.f5')} -
  • -
-
-
-
-
+ {/* ============ CONTACT ============ */} -
-
-
-
- - - {t('Contact.eyebrow')} - -

{t('Contact.title')}

-

{t('Contact.desc')}

- -
-
-
- -
-
- {t('Contact.email')} - hello@pintumasuk.id -
-
-
-
- -
-
- {t('Contact.phone')} - +62 812-3456-7890 -
-
-
-
- -
-
- {t('Contact.office')} - {t('Contact.address')} -
-
-
-
- -
-

{t('Contact.form.title')}

-
e.preventDefault()}> -
-
- - -
-
- - -
-
-
- - -
- -

{t('Contact.form.note')}

-
-
-
-
-
+ {/* ============ FOOTER ============ */} - +
); } diff --git a/apps/product-site/src/components/sections/Contact.tsx b/apps/product-site/src/components/sections/Contact.tsx new file mode 100644 index 0000000..36ea2de --- /dev/null +++ b/apps/product-site/src/components/sections/Contact.tsx @@ -0,0 +1,80 @@ +'use client'; + +import { useTranslations } from 'next-intl'; + +export function Contact() { + const t = useTranslations(); + + const contactList = [ + { + icon: 'uil-envelope-alt', + titleKey: 'Contact.email', + value: 'hello@pintumasuk.id', + }, + { + icon: 'uil-phone', + titleKey: 'Contact.phone', + value: '+62 812-3456-7890', + }, + { + icon: 'uil-map-marker', + titleKey: 'Contact.office', + valueKey: 'Contact.address', + }, + ]; + + return ( +
+
+
+
+ + + {t('Contact.eyebrow')} + +

{t('Contact.title')}

+

{t('Contact.desc')}

+ +
+ {contactList.map((item, i) => ( +
+
+ +
+
+ {(t as any)(item.titleKey)} + {item.valueKey ? (t as any)(item.valueKey) : item.value} +
+
+ ))} +
+
+ +
+

{t('Contact.form.title')}

+
e.preventDefault()}> +
+
+ + +
+
+ + +
+
+
+ + +
+ +

{t('Contact.form.note')}

+
+
+
+
+
+ ); +} diff --git a/apps/product-site/src/components/sections/Dashboard.tsx b/apps/product-site/src/components/sections/Dashboard.tsx new file mode 100644 index 0000000..cb09344 --- /dev/null +++ b/apps/product-site/src/components/sections/Dashboard.tsx @@ -0,0 +1,101 @@ +import { useTranslations } from 'next-intl'; + +export function Dashboard() { + const t = useTranslations(); + + return ( +
+
+
+

+ {t('Destinations.title21')} +
+ {t('Destinations.title22')} +

+

{t('Destinations.desc2')}

+
+ + + + + + + + + + +
+
+
+
+ +
+

{t('Destinations.apps.online.title')}

+

{t('Destinations.apps.online.desc')}

+
+ +
+
+ +
+

{t('Destinations.apps.cashier.title')}

+

{t('Destinations.apps.cashier.desc')}

+
+
+ +
+ + + + + + + +
+ +
+
+ +
+

{t('Destinations.apps.pos.title')}

+

{t('Destinations.apps.pos.desc')}

+
+ +
+ + + + + + +
+ +
+
+ +
+

{t('Destinations.apps.queue.title')}

+

{t('Destinations.apps.queue.desc')}

+
+ +
+ + + + + + +
+ +
+
+ +
+

{t('Destinations.apps.admin.title')}

+

{t('Destinations.apps.admin.desc')}

+
+
+
+
+ ); +} diff --git a/apps/product-site/src/components/sections/Destinations.tsx b/apps/product-site/src/components/sections/Destinations.tsx new file mode 100644 index 0000000..ec7a04b --- /dev/null +++ b/apps/product-site/src/components/sections/Destinations.tsx @@ -0,0 +1,59 @@ +import { useTranslations } from 'next-intl'; +import { DestinationsCarousel } from '@/components/DestinationsCarousel'; + +export function Destinations() { + const t = useTranslations(); + + const destinationsData = [ + { + icon: 'uil-trees', + titleKey: 'Destinations.items.themePark.title', + descKey: 'Destinations.items.themePark.desc', + }, + { + icon: 'uil-ticket', + titleKey: 'Destinations.items.event.title', + descKey: 'Destinations.items.event.desc', + }, + { + icon: 'uil-archway', + titleKey: 'Destinations.items.museum.title', + descKey: 'Destinations.items.museum.desc', + }, + { + icon: 'uil-basketball', + titleKey: 'Destinations.items.stadium.title', + descKey: 'Destinations.items.stadium.desc', + }, + { + icon: 'uil-scenery', + titleKey: 'Destinations.items.exhibition.title', + descKey: 'Destinations.items.exhibition.desc', + }, + ]; + + return ( +
+
+ +

{t('Destinations.title')}

+

{t('Destinations.desc')}

+
+ } + > + {destinationsData.map((item, i) => ( +
+

{(t as any)(item.titleKey)}

+

{(t as any)(item.descKey)}

+
+ +
+
+ ))} + + +
+ ); +} diff --git a/apps/product-site/src/components/sections/Ecosystem.tsx b/apps/product-site/src/components/sections/Ecosystem.tsx new file mode 100644 index 0000000..d37da51 --- /dev/null +++ b/apps/product-site/src/components/sections/Ecosystem.tsx @@ -0,0 +1,194 @@ +'use client'; + +import { useTranslations } from 'next-intl'; +import { useEffect } from 'react'; + +export function Ecosystem() { + const t = useTranslations(); + + useEffect(() => { + const grid = document.getElementById('bentoGrid'); + let obs: IntersectionObserver; + + if (grid) { + const bars = grid.querySelectorAll('.report-bar'); + const ring = grid.querySelector('.ring-progress'); + let done = false; + + obs = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && !done) { + done = true; + bars.forEach((bar, i) => { + setTimeout(() => { + if (bar.dataset.w) bar.style.width = bar.dataset.w; + }, i * 150); + }); + if (ring && ring.dataset.dash) { + setTimeout(() => { + ring.style.strokeDasharray = ring.dataset.dash!; + }, 200); + } + obs.disconnect(); + } + }); + }, + { threshold: 0.35 }, + ); + obs.observe(grid); + } + + return () => { + if (obs) obs.disconnect(); + }; + }, []); + + return ( +
+
+
+

{t('Ecosystem.title')}

+

{t('Ecosystem.desc')}

+
+ +
+ {/* Ticketing */} +
+

{t('Ecosystem.ticketing.title')}

+

{t('Ecosystem.ticketing.desc')}

+
+
+
+
+ TX24409198-8798 + TX24409198-8797 + TX24409198-8798 + TX24409198-8901 + TX24409198-8811 +
+
+
+
+ TX24409198-8801 + TX24409198-8787 + TX24409198-8786 + TX24409198-8901 + TX24409198-8881 +
+
+
+
+ TX24409198-8902 + TX24409198-8788 + TX24409198-8790 + TX24409198-8905 + TX24409198-8812 +
+
+
+
+
+ + {/* Access Management */} +
+
+
+
+ {t('Ecosystem.stats.qr')} + +
+
+ {t('Ecosystem.stats.gate')} + +
+
+ {t('Ecosystem.stats.rfid')} + +
+
+
+ + + + + + + + + + + +
+ 0.4s + {t('Ecosystem.stats.avg')} +
+
+
+

{t('Ecosystem.access.title')}

+

{t('Ecosystem.access.desc')}

+
+ + {/* Visitor Operations */} +
+
+
+
+
+
+
+
+
+ {t('Ecosystem.stats.queue')} +
+
+
+
+
+
+
+
+
+
+
+
+

{t('Ecosystem.visitor.title')}

+

{t('Ecosystem.visitor.desc')}

+
+ + {/* Report */} +
+

{t('Ecosystem.report.title')}

+

{t('Ecosystem.report.desc')}

+
+ {[ + { w: '150px', bg: '#b9f001', val: 'Rp32.5M', key: 'Ecosystem.stats.revenue' }, + { w: '220px', bg: '#f66b0e', val: '1,284', key: 'Ecosystem.stats.visitors' }, + { w: '300px', bg: '#112b3c', val: '1,120,240', key: 'Ecosystem.stats.checkin' }, + ].map((item, idx) => ( +
+
+
+ {item.val} + {(t as any)(item.key)} +
+
+ ))} +
+
+
+
+
+ ); +} diff --git a/apps/product-site/src/components/sections/Footer.tsx b/apps/product-site/src/components/sections/Footer.tsx new file mode 100644 index 0000000..8338d87 --- /dev/null +++ b/apps/product-site/src/components/sections/Footer.tsx @@ -0,0 +1,67 @@ +import { useTranslations } from 'next-intl'; + +export function Footer() { + const t = useTranslations(); + + const footerLinks = [ + { labelKey: 'Nav.home', href: '#' }, + { labelKey: 'Nav.solutions', href: '#ecosystem' }, + { labelKey: 'Nav.industries', href: '#destinations' }, + { labelKey: 'Nav.pricing', href: '#pricing' }, + { labelKey: 'Nav.contact', href: '#contact' }, + ]; + + const socialLinks = [ + { icon: 'uil-envelope-alt', href: '#' }, + { icon: 'uil-phone', href: '#' }, + { icon: 'uil-youtube', href: '#' }, + ]; + + return ( + + ); +} diff --git a/apps/product-site/src/components/sections/Hero.tsx b/apps/product-site/src/components/sections/Hero.tsx new file mode 100644 index 0000000..432e4fe --- /dev/null +++ b/apps/product-site/src/components/sections/Hero.tsx @@ -0,0 +1,85 @@ +import { useTranslations } from 'next-intl'; + +export function Hero() { + const t = useTranslations(); + + const statsData = [ + { + icon: 'uil-clock-eight', + labelKey: 'Hero.stats.timeAccess', + value: ( + <> + 0.4{t('Hero.stats.seconds')} + + ), + subKey: 'Hero.stats.avgValidation', + }, + { + icon: 'uil-ticket', + labelKey: 'Hero.stats.ticketsSold', + value: '1.249', + subKey: 'Hero.stats.todayAllChannels', + }, + { + icon: 'uil-sync', + labelKey: 'Hero.stats.sync', + value: '12', + subKey: 'Hero.stats.txSynced', + }, + { + icon: 'uil-sliders-v', + labelKey: 'Hero.stats.visitorOps', + bars: [40, 70, 55, 90], + subKey: 'Hero.stats.activeZones', + }, + ]; + + return ( +
+
+
+

+ {t('Hero.title1')} +
+ {t('Hero.title2')} +

+ +
+ {statsData.map((stat, i) => ( +
+
+ +
+
{(t as any)(stat.labelKey)}
+ + {stat.bars ? ( +
+ {stat.bars.map((h, j) => ( + + ))} +
+ ) : ( +
{stat.value}
+ )} + +
{(t as any)(stat.subKey)}
+
+ ))} +
+ +
+ + + {t('Hero.subtitle')} + +

{t('Hero.desc')}

+ + {t('Hero.cta')} + + +
+
+
+
+ ); +} diff --git a/apps/product-site/src/components/sections/Navbar.tsx b/apps/product-site/src/components/sections/Navbar.tsx new file mode 100644 index 0000000..d7c8591 --- /dev/null +++ b/apps/product-site/src/components/sections/Navbar.tsx @@ -0,0 +1,70 @@ +'use client'; + +import { useTranslations } from 'next-intl'; +import { LanguageSwitcher } from '@/components/LanguageSwitcher'; +import { useEffect, useState } from 'react'; + +export function Navbar() { + const t = useTranslations(); + const [isScrolled, setIsScrolled] = useState(false); + const [isMenuOpen, setIsMenuOpen] = useState(false); + + useEffect(() => { + const handleScroll = () => { + const floating = window.scrollY > 60; + setIsScrolled(floating); + document.body.classList.toggle('nav-is-floating', floating); + }; + window.addEventListener('scroll', handleScroll, { passive: true }); + handleScroll(); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, []); + + return ( +
+
+ +
+
+ ); +} diff --git a/apps/product-site/src/components/sections/Pricing.tsx b/apps/product-site/src/components/sections/Pricing.tsx new file mode 100644 index 0000000..a084d6b --- /dev/null +++ b/apps/product-site/src/components/sections/Pricing.tsx @@ -0,0 +1,71 @@ +import { useTranslations } from 'next-intl'; + +export function Pricing() { + const t = useTranslations(); + + const pricingPlans = [ + { + key: 'rental', + tags: [0, 1, 2], + features: ['f1', 'f2', 'f3', 'f4', 'f5'], + }, + { + key: 'subscription', + tags: [0, 1], + features: ['f1', 'f2', 'f3', 'f4', 'f5'], + }, + { + key: 'onPremise', + tags: [0, 1], + features: ['f1', 'f2', 'f3', 'f4', 'f5'], + }, + { + key: 'buyOut', + tags: [0, 1, 2], + features: ['f1', 'f2', 'f3', 'f4', 'f5'], + }, + ]; + + return ( +
+
+
+

+ {t('Pricing.title1')} +
+ {t('Pricing.title2')} +

+

{t('Pricing.desc')}

+
+ +
+ {pricingPlans.map((plan) => ( +
+

{(t as any)(`Pricing.${plan.key}.title`)}

+
+ {plan.tags.map((tagIndex) => ( + + {(t as any)(`Pricing.${plan.key}.tags.${tagIndex}`)} + + ))} +
+

{(t as any)(`Pricing.${plan.key}.desc`)}

+ + {t('Pricing.contactUs')} + + {t('Pricing.whatYouGet')} +
    + {plan.features.map((featKey) => ( +
  • + + {(t as any)(`Pricing.${plan.key}.${featKey}`)} +
  • + ))} +
+
+ ))} +
+
+
+ ); +} diff --git a/apps/product-site/src/components/sections/Why.tsx b/apps/product-site/src/components/sections/Why.tsx new file mode 100644 index 0000000..b06e418 --- /dev/null +++ b/apps/product-site/src/components/sections/Why.tsx @@ -0,0 +1,49 @@ +import { useTranslations } from 'next-intl'; + +export function Why() { + const t = useTranslations(); + + const reasonsData = [ + { + icon: 'uil-wifi-slash', + titleKey: 'Why.items.offline.title', + descKey: 'Why.items.offline.desc', + }, + { + icon: 'uil-bolt', + titleKey: 'Why.items.speed.title', + descKey: 'Why.items.speed.desc', + }, + { + icon: 'uil-expand-arrows-alt', + titleKey: 'Why.items.flexibility.title', + descKey: 'Why.items.flexibility.desc', + }, + ]; + + return ( +
+
+
+

{t('Why.title')}

+

{t('Why.desc')}

+
+ +
+
+ +
+ {reasonsData.map((reason, i) => ( +
+
+ +
+

{(t as any)(reason.titleKey)}

+

{(t as any)(reason.descKey)}

+
+ ))} +
+
+
+ ); +}