Files
afriadirahman 8c6dad2a31
tests / ci (push) Canceled after 0s
Admin Panel
2026-09-29 14:57:37 +07:00

386 lines
19 KiB
TypeScript

import { Head, Link } from '@inertiajs/react';
import { ButtonLink } from '@/components/site/button';
import Card from '@/components/site/card';
import Eyebrow from '@/components/site/eyebrow';
import Glow, { GridPattern } from '@/components/site/glow';
import Icon from '@/components/site/icon';
import IconTile from '@/components/site/icon-tile';
import OdooPartnerBadge from '@/components/site/odoo-partner-badge';
import Pill from '@/components/site/pill';
import RichText from '@/components/site/rich-text';
import Section from '@/components/site/section';
import SectionHeader from '@/components/site/section-header';
import ConsultationSection from '@/components/site/sections/consultation-section';
import ServiceGrid from '@/components/site/sections/service-grid';
import StatCard from '@/components/site/stat-card';
import { homeContent } from '@/content/home';
import { fillCounts } from '@/data/projects';
import { useContent } from '@/hooks/use-locale';
import { usePortfolio } from '@/hooks/use-portfolio';
import { about, clients, services } from '@/routes';
type Content = typeof homeContent.id;
export default function Home() {
const stats = usePortfolio();
const t = fillCounts(useContent(homeContent), stats);
return (
<>
<Head title={t.title} />
<Hero t={t} />
<Section tone="muted">
<div className="flex flex-col gap-12">
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4 lg:gap-6">
{t.stats.map((stat) => (
<StatCard key={stat.label} {...stat} />
))}
</div>
<div className="flex flex-col gap-4">
<div className="flex flex-col justify-between gap-2 md:flex-row md:items-center">
<div className="flex flex-col gap-1">
<Eyebrow>{t.sectors.eyebrow}</Eyebrow>
<p className="text-on-surface-variant text-xs">
{t.sectors.description}
</p>
</div>
<span className="text-on-surface-variant font-mono text-[11px]">
{t.sectors.since}
</span>
</div>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-5">
{stats.topClients(10).map((client) => (
<div
key={client}
className="border-outline-variant/40 bg-surface-container text-on-surface hover:border-primary/50 hover:bg-surface-container-high flex min-h-16 items-center justify-center rounded-xl border px-4 py-3 text-center font-mono text-xs font-medium transition-all"
>
{client}
</div>
))}
</div>
<ButtonLink
href={clients()}
variant="ghost"
size="inline"
trailingIcon="arrow_forward"
className="self-start"
>
{t.sectors.more}
</ButtonLink>
</div>
</div>
</Section>
<Section>
<div className="grid grid-cols-1 items-start gap-10 lg:grid-cols-12">
<div className="flex flex-col gap-4 lg:col-span-5">
<Eyebrow icon="history_edu">
{t.profile.eyebrow}
</Eyebrow>
<h2 className="font-display text-on-surface text-3xl leading-tight font-extrabold lg:text-4xl">
{t.profile.title}
</h2>
<div className="bg-paj-crimson my-1 h-1 w-20 rounded-full" />
{t.profile.paragraphs.map((paragraph) => (
<p
key={paragraph}
className="text-on-surface-variant text-sm leading-relaxed"
>
<RichText text={paragraph} />
</p>
))}
<ButtonLink
href={about()}
variant="ghost"
size="inline"
trailingIcon="arrow_forward"
className="self-start"
>
{t.profile.more}
</ButtonLink>
</div>
<div className="grid grid-cols-1 gap-6 lg:col-span-7">
{t.profile.statements.map((statement) => (
<Card
key={statement.label}
padding="lg"
interactive
className="overflow-hidden"
>
<Glow className="-top-12 -right-12 h-40 w-40 blur-2xl" />
<div className="relative flex items-start gap-4">
<IconTile icon={statement.icon} size="md" />
<div className="flex flex-col gap-2">
<span className="text-primary font-mono text-xs font-bold tracking-wider uppercase">
{statement.label}
</span>
<blockquote className="border-primary/60 text-on-surface border-l-2 pl-3 text-sm leading-relaxed italic sm:text-base">
“{statement.text}”
</blockquote>
</div>
</div>
</Card>
))}
</div>
</div>
</Section>
<Section id="jasa-layanan" tone="muted">
<div className="flex flex-col gap-12">
<SectionHeader
align="split"
eyebrow={t.services.eyebrow}
eyebrowIcon="apps"
title={t.services.title}
description={t.services.description}
/>
<OdooSpotlight t={t} />
<ServiceGrid />
</div>
</Section>
<ConsultationSection
id="konsultasi"
source="home"
title={t.consultation.title}
description={t.consultation.description}
/>
</>
);
}
function Hero({ t }: { t: Content }) {
return (
<Section
tone="muted"
background={
<>
<GridPattern />
<Glow className="-top-40 -left-20 h-[640px] w-[640px] blur-[140px]" />
<Glow className="bg-primary/15 top-1/4 -right-20 h-[560px] w-[560px] blur-[150px]" />
</>
}
>
<div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-12">
<div className="flex flex-col gap-6 lg:col-span-6">
<Pill pulse>{t.hero.badge}</Pill>
<div className="flex flex-col gap-4">
<h1 className="font-display text-on-surface text-4xl leading-[1.12] font-extrabold tracking-tight sm:text-5xl lg:text-6xl">
<RichText text={t.hero.title} />
</h1>
<p className="text-on-surface-variant text-base leading-relaxed lg:text-lg">
<RichText text={t.hero.description} />
</p>
</div>
<div className="flex flex-wrap items-center gap-4 pt-2">
<ButtonLink
href="#konsultasi"
trailingIcon="arrow_forward"
>
{t.hero.primaryAction}
</ButtonLink>
<ButtonLink
href={services()}
variant="secondary"
icon="layers"
>
{t.hero.secondaryAction}
</ButtonLink>
</div>
<div className="flex items-center gap-3 pt-2">
<div className="flex -space-x-2">
{['KS', 'ME', 'TP'].map((initials) => (
<div
key={initials}
className="border-outline-variant bg-surface-container-high text-primary flex h-8 w-8 items-center justify-center rounded-full border font-mono text-[11px] font-bold shadow-sm"
>
{initials}
</div>
))}
</div>
<p className="text-on-surface-variant text-xs">
{t.hero.trustBefore}{' '}
<Link
href={clients()}
className="text-on-surface hover:text-primary font-semibold"
>
{t.hero.trustLink}
</Link>{' '}
{t.hero.trustAfter}
</p>
</div>
</div>
<div className="relative lg:col-span-6">
<OperationsConsole t={t} />
</div>
</div>
</Section>
);
}
/** Decorative dashboard mock that illustrates an automated ERP pipeline. */
function OperationsConsole({ t }: { t: Content }) {
return (
<div aria-hidden="true" className="relative">
<div className="from-paj-crimson/30 via-primary/15 absolute -inset-1 rounded-2xl bg-gradient-to-tr to-transparent blur-2xl" />
<div className="border-outline-variant/60 bg-surface-container-low relative flex w-full flex-col overflow-hidden rounded-2xl border shadow-2xl">
<div className="border-outline-variant/40 bg-surface-container-high flex items-center justify-between border-b px-4 py-3">
<div className="flex items-center gap-2">
<span className="bg-primary/80 h-3 w-3 rounded-full" />
<span className="h-3 w-3 rounded-full bg-amber-400/80" />
<span className="h-3 w-3 rounded-full bg-emerald-400/80" />
<span className="text-on-surface-variant ml-2 font-mono text-[11px]">
paj-core-suite.internal // v4.20.1
</span>
</div>
<span className="border-primary/30 bg-surface-container-lowest text-primary inline-flex items-center gap-1 rounded border px-2 py-0.5 font-mono text-[11px]">
<span className="bg-primary h-1.5 w-1.5 animate-pulse rounded-full" />
LIVE 14ms
</span>
</div>
<div className="bg-surface-container-lowest/90 flex flex-col gap-4 p-6">
<div className="grid grid-cols-3 gap-3">
{t.console.metrics.map((metric) => (
<div
key={metric.label}
className="border-outline-variant/40 bg-surface-container flex flex-col rounded-xl border p-3"
>
<span className="text-on-surface-variant text-xs">
{metric.label}
</span>
<span
className={
metric.accent
? 'font-display text-primary mt-1 text-xl font-bold'
: 'font-display text-on-surface mt-1 text-xl font-bold'
}
>
{metric.value}
</span>
<span className="text-on-surface-variant mt-0.5 font-mono text-[10px]">
{metric.note}
</span>
</div>
))}
</div>
<div className="border-outline-variant/40 bg-surface-container flex flex-col gap-2 rounded-xl border p-4">
<div className="flex items-center justify-between gap-2">
<span className="font-display text-on-surface text-xs font-semibold">
{t.console.chartTitle}
</span>
<span className="text-primary font-mono text-[11px]">
PostgreSQL • Odoo XML-RPC
</span>
</div>
<Sparkline />
</div>
<div className="flex flex-col gap-1.5">
<div className="text-on-surface-variant flex items-center justify-between px-1 font-mono text-[11px]">
<span>ENTERPRISE EVENT LOG</span>
<span>SLA STATUS</span>
</div>
{t.console.events.map((event) => (
<div
key={event.text}
className="border-outline-variant/30 bg-surface-container flex items-center justify-between gap-2 rounded-lg border p-2.5"
>
<div className="flex items-center gap-2">
<Icon
name={event.icon}
className="text-primary text-sm"
/>
<span className="text-on-surface font-mono text-[11px]">
{event.text}
</span>
</div>
<span className="bg-primary/15 text-primary shrink-0 rounded px-2 py-0.5 font-mono text-[10px] font-semibold">
{event.status}
</span>
</div>
))}
</div>
</div>
</div>
</div>
);
}
function Sparkline() {
const line =
'M 0 50 Q 30 45, 60 30 T 120 40 T 180 20 T 240 35 T 300 12 T 380 16';
return (
<svg
className="h-20 w-full overflow-visible pt-1"
fill="none"
preserveAspectRatio="none"
viewBox="0 0 380 70"
>
<defs>
<linearGradient id="spark-glow" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stopColor="#9E1B22" stopOpacity="0.45" />
<stop offset="60%" stopColor="#FF4D58" stopOpacity="0.12" />
<stop offset="100%" stopColor="#FF4D58" stopOpacity="0" />
</linearGradient>
</defs>
<path d={`${line} L 380 70 L 0 70 Z`} fill="url(#spark-glow)" />
<path
d={line}
className="stroke-primary"
strokeLinecap="round"
strokeWidth="2.5"
/>
<circle cx="380" cy="16" r="3.5" className="fill-primary" />
</svg>
);
}
function OdooSpotlight({ t }: { t: Content }) {
return (
<div className="border-primary-container/40 from-surface-container-low via-surface-container to-surface-container-low relative overflow-hidden rounded-3xl border bg-gradient-to-r p-6 shadow-xl sm:p-8">
<Glow className="-top-24 -right-24 h-80 w-80 blur-3xl" />
<div className="relative grid grid-cols-1 items-center gap-8 lg:grid-cols-12">
<div className="flex flex-col gap-4 lg:col-span-8">
<OdooPartnerBadge className="self-start" />
<h3 className="font-display text-on-surface text-2xl font-extrabold lg:text-3xl">
{t.odoo.title}
<span className="text-on-surface-variant mt-1 block font-sans text-xl font-normal lg:text-2xl">
{t.odoo.subtitle}
</span>
</h3>
<p className="text-on-surface-variant text-sm leading-relaxed">
{t.odoo.description}
</p>
<div className="flex flex-wrap gap-2 pt-2">
{t.odoo.modules.map((module) => (
<span
key={module}
className="border-outline-variant/40 bg-surface-container-high text-on-surface rounded-lg border px-3 py-1 font-mono text-xs"
>
{module}
</span>
))}
</div>
</div>
<div className="border-outline-variant/50 bg-surface-container-lowest/80 flex flex-col gap-2 rounded-2xl border p-5 lg:col-span-4">
<span className="text-primary font-mono text-xs font-bold uppercase">
{t.odoo.cardLabel}
</span>
<p className="text-on-surface-variant text-xs">
{t.odoo.cardText}
</p>
<ButtonLink
href="#konsultasi"
size="block"
trailingIcon="arrow_forward"
className="mt-2 justify-between"
>
{t.odoo.cardAction}
</ButtonLink>
</div>
</div>
</div>
);
}