feat: add product section components and integrate them into the homepage landing page

This commit is contained in:
Firman Ramdhani
2026-07-29 15:50:27 +07:00
parent b43df8f35b
commit db57a22a09
10 changed files with 796 additions and 772 deletions
+20 -772
View File
@@ -1,794 +1,42 @@
'use client'; import { Navbar } from '@/components/sections/Navbar';
import { Hero } from '@/components/sections/Hero';
import { useTranslations } from 'next-intl'; import { Why } from '@/components/sections/Why';
import { LanguageSwitcher } from '@/components/LanguageSwitcher'; import { Ecosystem } from '@/components/sections/Ecosystem';
import { useEffect, useState } from 'react'; import { Destinations } from '@/components/sections/Destinations';
import { DestinationsCarousel } from '@/components/DestinationsCarousel'; 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() { 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<HTMLElement>('.report-bar');
const ring = grid.querySelector<SVGElement>('.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 ( return (
<main> <main>
{/* ============ FIXED NAV ============ */} {/* ============ FIXED NAV ============ */}
<div className={`nav-wrapper ${isScrolled ? 'floating' : ''}`}> <Navbar />
<div className="container">
<nav className="nav">
<div className="brand">
<a href="#">
<div className="brand-mark">
<img src="/brand/logo-primary.svg" alt="" />
</div>
</a>
</div>
<button className="nav-toggle" aria-label="Open menu" onClick={() => setIsMenuOpen(!isMenuOpen)}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M4 7h16M4 12h16M4 17h16" stroke="#fff" strokeWidth="2" strokeLinecap="round" />
</svg>
</button>
<div className={`nav-links ${isMenuOpen ? 'open' : ''}`} id="navLinks" onClick={() => setIsMenuOpen(false)}>
<a href="#" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.home')}
</a>
<a href="#ecosystem" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.solutions')}
</a>
<a href="#destinations" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.industries')}
</a>
<a href="#pricing" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.pricing')}
</a>
<a href="#contact" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.contact')}
</a>
<a href="#contact" className="btn btn-orange">
{t('Nav.demo')}
</a>
<div className="nav-divider"></div>
<div onClick={(e) => e.stopPropagation()}>
<LanguageSwitcher />
</div>
</div>
</nav>
</div>
</div>
<header className="hero"> {/* ============ HERO ============ */}
<div className="container"> <Hero />
<div className="hero-inner">
<h1>
{t('Hero.title1')}
<br />
{t('Hero.title2')}
</h1>
<div className="stat-row">
<div className="stat-card">
<div className="stat-icon">
<i className="uil uil-clock-eight"></i>
</div>
<div className="stat-label">{t('Hero.stats.timeAccess')}</div>
<div className="stat-value">
0.4<span>{t('Hero.stats.seconds')}</span>
</div>
<div className="stat-sub">{t('Hero.stats.avgValidation')}</div>
</div>
<div className="stat-card">
<div className="stat-icon">
<i className="uil uil-ticket"></i>
</div>
<div className="stat-label">{t('Hero.stats.ticketsSold')}</div>
<div className="stat-value">1.249</div>
<div className="stat-sub">{t('Hero.stats.todayAllChannels')}</div>
</div>
<div className="stat-card">
<div className="stat-icon">
<i className="uil uil-sync"></i>
</div>
<div className="stat-label">{t('Hero.stats.sync')}</div>
<div className="stat-value">12</div>
<div className="stat-sub">{t('Hero.stats.txSynced')}</div>
</div>
<div className="stat-card">
<div className="stat-icon">
<i className="uil uil-sliders-v"></i>
</div>
<div className="stat-label">{t('Hero.stats.visitorOps')}</div>
<div className="stat-bars" style={{ marginTop: '12px' }}>
<span style={{ height: '40%' }}></span>
<span style={{ height: '70%' }}></span>
<span style={{ height: '55%' }}></span>
<span style={{ height: '90%' }}></span>
</div>
<div className="stat-sub">{t('Hero.stats.activeZones')}</div>
</div>
</div>
<div className="hero-body">
<span className="eyebrow eyebrow-light eyebrow-pill">
<span className="dot"></span>
{t('Hero.subtitle')}
</span>
<p>{t('Hero.desc')}</p>
<a href="#why" className="btn btn-navy">
{t('Hero.cta')}
<i className="uil uil-arrow-down-right"></i>
</a>
</div>
</div>
</div>
</header>
{/* ============ WHY SECTION ============ */} {/* ============ WHY SECTION ============ */}
<section className="why" id="why"> <Why />
<div className="container why-grid">
<div className="why-sticky">
<h2>{t('Why.title')}</h2>
<p>{t('Why.desc')}</p>
<div className="why-arrow">
<i className="uil uil-arrow-up-right"></i>
</div>
</div>
<div className="why-cards">
<div className="why-card">
<div className="why-card-icon">
<i className="uil uil-wifi-slash"></i>
</div>
<h3>{t('Why.items.offline.title')}</h3>
<p>{t('Why.items.offline.desc')}</p>
</div>
<div className="why-card">
<div className="why-card-icon">
<i className="uil uil-bolt"></i>
</div>
<h3>{t('Why.items.speed.title')}</h3>
<p>{t('Why.items.speed.desc')}</p>
</div>
<div className="why-card">
<div className="why-card-icon">
<i className="uil uil-expand-arrows-alt"></i>
</div>
<h3>{t('Why.items.flexibility.title')}</h3>
<p>{t('Why.items.flexibility.desc')}</p>
</div>
</div>
</div>
</section>
{/* ============ ECOSYSTEM / BENTO ============ */} {/* ============ ECOSYSTEM / BENTO ============ */}
<section className="ecosystem" id="ecosystem"> <Ecosystem />
<div className="container">
<div className="ecosystem-head">
<h2>{t('Ecosystem.title')}</h2>
<p>{t('Ecosystem.desc')}</p>
</div>
<div className="bento" id="bentoGrid">
{/* Ticketing */}
<div className="bento-card bento-r1">
<h3>{t('Ecosystem.ticketing.title')}</h3>
<p>{t('Ecosystem.ticketing.desc')}</p>
<div className="bento-illustration">
<div className="ticket-rows">
<div className="ticket-row">
<div className="ticket-track drift-a" style={{ marginLeft: '-14px' }}>
<span className="pill pill-outline">TX24409198-8798</span>
<span className="pill pill-orange">TX24409198-8797</span>
<span className="pill pill-lime">TX24409198-8798</span>
<span className="pill pill-outline">TX24409198-8901</span>
<span className="pill pill-navy">TX24409198-8811</span>
</div>
</div>
<div className="ticket-row">
<div className="ticket-track drift-b" style={{ marginLeft: '-32px' }}>
<span className="pill pill-lime">TX24409198-8801</span>
<span className="pill pill-outline">TX24409198-8787</span>
<span className="pill pill-navy">TX24409198-8786</span>
<span className="pill pill-outline">TX24409198-8901</span>
<span className="pill pill-outline">TX24409198-8881</span>
</div>
</div>
<div className="ticket-row">
<div className="ticket-track drift-a" style={{ marginLeft: '-22px', animationDelay: '-1.4s' }}>
<span className="pill pill-outline">TX24409198-8902</span>
<span className="pill pill-lime">TX24409198-8788</span>
<span className="pill pill-outline">TX24409198-8790</span>
<span className="pill pill-orange">TX24409198-8905</span>
<span className="pill pill-outline">TX24409198-8812</span>
</div>
</div>
</div>
</div>
</div>
{/* Access Management */}
<div className="bento-card bento-r2">
<div className="bento-illustration access-illustration">
<div className="chip-row">
<div className="chip active">
<span>{t('Ecosystem.stats.qr')}</span>
<i className="uil uil-qrcode-scan"></i>
</div>
<div className="chip">
<span>{t('Ecosystem.stats.gate')}</span>
<i className="uil uil-monitor-heart-rate"></i>
</div>
<div className="chip">
<span>{t('Ecosystem.stats.rfid')}</span>
<i className="uil uil-rss-interface"></i>
</div>
</div>
<div className="ring-wrap">
<svg width="140" height="140" viewBox="0 0 100 100">
<defs>
<linearGradient id="rainbowGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#f66b0e" />
<stop offset="70%" stopColor="#112b3c" />
<stop offset="100%" stopColor="#205375" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="34" fill="none" stroke="#E1E5E3" strokeWidth="9" />
<circle
className="ring-progress"
cx="50"
cy="50"
r="34"
fill="none"
stroke="url(#rainbowGrad)"
strokeWidth="9"
strokeLinecap="round"
data-dash="166 213.6"
transform="rotate(-90 50 50)"
/>
</svg>
<div className="ring-text">
<b>0.4s</b>
<small>{t('Ecosystem.stats.avg')}</small>
</div>
</div>
</div>
<h3>{t('Ecosystem.access.title')}</h3>
<p>{t('Ecosystem.access.desc')}</p>
</div>
{/* Visitor Operations */}
<div className="bento-card bento-r3">
<div className="bento-illustration visitor-illustration">
<div className="avatar-row">
<div className="avatar-stack">
<div className="avatar" style={{ background: '#b9f001' }}></div>
<div className="avatar" style={{ background: '#f66b0e' }}></div>
<div className="avatar" style={{ background: '#112b3c' }}></div>
<div className="avatar" style={{ background: '#e1e5e3' }}></div>
</div>
<span>{t('Ecosystem.stats.queue')}</span>
</div>
<div className="zone-grid">
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#205375' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#b9f001' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: 'linear-gradient(135deg, #205375, #112b3c)' }}></div>
</div>
</div>
<h3>{t('Ecosystem.visitor.title')}</h3>
<p>{t('Ecosystem.visitor.desc')}</p>
</div>
{/* Report */}
<div className="bento-card bento-r4">
<h3 style={{ marginBottom: '8px' }}>{t('Ecosystem.report.title')}</h3>
<p style={{ marginBottom: '26px' }}>{t('Ecosystem.report.desc')}</p>
<div className="report-bars">
<div className="report-bar-row">
<div className="report-bar" data-w="150px" style={{ background: '#b9f001' }}></div>
<div className="report-bar-value">
<b>Rp32.5M</b>
<small>{t('Ecosystem.stats.revenue')}</small>
</div>
</div>
<div className="report-bar-row">
<div className="report-bar" data-w="220px" style={{ background: '#f66b0e' }}></div>
<div className="report-bar-value">
<b>1,284</b>
<small>{t('Ecosystem.stats.visitors')}</small>
</div>
</div>
<div className="report-bar-row">
<div className="report-bar" data-w="300px" style={{ background: '#112b3c' }}></div>
<div className="report-bar-value">
<b>1,120,240</b>
<small>{t('Ecosystem.stats.checkin')}</small>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{/* ============ DESTINATIONS (carousel) ============ */} {/* ============ DESTINATIONS (carousel) ============ */}
<section className="destinations" id="destinations"> <Destinations />
<div className="container">
<DestinationsCarousel
header={
<div className="destinations-head">
<h2>{t('Destinations.title')}</h2>
<p>{t('Destinations.desc')}</p>
</div>
}
>
<div className="dest-card">
<h4>{t('Destinations.items.themePark.title')}</h4>
<p>{t('Destinations.items.themePark.desc')}</p>
<div className="dest-icon">
<i className="uil uil-trees"></i>
</div>
</div>
<div className="dest-card">
<h4>{t('Destinations.items.event.title')}</h4>
<p>{t('Destinations.items.event.desc')}</p>
<div className="dest-icon">
<i className="uil uil-ticket"></i>
</div>
</div>
<div className="dest-card">
<h4>{t('Destinations.items.museum.title')}</h4>
<p>{t('Destinations.items.museum.desc')}</p>
<div className="dest-icon">
<i className="uil uil-archway"></i>
</div>
</div>
<div className="dest-card">
<h4>{t('Destinations.items.stadium.title')}</h4>
<p>{t('Destinations.items.stadium.desc')}</p>
<div className="dest-icon">
<i className="uil uil-basketball"></i>
</div>
</div>
<div className="dest-card">
<h4>{t('Destinations.items.exhibition.title')}</h4>
<p>{t('Destinations.items.exhibition.desc')}</p>
<div className="dest-icon">
<i className="uil uil-scenery"></i>
</div>
</div>
</DestinationsCarousel>
</div>
</section>
<section className="dash-showcase" id="dashboard"> {/* ============ DASHBOARD ============ */}
<div className="container"> <Dashboard />
<div className="dash-head">
<h2>
{t('Destinations.title21')}
<br />
{t('Destinations.title22')}
</h2>
<p>{t('Destinations.desc2')}</p>
</div>
<svg width="0" height="0" style={{ position: 'absolute' }}>
<defs>
<linearGradient id="flowGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" style={{ stopColor: 'var(--navy-900)' }} />
<stop offset="100%" style={{ stopColor: 'var(--color-brand-2-500)' }} />
</linearGradient>
</defs>
</svg>
<div className="dash-flow">
<div className="dash-flow-row">
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-calendar-alt"></i>
</div>
<h4>{t('Destinations.apps.online.title')}</h4>
<p>{t('Destinations.apps.online.desc')}</p>
</div>
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-money-stack"></i>
</div>
<h4>{t('Destinations.apps.cashier.title')}</h4>
<p>{t('Destinations.apps.cashier.desc')}</p>
</div>
</div>
<div className="dash-flow-merge">
<svg className="flow-curve" viewBox="0 0 60 100" preserveAspectRatio="none">
<path className="flow-dash" d="M0,24 C 32,24 20,50 58,50" />
<path className="flow-dash" d="M0,76 C 32,76 20,50 58,50" />
{/* <path className="flow-head" d="M52,46 L59,50 L52,54" /> */}
</svg>
<span className="flow-simple">
<i className="uil uil-angle-double-down"></i>
</span>
</div>
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-newspaper"></i>
</div>
<h4>{t('Destinations.apps.pos.title')}</h4>
<p>{t('Destinations.apps.pos.desc')}</p>
</div>
<div className="dash-flow-arrow">
<svg className="flow-curve" viewBox="0 0 44 20" preserveAspectRatio="none">
<path className="flow-dash" d="M1,10 C 14,1 20,19 43,11" />
{/* <path className="flow-head" d="M28,3 L36,10 L28,17" /> */}
</svg>
<span className="flow-simple">
<i className="uil uil-angle-double-down"></i>
</span>
</div>
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-users-alt"></i>
</div>
<h4>{t('Destinations.apps.queue.title')}</h4>
<p>{t('Destinations.apps.queue.desc')}</p>
</div>
<div className="dash-flow-arrow">
<svg className="flow-curve" viewBox="0 0 44 20" preserveAspectRatio="none">
<path className="flow-dash" d="M1,10 C 14,1 20,19 43,11 " />
{/* <path className="flow-head" d="M28,3 L36,10 L28,17" /> */}
</svg>
<span className="flow-simple">
<i className="uil uil-angle-double-down"></i>
</span>
</div>
<div className="dash-source-card wide accent">
<div className="dash-source-icon">
<i className="uil uil-desktop"></i>
</div>
<h4>{t('Destinations.apps.admin.title')}</h4>
<p>{t('Destinations.apps.admin.desc')}</p>
</div>
</div>
</div>
</section>
{/* ============ PRICING ============ */} {/* ============ PRICING ============ */}
<section className="pricing" id="pricing"> <Pricing />
<div className="container-lg">
<div className="pricing-head">
<h2>
{t('Pricing.title1')}
<br />
{t('Pricing.title2')}
</h2>
<p>{t('Pricing.desc')}</p>
</div>
<div className="pricing-grid">
<div className="price-card">
<h3>{t('Pricing.rental.title')}</h3>
<div className="price-tags">
<span className="tag">{t('Pricing.rental.tags.0')}</span>
<span className="tag">{t('Pricing.rental.tags.1')}</span>
<span className="tag">{t('Pricing.rental.tags.2')}</span>
</div>
<p className="price-desc">{t('Pricing.rental.desc')}</p>
<a href="#contact" className="btn btn-orange">
{t('Pricing.contactUs')}
</a>
<span className="feat-label">{t('Pricing.whatYouGet')}</span>
<ul className="feat-list">
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.rental.f1')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.rental.f2')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.rental.f3')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.rental.f4')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.rental.f5')}
</li>
</ul>
</div>
<div className="price-card">
<h3>{t('Pricing.subscription.title')}</h3>
<div className="price-tags">
<span className="tag">{t('Pricing.subscription.tags.0')}</span>
<span className="tag">{t('Pricing.subscription.tags.1')}</span>
</div>
<p className="price-desc">{t('Pricing.subscription.desc')}</p>
<a href="#contact" className="btn btn-orange">
{t('Pricing.contactUs')}
</a>
<span className="feat-label">{t('Pricing.whatYouGet')}</span>
<ul className="feat-list">
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.subscription.f1')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.subscription.f2')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.subscription.f3')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.subscription.f4')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.subscription.f5')}
</li>
</ul>
</div>
<div className="price-card">
<h3>{t('Pricing.onPremise.title')}</h3>
<div className="price-tags">
<span className="tag">{t('Pricing.onPremise.tags.0')}</span>
<span className="tag">{t('Pricing.onPremise.tags.1')}</span>
</div>
<p className="price-desc">{t('Pricing.onPremise.desc')}</p>
<a href="#contact" className="btn btn-orange">
{t('Pricing.contactUs')}
</a>
<span className="feat-label">{t('Pricing.whatYouGet')}</span>
<ul className="feat-list">
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.onPremise.f1')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.onPremise.f2')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.onPremise.f3')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.onPremise.f4')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.onPremise.f5')}
</li>
</ul>
</div>
<div className="price-card">
<h3>{t('Pricing.buyOut.title')}</h3>
<div className="price-tags">
<span className="tag">{t('Pricing.buyOut.tags.0')}</span>
<span className="tag">{t('Pricing.buyOut.tags.1')}</span>
<span className="tag">{t('Pricing.buyOut.tags.2')}</span>
</div>
<p className="price-desc">{t('Pricing.buyOut.desc')}</p>
<a href="#contact" className="btn btn-orange">
{t('Pricing.contactUs')}
</a>
<span className="feat-label">{t('Pricing.whatYouGet')}</span>
<ul className="feat-list">
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.buyOut.f1')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.buyOut.f2')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.buyOut.f3')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.buyOut.f4')}
</li>
<li>
<i className="uil uil-check-circle"></i>
{t('Pricing.buyOut.f5')}
</li>
</ul>
</div>
</div>
</div>
</section>
{/* ============ CONTACT ============ */} {/* ============ CONTACT ============ */}
<section className="contact" id="contact"> <Contact />
<div className="container">
<div className="contact-card">
<div className="contact-info">
<span className="eyebrow eyebrow-light eyebrow-pill">
<span className="dot"></span>
{t('Contact.eyebrow')}
</span>
<h2 style={{ marginTop: '14px' }}>{t('Contact.title')}</h2>
<p>{t('Contact.desc')}</p>
<div className="contact-list">
<div className="contact-item">
<div className="ci-icon">
<i className="uil uil-envelope-alt"></i>
</div>
<div>
<b>{t('Contact.email')}</b>
<span>hello@pintumasuk.id</span>
</div>
</div>
<div className="contact-item">
<div className="ci-icon">
<i className="uil uil-phone"></i>
</div>
<div>
<b>{t('Contact.phone')}</b>
<span>+62 812-3456-7890</span>
</div>
</div>
<div className="contact-item">
<div className="ci-icon">
<i className="uil uil-map-marker"></i>
</div>
<div>
<b>{t('Contact.office')}</b>
<span>{t('Contact.address')}</span>
</div>
</div>
</div>
</div>
<div className="contact-form">
<h3>{t('Contact.form.title')}</h3>
<form onSubmit={(e) => e.preventDefault()}>
<div className="field-row">
<div className="field">
<label htmlFor="cname">{t('Contact.form.name')}</label>
<input id="cname" type="text" placeholder={t('Contact.form.placeholderName')} />
</div>
<div className="field">
<label htmlFor="cemail">{t('Contact.email')}</label>
<input id="cemail" type="email" placeholder={t('Contact.form.placeholderEmail')} />
</div>
</div>
<div className="field">
<label htmlFor="cmsg">{t('Contact.form.needs')}</label>
<textarea id="cmsg" placeholder={t('Contact.form.placeholderNeeds')}></textarea>
</div>
<button type="submit" className="btn btn-orange">
{t('Contact.form.submit')}
</button>
<p className="form-note">{t('Contact.form.note')}</p>
</form>
</div>
</div>
</div>
</section>
{/* ============ FOOTER ============ */} {/* ============ FOOTER ============ */}
<footer className="footer"> <Footer />
<div className="container">
<div className="footer-top">
<div className="footer-brand">
<div className="brand">
<a href="#">
<div className="brand-mark">
<img src="/brand/logo-primary.svg" alt="" />
</div>
</a>
<span className="brand-name" style={{ color: '#fff' }}></span>
</div>
<p>{t('Footer.desc')}</p>
<p className="address">{t('Contact.address')}</p>
</div>
<div className="footer-cols">
<div className="footer-col">
<h5>{t('Footer.links')}</h5>
<a href="#">{t('Nav.home')}</a>
<a href="#ecosystem">{t('Nav.solutions')}</a>
<a href="#destinations">{t('Nav.industries')}</a>
<a href="#pricing">{t('Nav.pricing')}</a>
<a href="#contact">{t('Nav.contact')}</a>
</div>
<div className="footer-col">
<h5>{t('Nav.contact')}</h5>
<a href="mailto:hello@pintumasuk.id">hello@pintumasuk.id</a>
<a href="tel:+6281234567890">+62 812-3456-7890</a>
</div>
</div>
</div>
<div className="footer-bottom">
<p>
© <span id="current-year"></span> {t('Footer.copyright')}
</p>
<div className="footer-social">
<a href="#">
<i className="uil uil-envelope-alt"></i>
</a>
<a href="#">
<i className="uil uil-phone"></i>
</a>
<a href="#">
<i className="uil uil-youtube"></i>
</a>
</div>
</div>
</div>
</footer>
</main> </main>
); );
} }
@@ -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 (
<section className="contact" id="contact">
<div className="container">
<div className="contact-card">
<div className="contact-info">
<span className="eyebrow eyebrow-light eyebrow-pill">
<span className="dot"></span>
{t('Contact.eyebrow')}
</span>
<h2 style={{ marginTop: '14px' }}>{t('Contact.title')}</h2>
<p>{t('Contact.desc')}</p>
<div className="contact-list">
{contactList.map((item, i) => (
<div className="contact-item" key={i}>
<div className="ci-icon">
<i className={`uil ${item.icon}`}></i>
</div>
<div>
<b>{(t as any)(item.titleKey)}</b>
<span>{item.valueKey ? (t as any)(item.valueKey) : item.value}</span>
</div>
</div>
))}
</div>
</div>
<div className="contact-form">
<h3>{t('Contact.form.title')}</h3>
<form onSubmit={(e) => e.preventDefault()}>
<div className="field-row">
<div className="field">
<label htmlFor="cname">{t('Contact.form.name')}</label>
<input id="cname" type="text" placeholder={t('Contact.form.placeholderName')} />
</div>
<div className="field">
<label htmlFor="cemail">{t('Contact.email')}</label>
<input id="cemail" type="email" placeholder={t('Contact.form.placeholderEmail')} />
</div>
</div>
<div className="field">
<label htmlFor="cmsg">{t('Contact.form.needs')}</label>
<textarea id="cmsg" placeholder={t('Contact.form.placeholderNeeds')}></textarea>
</div>
<button type="submit" className="btn btn-orange">
{t('Contact.form.submit')}
</button>
<p className="form-note">{t('Contact.form.note')}</p>
</form>
</div>
</div>
</div>
</section>
);
}
@@ -0,0 +1,101 @@
import { useTranslations } from 'next-intl';
export function Dashboard() {
const t = useTranslations();
return (
<section className="dash-showcase" id="dashboard">
<div className="container">
<div className="dash-head">
<h2>
{t('Destinations.title21')}
<br />
{t('Destinations.title22')}
</h2>
<p>{t('Destinations.desc2')}</p>
</div>
<svg width="0" height="0" style={{ position: 'absolute' }}>
<defs>
<linearGradient id="flowGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" style={{ stopColor: 'var(--navy-900)' }} />
<stop offset="100%" style={{ stopColor: 'var(--color-brand-2-500)' }} />
</linearGradient>
</defs>
</svg>
<div className="dash-flow">
<div className="dash-flow-row">
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-calendar-alt"></i>
</div>
<h4>{t('Destinations.apps.online.title')}</h4>
<p>{t('Destinations.apps.online.desc')}</p>
</div>
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-money-stack"></i>
</div>
<h4>{t('Destinations.apps.cashier.title')}</h4>
<p>{t('Destinations.apps.cashier.desc')}</p>
</div>
</div>
<div className="dash-flow-merge">
<svg className="flow-curve" viewBox="0 0 60 100" preserveAspectRatio="none">
<path className="flow-dash" d="M0,24 C 32,24 20,50 58,50" />
<path className="flow-dash" d="M0,76 C 32,76 20,50 58,50" />
</svg>
<span className="flow-simple">
<i className="uil uil-angle-double-down"></i>
</span>
</div>
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-newspaper"></i>
</div>
<h4>{t('Destinations.apps.pos.title')}</h4>
<p>{t('Destinations.apps.pos.desc')}</p>
</div>
<div className="dash-flow-arrow">
<svg className="flow-curve" viewBox="0 0 44 20" preserveAspectRatio="none">
<path className="flow-dash" d="M1,10 C 14,1 20,19 43,11" />
</svg>
<span className="flow-simple">
<i className="uil uil-angle-double-down"></i>
</span>
</div>
<div className="dash-source-card wide">
<div className="dash-source-icon">
<i className="uil uil-users-alt"></i>
</div>
<h4>{t('Destinations.apps.queue.title')}</h4>
<p>{t('Destinations.apps.queue.desc')}</p>
</div>
<div className="dash-flow-arrow">
<svg className="flow-curve" viewBox="0 0 44 20" preserveAspectRatio="none">
<path className="flow-dash" d="M1,10 C 14,1 20,19 43,11 " />
</svg>
<span className="flow-simple">
<i className="uil uil-angle-double-down"></i>
</span>
</div>
<div className="dash-source-card wide accent">
<div className="dash-source-icon">
<i className="uil uil-desktop"></i>
</div>
<h4>{t('Destinations.apps.admin.title')}</h4>
<p>{t('Destinations.apps.admin.desc')}</p>
</div>
</div>
</div>
</section>
);
}
@@ -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 (
<section className="destinations" id="destinations">
<div className="container">
<DestinationsCarousel
header={
<div className="destinations-head">
<h2>{t('Destinations.title')}</h2>
<p>{t('Destinations.desc')}</p>
</div>
}
>
{destinationsData.map((item, i) => (
<div className="dest-card" key={i}>
<h4>{(t as any)(item.titleKey)}</h4>
<p>{(t as any)(item.descKey)}</p>
<div className="dest-icon">
<i className={`uil ${item.icon}`}></i>
</div>
</div>
))}
</DestinationsCarousel>
</div>
</section>
);
}
@@ -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<HTMLElement>('.report-bar');
const ring = grid.querySelector<SVGElement>('.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 (
<section className="ecosystem" id="ecosystem">
<div className="container">
<div className="ecosystem-head">
<h2>{t('Ecosystem.title')}</h2>
<p>{t('Ecosystem.desc')}</p>
</div>
<div className="bento" id="bentoGrid">
{/* Ticketing */}
<div className="bento-card bento-r1">
<h3>{t('Ecosystem.ticketing.title')}</h3>
<p>{t('Ecosystem.ticketing.desc')}</p>
<div className="bento-illustration">
<div className="ticket-rows">
<div className="ticket-row">
<div className="ticket-track drift-a" style={{ marginLeft: '-14px' }}>
<span className="pill pill-outline">TX24409198-8798</span>
<span className="pill pill-orange">TX24409198-8797</span>
<span className="pill pill-lime">TX24409198-8798</span>
<span className="pill pill-outline">TX24409198-8901</span>
<span className="pill pill-navy">TX24409198-8811</span>
</div>
</div>
<div className="ticket-row">
<div className="ticket-track drift-b" style={{ marginLeft: '-32px' }}>
<span className="pill pill-lime">TX24409198-8801</span>
<span className="pill pill-outline">TX24409198-8787</span>
<span className="pill pill-navy">TX24409198-8786</span>
<span className="pill pill-outline">TX24409198-8901</span>
<span className="pill pill-outline">TX24409198-8881</span>
</div>
</div>
<div className="ticket-row">
<div className="ticket-track drift-a" style={{ marginLeft: '-22px', animationDelay: '-1.4s' }}>
<span className="pill pill-outline">TX24409198-8902</span>
<span className="pill pill-lime">TX24409198-8788</span>
<span className="pill pill-outline">TX24409198-8790</span>
<span className="pill pill-orange">TX24409198-8905</span>
<span className="pill pill-outline">TX24409198-8812</span>
</div>
</div>
</div>
</div>
</div>
{/* Access Management */}
<div className="bento-card bento-r2">
<div className="bento-illustration access-illustration">
<div className="chip-row">
<div className="chip active">
<span>{t('Ecosystem.stats.qr')}</span>
<i className="uil uil-qrcode-scan"></i>
</div>
<div className="chip">
<span>{t('Ecosystem.stats.gate')}</span>
<i className="uil uil-monitor-heart-rate"></i>
</div>
<div className="chip">
<span>{t('Ecosystem.stats.rfid')}</span>
<i className="uil uil-rss-interface"></i>
</div>
</div>
<div className="ring-wrap">
<svg width="140" height="140" viewBox="0 0 100 100">
<defs>
<linearGradient id="rainbowGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#f66b0e" />
<stop offset="70%" stopColor="#112b3c" />
<stop offset="100%" stopColor="#205375" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="34" fill="none" stroke="#E1E5E3" strokeWidth="9" />
<circle
className="ring-progress"
cx="50"
cy="50"
r="34"
fill="none"
stroke="url(#rainbowGrad)"
strokeWidth="9"
strokeLinecap="round"
data-dash="166 213.6"
transform="rotate(-90 50 50)"
/>
</svg>
<div className="ring-text">
<b>0.4s</b>
<small>{t('Ecosystem.stats.avg')}</small>
</div>
</div>
</div>
<h3>{t('Ecosystem.access.title')}</h3>
<p>{t('Ecosystem.access.desc')}</p>
</div>
{/* Visitor Operations */}
<div className="bento-card bento-r3">
<div className="bento-illustration visitor-illustration">
<div className="avatar-row">
<div className="avatar-stack">
<div className="avatar" style={{ background: '#b9f001' }}></div>
<div className="avatar" style={{ background: '#f66b0e' }}></div>
<div className="avatar" style={{ background: '#112b3c' }}></div>
<div className="avatar" style={{ background: '#e1e5e3' }}></div>
</div>
<span>{t('Ecosystem.stats.queue')}</span>
</div>
<div className="zone-grid">
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#205375' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: '#b9f001' }}></div>
<div className="zone-cell" style={{ background: '#e1e5e3' }}></div>
<div className="zone-cell" style={{ background: 'linear-gradient(135deg, #205375, #112b3c)' }}></div>
</div>
</div>
<h3>{t('Ecosystem.visitor.title')}</h3>
<p>{t('Ecosystem.visitor.desc')}</p>
</div>
{/* Report */}
<div className="bento-card bento-r4">
<h3 style={{ marginBottom: '8px' }}>{t('Ecosystem.report.title')}</h3>
<p style={{ marginBottom: '26px' }}>{t('Ecosystem.report.desc')}</p>
<div className="report-bars">
{[
{ 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) => (
<div className="report-bar-row" key={idx}>
<div className="report-bar" data-w={item.w} style={{ background: item.bg }}></div>
<div className="report-bar-value">
<b>{item.val}</b>
<small>{(t as any)(item.key)}</small>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</section>
);
}
@@ -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 (
<footer className="footer">
<div className="container">
<div className="footer-top">
<div className="footer-brand">
<div className="brand">
<a href="#">
<div className="brand-mark">
<img src="/brand/logo-primary.svg" alt="" />
</div>
</a>
<span className="brand-name" style={{ color: '#fff' }}></span>
</div>
<p>{t('Footer.desc')}</p>
<p className="address">{t('Contact.address')}</p>
</div>
<div className="footer-cols">
<div className="footer-col">
<h5>{t('Footer.links')}</h5>
{footerLinks.map((link, i) => (
<a href={link.href} key={i}>
{(t as any)(link.labelKey)}
</a>
))}
</div>
<div className="footer-col">
<h5>{t('Nav.contact')}</h5>
<a href="mailto:hello@pintumasuk.id">hello@pintumasuk.id</a>
<a href="tel:+6281234567890">+62 812-3456-7890</a>
</div>
</div>
</div>
<div className="footer-bottom">
<p>
© <span>{new Date().getFullYear()}</span> {t('Footer.copyright')}
</p>
<div className="footer-social">
{socialLinks.map((social, i) => (
<a href={social.href} key={i}>
<i className={`uil ${social.icon}`}></i>
</a>
))}
</div>
</div>
</div>
</footer>
);
}
@@ -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<span>{t('Hero.stats.seconds')}</span>
</>
),
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 (
<header className="hero">
<div className="container">
<div className="hero-inner">
<h1>
{t('Hero.title1')}
<br />
{t('Hero.title2')}
</h1>
<div className="stat-row">
{statsData.map((stat, i) => (
<div className="stat-card" key={i}>
<div className="stat-icon">
<i className={`uil ${stat.icon}`}></i>
</div>
<div className="stat-label">{(t as any)(stat.labelKey)}</div>
{stat.bars ? (
<div className="stat-bars" style={{ marginTop: '12px' }}>
{stat.bars.map((h, j) => (
<span key={j} style={{ height: `${h}%` }}></span>
))}
</div>
) : (
<div className="stat-value">{stat.value}</div>
)}
<div className="stat-sub">{(t as any)(stat.subKey)}</div>
</div>
))}
</div>
<div className="hero-body">
<span className="eyebrow eyebrow-light eyebrow-pill">
<span className="dot"></span>
{t('Hero.subtitle')}
</span>
<p>{t('Hero.desc')}</p>
<a href="#why" className="btn btn-navy">
{t('Hero.cta')}
<i className="uil uil-arrow-down-right"></i>
</a>
</div>
</div>
</div>
</header>
);
}
@@ -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 (
<div className={`nav-wrapper ${isScrolled ? 'floating' : ''}`}>
<div className="container">
<nav className="nav">
<div className="brand">
<a href="#">
<div className="brand-mark">
<img src="/brand/logo-primary.svg" alt="" />
</div>
</a>
</div>
<button className="nav-toggle" aria-label="Open menu" onClick={() => setIsMenuOpen(!isMenuOpen)}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M4 7h16M4 12h16M4 17h16" stroke="#fff" strokeWidth="2" strokeLinecap="round" />
</svg>
</button>
<div className={`nav-links ${isMenuOpen ? 'open' : ''}`} id="navLinks" onClick={() => setIsMenuOpen(false)}>
<a href="#" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.home')}
</a>
<a href="#ecosystem" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.solutions')}
</a>
<a href="#destinations" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.industries')}
</a>
<a href="#pricing" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.pricing')}
</a>
<a href="#contact" style={{ color: '#fff', opacity: 0.85 }}>
{t('Nav.contact')}
</a>
<a href="#contact" className="btn btn-orange">
{t('Nav.demo')}
</a>
<div className="nav-divider"></div>
<div onClick={(e) => e.stopPropagation()}>
<LanguageSwitcher />
</div>
</div>
</nav>
</div>
</div>
);
}
@@ -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 (
<section className="pricing" id="pricing">
<div className="container-lg">
<div className="pricing-head">
<h2>
{t('Pricing.title1')}
<br />
{t('Pricing.title2')}
</h2>
<p>{t('Pricing.desc')}</p>
</div>
<div className="pricing-grid">
{pricingPlans.map((plan) => (
<div className="price-card" key={plan.key}>
<h3>{(t as any)(`Pricing.${plan.key}.title`)}</h3>
<div className="price-tags">
{plan.tags.map((tagIndex) => (
<span className="tag" key={tagIndex}>
{(t as any)(`Pricing.${plan.key}.tags.${tagIndex}`)}
</span>
))}
</div>
<p className="price-desc">{(t as any)(`Pricing.${plan.key}.desc`)}</p>
<a href="#contact" className="btn btn-orange">
{t('Pricing.contactUs')}
</a>
<span className="feat-label">{t('Pricing.whatYouGet')}</span>
<ul className="feat-list">
{plan.features.map((featKey) => (
<li key={featKey}>
<i className="uil uil-check-circle"></i>
{(t as any)(`Pricing.${plan.key}.${featKey}`)}
</li>
))}
</ul>
</div>
))}
</div>
</div>
</section>
);
}
@@ -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 (
<section className="why" id="why">
<div className="container why-grid">
<div className="why-sticky">
<h2>{t('Why.title')}</h2>
<p>{t('Why.desc')}</p>
<div className="why-arrow">
<i className="uil uil-arrow-up-right"></i>
</div>
</div>
<div className="why-cards">
{reasonsData.map((reason, i) => (
<div className="why-card" key={i}>
<div className="why-card-icon">
<i className={`uil ${reason.icon}`}></i>
</div>
<h3>{(t as any)(reason.titleKey)}</h3>
<p>{(t as any)(reason.descKey)}</p>
</div>
))}
</div>
</div>
</section>
);
}