Add Jenkinsfile

This commit is contained in:
Thanakarn Klangkasame
2025-10-31 16:57:00 +07:00
parent 2c500219b4
commit 0d13390f34
19 changed files with 1294 additions and 363 deletions

109
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,109 @@
pipeline {
agent any
options {
ansiColor('xterm'); timestamps(); disableConcurrentBuilds()
timeout(time: 25, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '10'))
}
environment {
REGISTRY = 'registry.aetherframe.tech'
IMAGE = 'simulationable/amrez-nova-web-services-app' // 👈 ชื่ออิมเมจให้ตรง repo นี้
APP_PORT = '3000' // Next.js in container
HOST_PORT = '5007' // 👈 พอร์ต host (กันชนตัวอื่น)
NODE_ENV = 'production'
NEXT_TELEMETRY_DISABLED = '1'
DOCKER_BUILDKIT = '0'
DOCKER_CLI_HINTS = 'false'
}
stages {
stage('Checkout'){ steps { checkout scm } }
stage('Preflight') {
steps {
sh '''
set -eux
echo "== Sys =="; whoami || true; uname -a || true
echo "== Git =="; git rev-parse --abbrev-ref HEAD; git rev-parse --short=12 HEAD
echo "== Docker =="; docker version; docker info || true
echo "== Registry =="; getent hosts ${REGISTRY} || true; curl -sS -I https://${REGISTRY}/v2/ || true
'''
}
}
stage('Docker Build'){
steps {
script {
def branch = (env.BRANCH_NAME ?: env.GIT_BRANCH ?: sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim())
.replaceFirst(/^origin\\//,'').toLowerCase()
def commit = sh(script: 'git rev-parse --short=12 HEAD', returnStdout: true).trim()
def tag = "${branch}-${env.BUILD_NUMBER}"
env.IMAGE_TAG = tag
sh """
set -eux
docker build --pull \
--label org.opencontainers.image.revision="${commit}" \
-t ${REGISTRY}/${IMAGE}:${tag} \
-t ${REGISTRY}/${IMAGE}:latest \
.
docker images ${REGISTRY}/${IMAGE} --digests || true
"""
}
}
}
stage('Registry Check') {
steps {
sh '''
set -eux
curl -sS -I https://${REGISTRY}/v2/ || true
docker images ${REGISTRY}/${IMAGE} --digests || true
'''
}
}
stage('Docker Push'){
steps {
withCredentials([usernamePassword(credentialsId:'reg-creds', usernameVariable:'REG_USER', passwordVariable:'REG_PASS')]){
sh '''
set -eux
docker logout ${REGISTRY} || true
echo "$REG_PASS" | docker login ${REGISTRY} -u "$REG_USER" --password-stdin
docker push ${REGISTRY}/${IMAGE}:${IMAGE_TAG}
docker push ${REGISTRY}/${IMAGE}:latest
curl -sS -u "$REG_USER:$REG_PASS" https://${REGISTRY}/v2/${IMAGE}/tags/list || true
docker logout ${REGISTRY} || true
'''
}
}
}
stage('Deploy (same host)'){
when { branch 'main' }
steps {
sh """
set -eux
docker rm -f amrez-nova-web || true
docker run -d --name amrez-nova-web \\
-p 127.0.0.1:${HOST_PORT}:${APP_PORT} \\
-e NODE_ENV=${NODE_ENV} \\
-e PORT=${APP_PORT} \\
--restart=always \\
${REGISTRY}/${IMAGE}:latest
sleep 2
docker ps --no-trunc | sed -n '1,5p' || true
docker logs --tail=200 amrez-nova-web || true
(curl -fsS http://127.0.0.1:${HOST_PORT}/ || true)
"""
}
}
}
post {
success { echo "✅ Deployed at http://127.0.0.1:${HOST_PORT}" }
failure { echo "❌ Failed — ดูสเตจ Preflight/Push/Deploy" }
always { cleanWs() }
}
}

View File

@@ -1,24 +1,41 @@
import Section from "../../components/Section";
import { company } from "../../lib/data";
// src/app/about/page.tsx
import Section from "@/app/components/Section";
import {company} from "@/app/lib/data";
export default function AboutPage() {
return (
<Section title="About AMREZ" subtitle={company.tagline}>
<div className="grid md:grid-cols-2 gap-6">
<div className="card">
<h3 className="text-xl font-semibold">Vision</h3>
<p className="text-white/80 mt-3">{company.vision}</p>
</div>
<div className="card">
<h3 className="text-xl font-semibold">Mission</h3>
<p className="text-white/80 mt-3">{company.mission}</p>
</div>
</div>
<div className="card mt-6">
<h3 className="text-xl font-semibold">Company</h3>
<p className="text-white/80 mt-3">{company.about}</p>
<p className="text-white/60 mt-3 text-sm">Source: Company profile</p>
</div>
</Section>
);
}
return (
<Section id="about" title="About AMREZ" subtitle={company.tagline}>
<div className="grid md:grid-cols-2 gap-6">
{/* Vision */}
<div className="rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm">
<h3 className="text-2xl md:text-[26px] font-semibold text-neutral-900 leading-tight">
Vision
</h3>
<p className="mt-3 md:mt-4 text-neutral-700 md:text-lg">
{company.vision}
</p>
</div>
{/* Mission */}
<div className="rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm">
<h3 className="text-2xl md:text-[26px] font-semibold text-neutral-900 leading-tight">
Mission
</h3>
<p className="mt-3 md:mt-4 text-neutral-700 md:text-lg">
{company.mission}
</p>
</div>
</div>
{/* Company */}
<div className="rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm mt-6">
<h3 className="text-2xl md:text-[26px] font-semibold text-neutral-900 leading-tight">
Company
</h3>
<p className="mt-3 md:mt-4 text-neutral-700 md:text-lg">
{company.about}
</p>
</div>
</Section>
);
}

View File

@@ -1,17 +1,64 @@
import Section from "../../components/Section";
import { groupCompanies } from "../../lib/data";
// src/app/companies/page.tsx
import Section from "@/app/components/Section";
import Link from "next/link";
import {groupCompanies} from "@/app/lib/data";
export default function CompaniesPage() {
return (
<Section title="Current Group of Companies">
<div className="grid md:grid-cols-2 gap-6">
{groupCompanies.map((c, i) => (
<div key={i} className="card">
<h3 className="text-lg font-semibold">{c.name}</h3>
<p className="text-white/70 mt-2">{c.focus}</p>
</div>
))}
</div>
</Section>
);
}
return (
<Section id="companies" title="Current Group of Companies">
<div className="grid gap-5 sm:gap-6 sm:grid-cols-2 lg:grid-cols-3">
{groupCompanies.map((c: any, i: number) => (
<article
key={i}
className="rounded-2xl border border-black/10 bg-white p-5 md:p-6 shadow-sm hover:shadow transition-shadow"
>
<div className="flex items-start gap-4">
{c.logo ? (
<img
src={c.logo}
alt={c.name}
className="h-8 w-auto max-w-[160px] object-contain"
/>
) : (
<div
className="h-9 w-9 rounded-lg bg-neutral-100 text-neutral-700 flex items-center justify-center text-sm font-semibold">
{(c.name?.[0] ?? "•").toUpperCase()}
</div>
)}
<div className="min-w-0">
<h3 className="text-xl font-semibold text-neutral-900 truncate">
{c.name}
</h3>
{c.focus && (
<p className="mt-1 text-neutral-700">{c.focus}</p>
)}
</div>
</div>
{(c.url || c.href) && (
<div className="mt-4">
{c.url ? (
<a
href={c.url}
target="_blank"
rel="noopener noreferrer"
className="text-neutral-800 hover:text-black font-medium"
>
Visit site
</a>
) : (
<Link
href={c.href}
className="text-neutral-800 hover:text-black font-medium"
>
Learn more
</Link>
)}
</div>
)}
</article>
))}
</div>
</Section>
);
}

View File

@@ -1,26 +1,101 @@
// src/app/contact/page.tsx
"use client";
import Section from "../../components/Section";
import { useState } from "react";
import Section from "@/app/components/Section";
import {useState} from "react";
export default function ContactPage() {
const [sent, setSent] = useState(false);
return (
<Section title="Contact Us" subtitle="Leave your info and well get back to you">
{!sent ? (
<form className="card grid gap-4 max-w-xl" onSubmit={(e)=>{e.preventDefault(); setSent(true);}}>
<input required placeholder="Name" className="px-4 py-3 rounded-xl bg-white/5 border border-white/10" />
<input required type="email" placeholder="Email" className="px-4 py-3 rounded-xl bg-white/5 border border-white/10" />
<textarea required placeholder="Message" rows={5} className="px-4 py-3 rounded-xl bg-white/5 border border-white/10"></textarea>
<button className="px-5 py-3 rounded-full bg-white text-black font-semibold w-fit">Send</button>
<p className="text-white/50 text-sm">This is a demo form. Hook your API or email service here.</p>
</form>
) : (
<div className="card max-w-xl">
<h3 className="text-xl font-semibold">Thanks!</h3>
<p className="text-white/80 mt-2">Well be in touch shortly.</p>
</div>
)}
</Section>
);
}
const [sent, setSent] = useState(false);
const field =
"w-full rounded-xl border border-black/10 bg-white px-4 py-3 text-neutral-900 placeholder:text-neutral-400 " +
"focus:outline-none focus:ring-2 focus:ring-neutral-900/10 focus:border-neutral-300";
return (
<Section
id="contact"
title="Contact Us"
subtitle="Leave your details and well get back to you."
>
{!sent ? (
<form
className="max-w-xl rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm space-y-4"
onSubmit={(e) => {
e.preventDefault();
setSent(true);
}}
noValidate
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
<div className="grid gap-4 sm:grid-cols-2">
<div>
<label htmlFor="name" className="block text-sm text-neutral-700 mb-1">
Name
</label>
<input
id="name"
name="name"
required
autoComplete="name"
placeholder="Your full name"
className={field}
/>
</div>
<div>
<label htmlFor="email" className="block text-sm text-neutral-700 mb-1">
Email
</label>
<input
id="email"
name="email"
type="email"
required
autoComplete="email"
placeholder="you@example.com"
className={field}
/>
</div>
</div>
<div>
<label htmlFor="message" className="block text-sm text-neutral-700 mb-1">
Message
</label>
<textarea
id="message"
name="message"
required
rows={6}
placeholder="Tell us how we can help…"
className={field}
/>
</div>
<div className="flex items-center gap-3 pt-2">
<button
type="submit"
className="px-5 py-3 rounded-full bg-neutral-900 text-white font-semibold hover:bg-black focus:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900/20 transition"
>
Send
</button>
<span className="text-sm text-neutral-500">
This is a demo form. Connect your API/email service here.
</span>
</div>
</form>
) : (
<div
className="max-w-xl rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm"
role="status"
aria-live="polite"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
<h3 className="text-2xl font-semibold text-neutral-900">Thanks!</h3>
<p className="mt-2 text-neutral-700">
Well be in touch shortly.
</p>
</div>
)}
</Section>
);
}

View File

@@ -1,19 +1,43 @@
import Section from "../../components/Section";
import { channels } from "../../lib/data";
// src/app/distribution/page.tsx
import Section from "@/app/components/Section";
import {channels} from "@/app/lib/data";
export default function DistributionPage() {
return (
<Section title="Channels of Distribution" subtitle="Online, offline, convenience & drug stores, and traditional retail">
<div className="grid md:grid-cols-2 gap-6">
{channels.map((c, i) => (
<div key={i} className="card">
<h3 className="text-xl font-semibold">{c.name}</h3>
<ul className="mt-3 grid sm:grid-cols-2 gap-2 text-white/80">
{c.items.map((it, idx) => <li key={idx} className="px-3 py-2 bg-white/5 rounded-lg">{it}</li>)}
</ul>
</div>
))}
</div>
</Section>
);
}
return (
<Section
id="distribution"
title="Channels of Distribution"
subtitle="Online, offline, convenience & drug stores, and traditional retail"
>
<div
className="grid gap-5 sm:gap-6 sm:grid-cols-2"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
{channels.map((c, i) => (
<section
key={i}
className="rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm"
>
<h3 className="text-xl md:text-2xl font-semibold text-neutral-900">
{c.name}
</h3>
<ul className="mt-4 grid grid-cols-2 sm:grid-cols-3 gap-2.5">
{c.items.map((it: string, idx: number) => (
<li key={idx}>
<span
className="inline-block w-full rounded-lg border border-black/10
bg-neutral-50 px-3 py-2 text-sm md:text-base text-neutral-800
hover:bg-neutral-100 transition-colors"
>
{it}
</span>
</li>
))}
</ul>
</section>
))}
</div>
</Section>
);
}

View File

@@ -1,18 +1,75 @@
import Section from "../../components/Section";
import { founders } from "../../lib/data";
// src/app/founders/page.tsx
import Section from "@/app/components/Section";
import { founders } from "@/app/lib/data";
export default function FoundersPage() {
return (
<Section title="Meet the Founders">
<div className="grid md:grid-cols-2 gap-6">
{founders.map((f, i) => (
<div key={i} className="card">
<h3 className="text-xl font-semibold">{f.name}</h3>
<p className="text-white/60 mt-1">{f.role}</p>
<p className="text-white/80 mt-3">{f.bio}</p>
</div>
))}
</div>
</Section>
);
}
return (
<Section id="founders" title="Meet the Founders">
<div className="grid gap-5 sm:gap-6 md:grid-cols-2">
{founders.map((f: any, i: number) => (
<article
key={i}
className="rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm hover:shadow transition-shadow"
style={{ fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)" }}
>
<div className="flex items-start gap-5">
{/* avatar */}
{f.photo ? (
<img
src={f.photo}
alt={f.name}
className="h-14 w-14 md:h-16 md:w-16 rounded-full object-cover bg-neutral-100 flex-none"
/>
) : (
<div className="h-14 w-14 md:h-16 md:w-16 rounded-full bg-neutral-100 text-neutral-700 flex items-center justify-center text-xl md:text-2xl font-semibold flex-none">
{(f.name?.[0] ?? "•").toUpperCase()}
</div>
)}
<div className="min-w-0">
<h3 className="text-2xl md:text-[26px] font-semibold text-neutral-900 leading-tight truncate">
{f.name}
</h3>
{f.role && (
<p className="mt-1 text-neutral-600">{f.role}</p>
)}
</div>
</div>
{f.bio && (
<p className="mt-4 text-neutral-700 md:text-lg">
{f.bio}
</p>
)}
{/* optional links if provided */}
{(f.linkedin || f.url) && (
<div className="mt-4">
{f.linkedin && (
<a
href={f.linkedin}
target="_blank"
rel="noopener noreferrer"
className="text-neutral-800 hover:text-black font-medium"
>
LinkedIn
</a>
)}
{!f.linkedin && f.url && (
<a
href={f.url}
target="_blank"
rel="noopener noreferrer"
className="text-neutral-800 hover:text-black font-medium"
>
Learn more
</a>
)}
</div>
)}
</article>
))}
</div>
</Section>
);
}

View File

@@ -1,17 +1,80 @@
import Section from "../../components/Section";
import { futurePlans } from "../../lib/data";
// src/app/future/page.tsx
import Section from "@/app/components/Section";
import { futurePlans } from "@/app/lib/data";
export default function FuturePage() {
return (
<Section title="Future of the Company" subtitle="Expansion roadmap 20252026 and beyond">
<div className="grid md:grid-cols-2 gap-6">
{futurePlans.map((p, i) => (
<div key={i} className="card">
<h3 className="text-lg font-semibold">{p.title}</h3>
{p.details && <p className="text-white/70 mt-2">{p.details}</p>}
</div>
))}
</div>
</Section>
);
}
return (
<Section
id="future"
title="Future of the Company"
subtitle="Expansion roadmap 20252026 and beyond"
>
<div
className="grid gap-5 sm:gap-6 md:grid-cols-2"
style={{ fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)" }}
>
{futurePlans.map((p: any, i: number) => (
<article
key={i}
className="rounded-2xl border border-black/10 bg-white p-6 md:p-8 shadow-sm hover:shadow transition-shadow"
>
<header className="flex items-start justify-between gap-4">
<h3 className="text-2xl md:text-[26px] font-semibold text-neutral-900 leading-tight">
{p.title}
</h3>
{/* สถานะ (ถ้ามี) */}
{p.status && (
<span className="shrink-0 rounded-full border border-black/10 bg-neutral-50 px-3 py-1 text-sm text-neutral-700">
{p.status}
</span>
)}
</header>
{/* ไทม์ไลน์สั้น ๆ เช่น Q3 2025 (ถ้ามี) */}
{(p.quarter || p.when) && (
<p className="mt-2 text-sm text-neutral-500">
{p.quarter ?? p.when}
</p>
)}
{/* รายละเอียด */}
{p.details && (
<p className="mt-4 text-neutral-700 md:text-lg">
{p.details}
</p>
)}
{/* แท็ก (ถ้ามี) */}
{Array.isArray(p.tags) && p.tags.length > 0 && (
<ul className="mt-4 flex flex-wrap gap-2">
{p.tags.map((t: string, idx: number) => (
<li
key={idx}
className="rounded-lg border border-black/10 bg-neutral-50 px-3 py-1.5 text-sm text-neutral-800"
>
{t}
</li>
))}
</ul>
)}
{/* ลิงก์อ่านเพิ่ม (ถ้ามี) */}
{p.href && (
<div className="mt-5">
<a
href={p.href}
target="_blank"
rel="noopener noreferrer"
className="text-neutral-800 hover:text-black font-medium"
>
Learn more
</a>
</div>
)}
</article>
))}
</div>
</Section>
);
}

View File

@@ -1,13 +1,61 @@
import Section from "../../components/Section";
import Timeline from "../../components/Timeline";
import { milestones } from "../../lib/data";
// src/app/milestones/page.tsx
import Section from "@/app/components/Section";
import Timeline from "@/app/components/Timeline";
import { milestones } from "@/app/lib/data";
export default function MilestonesPage() {
return (
<Section title="Milestones">
<div className="card">
<Timeline entries={milestones} />
</div>
</Section>
);
}
const years = milestones.map((m: any) => Number(m.year)).filter(Boolean);
const minYear = years.length ? Math.min(...years) : null;
const maxYear = years.length ? Math.max(...years) : null;
const totalItems = milestones.reduce(
(acc: number, m: any) => acc + (Array.isArray(m.items) ? m.items.length : 0),
0
);
return (
<Section
id="milestones"
title="Milestones"
subtitle={minYear && maxYear ? `Highlights from ${minYear} ${maxYear}` : "Key highlights over the years"}
>
{/* meta stripe */}
<div
className="mb-6 rounded-2xl border border-black/10 bg-white p-5 md:p-6 shadow-sm"
style={{ fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)" }}
>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4 sm:divide-x sm:divide-black/10">
<div className="sm:px-4">
<div className="text-sm text-neutral-500">Years</div>
<div className="mt-1 text-2xl font-semibold text-neutral-900">
{minYear && maxYear ? `${minYear}${maxYear}` : "—"}
</div>
</div>
<div className="sm:px-4">
<div className="text-sm text-neutral-500">Total Milestones</div>
<div className="mt-1 text-2xl font-semibold text-neutral-900">
{totalItems || 0}
</div>
</div>
<div className="hidden sm:block sm:px-4">
<div className="text-sm text-neutral-500">Entries</div>
<div className="mt-1 text-2xl font-semibold text-neutral-900">
{milestones.length}
</div>
</div>
</div>
</div>
{/* timeline card */}
<div
className="rounded-3xl border border-black/10 bg-white shadow-sm overflow-hidden"
style={{ fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)" }}
>
<div className="h-px bg-gradient-to-r from-transparent via-black/15 to-transparent" />
<div className="p-6 md:p-8">
<Timeline entries={milestones} />
</div>
<div className="h-px bg-gradient-to-r from-transparent via-black/10 to-transparent" />
</div>
</Section>
);
}

View File

@@ -1,39 +1,62 @@
// src/app/performance/page.tsx
"use client";
import Section from "../../components/Section";
import { kpis } from "../../lib/data";
import { useMemo } from "react";
import Section from "@/app/components/Section";
import {kpis} from "@/app/lib/data";
import {useMemo} from "react";
export default function PerformancePage() {
const rows = useMemo(() => kpis.map(k => ({
year: k.year,
sales: k.salesMillionBaht.toFixed(1),
note: k.note ?? ""
})), []);
const rows = useMemo(() => {
const fmt = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
});
return (
<Section title="Historical Performance" subtitle="Sales (Million Baht)">
<div className="overflow-auto">
<table className="min-w-[480px] w-full text-left border-separate border-spacing-y-2">
<thead>
<tr className="text-white/70">
<th className="px-3 py-2">Year</th>
<th className="px-3 py-2">Sales (M ฿)</th>
<th className="px-3 py-2">Notes</th>
</tr>
</thead>
<tbody>
{rows.map((r) => (
<tr key={r.year} className="bg-white/5">
<td className="px-3 py-2 rounded-l-lg font-semibold">{r.year}</td>
<td className="px-3 py-2">{r.sales}</td>
<td className="px-3 py-2 rounded-r-lg text-white/70">{r.note}</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-white/60 mt-6 text-sm">* 2024: Strategic shift to profitability; profit +2.5%.</p>
</Section>
);
}
return kpis.map((k: any) => {
const n =
typeof k.salesMillionBaht === "number" ? k.salesMillionBaht : NaN;
return {
year: k.year,
sales: Number.isFinite(n) ? fmt.format(n) : "-",
note: k.note ?? "",
};
});
}, []);
return (
<Section title="Historical Performance" subtitle="Sales (Million Baht)">
<div
className="rounded-2xl border border-black/10 bg-white shadow-sm overflow-hidden"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
<div className="overflow-x-auto">
<table className="min-w-[560px] w-full text-left">
<thead className="bg-neutral-50 text-neutral-600 text-sm">
<tr>
<th className="px-5 py-3 font-medium">Year</th>
<th className="px-5 py-3 font-medium">Sales (M ฿)</th>
<th className="px-5 py-3 font-medium">Notes</th>
</tr>
</thead>
<tbody className="divide-y divide-black/5 text-neutral-900">
{rows.map((r) => (
<tr key={r.year} className="hover:bg-neutral-50/70">
<td className="px-5 py-3 font-semibold tabular-nums">
{r.year}
</td>
<td className="px-5 py-3 tabular-nums">{r.sales}</td>
<td className="px-5 py-3 text-neutral-600">{r.note}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* หมายเหตุจริงของคุณ—คงไว้ตามเดิมแต่เปลี่ยนสีให้เข้ากับพื้นขาว */}
<p className="mt-4 text-sm text-neutral-500">
* 2024: Strategic shift to profitability; profit +2.5%.
</p>
</Section>
);
}

View File

@@ -1,12 +1,52 @@
import Section from "../../components/Section";
import ProductGrid from "../../components/ProductGrid";
import { products } from "../../lib/data";
// src/app/products/page.tsx
import Section from "@/app/components/Section";
import ProductGrid from "@/app/components/ProductGrid";
import {products} from "@/app/lib/data";
export default function ProductsPage() {
return (
<Section title="Products">
<ProductGrid products={products} />
<p className="text-white/60 mt-6 text-sm">Note: This is a catalog overview images, shades, and details can be added later.</p>
</Section>
);
}
const hasProducts = Array.isArray(products) && products.length > 0;
const count = hasProducts ? products.length : 0;
return (
<Section id="products" title="Products">
<div
className="rounded-[28px] border border-black/10 bg-white shadow-[0_8px_36px_rgba(0,0,0,0.05)] overflow-hidden"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
{/* top hairline */}
<div className="h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
{/* header */}
<div className="px-6 md:px-8 lg:px-10 py-5 md:py-6 border-b border-black/10">
<div className="flex items-baseline justify-between gap-4">
<span className="text-[12px] md:text-[13px] tracking-[0.14em] text-neutral-500 uppercase">
Catalog
</span>
<span className="text-neutral-500 text-sm">
{count.toLocaleString()} Catelog{count === 1 ? "" : "s"}
</span>
</div>
</div>
{/* grid area */}
<div className="px-4 sm:px-6 md:px-8 lg:px-10 py-6 md:py-8">
{hasProducts ? (
<ProductGrid products={products}/>
) : (
<div className="rounded-2xl border border-black/10 bg-white p-8 text-center text-neutral-600">
Products will appear here.
</div>
)}
</div>
{/* bottom hairline */}
<div className="h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
</div>
{/* note */}
<p className="mt-6 text-sm text-neutral-500">
Note: This is a catalog overview images, shades, and details can be added later.
</p>
</Section>
);
}

View File

@@ -1,17 +1,99 @@
import Link from "next/link";
export default function Footer() {
return (
<footer className="border-t border-white/10 mt-16">
<div className="container-xxl py-10 grid sm:grid-cols-2 gap-6 text-sm text-white/70">
<div>
<div className="font-semibold text-white">KATHY AMREZ</div>
<p className="mt-2">Beauty Skincare Supplements</p>
<p className="mt-2">Bangkok, Thailand</p>
</div>
<div className="sm:text-right">
<p>© {new Date().getFullYear()} Amrez Co., Ltd. All rights reserved.</p>
<p className="mt-2">Built with Next.js Tailwind CSS</p>
</div>
</div>
</footer>
);
}
const year = new Date().getFullYear();
return (
<footer className="bg-white border-t border-black/10 mt-8 md:mt-6">
<div className="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8 py-8 md:py-6 text-base">
{/* Top section */}
<div className="grid gap-8 md:gap-6 md:grid-cols-3">
{/* Brand */}
<div>
<div className="text-xl font-semibold text-gray-900 tracking-wide">KATHY AMREZ</div>
<p className="mt-3 text-gray-600">Beauty · Skincare · Supplements</p>
<p className="mt-1 text-gray-600">Bangkok, Thailand</p>
{/* Socials */}
<div className="mt-5 flex items-center gap-4">
<a href="#" aria-label="Facebook"
className="text-gray-500 hover:text-gray-900 transition-colors">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path
d="M22 12.06C22 6.49 17.52 2 12 2S2 6.49 2 12.06c0 5 3.66 9.14 8.44 9.94v-7.02H7.9v-2.92h2.54V9.41c0-2.5 1.49-3.88 3.77-3.88 1.09 0 2.24.2 2.24.2v2.47h-1.26c-1.24 0-1.63.77-1.63 1.56v1.87h2.78l-.44 2.92h-2.34V22c4.78-.8 8.44-4.94 8.44-9.94Z"/>
</svg>
</a>
<a href="#" aria-label="Instagram"
className="text-gray-500 hover:text-gray-900 transition-colors">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path
d="M7 2h10a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5V7a5 5 0 0 1 5-5Zm0 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3H7Zm5 3.5A5.5 5.5 0 1 1 6.5 13 5.5 5.5 0 0 1 12 7.5Zm0 2A3.5 3.5 0 1 0 15.5 13 3.5 3.5 0 0 0 12 9.5Zm5.75-3.25a1 1 0 1 1-1 1 1 1 0 0 1 1-1Z"/>
</svg>
</a>
<a href="#" aria-label="TikTok"
className="text-gray-500 hover:text-gray-900 transition-colors">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path
d="M21.7 8.5a7.4 7.4 0 0 1-4.7-1.6v6.92a5.86 5.86 0 1 1-5.86-5.86c.35 0 .7.03 1.03.1v2.33a3.58 3.58 0 1 0 2.58 3.44V2h2.5c.1.8.5 1.54 1.1 2.1a4.9 4.9 0 0 0 3.35 1.28Z"/>
</svg>
</a>
<a href="#" aria-label="YouTube"
className="text-gray-500 hover:text-gray-900 transition-colors">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path
d="M23.5 7.5s-.23-1.66-.95-2.39c-.91-.95-1.93-.95-2.4-1C16.9 4 12 4 12 4h0s-4.9 0-8.15.11c-.47.05-1.49.05-2.4 1-.72.73-.95 2.39-.95 2.39S0 9.33 0 11.17v1.66c0 1.83.1 3.66.1 3.66s.23 1.66.95 2.39c.91.95 2.1.92 2.64 1C5.86 20 12 20 12 20s4.9 0 8.15-.11c.47-.05 1.49-.05 2.4-1 .72-.73.95-2.39.95-2.39s.1-1.83.1-3.66v-1.66C23.6 9.33 23.5 7.5 23.5 7.5ZM9.5 15.5v-7l6.25 3.5L9.5 15.5Z"/>
</svg>
</a>
</div>
</div>
{/* Links */}
<div>
<div className="text-lg font-semibold text-gray-900">Explore</div>
<ul className="mt-3 grid grid-cols-2 gap-2 text-gray-600">
<li><Link href="/about" className="hover:text-gray-900 transition-colors">About</Link></li>
<li><Link href="/founders" className="hover:text-gray-900 transition-colors">Founders</Link>
</li>
<li><Link href="/products" className="hover:text-gray-900 transition-colors">Products</Link>
</li>
<li><Link href="/milestones"
className="hover:text-gray-900 transition-colors">Milestones</Link></li>
<li><Link href="/performance"
className="hover:text-gray-900 transition-colors">Performance</Link></li>
<li><Link href="/distribution"
className="hover:text-gray-900 transition-colors">Distribution</Link></li>
<li><Link href="/companies" className="hover:text-gray-900 transition-colors">Group</Link>
</li>
<li><Link href="/future" className="hover:text-gray-900 transition-colors">Future</Link>
</li>
<li className="col-span-2"><Link href="/contact"
className="hover:text-gray-900 transition-colors">Contact</Link>
</li>
</ul>
</div>
{/* Contact */}
<div>
<div className="text-lg font-semibold text-gray-900">Contact</div>
<div className="mt-3 space-y-2 text-gray-600">
<p>Amrez Co., Ltd.</p>
<p>Bangkok, Thailand</p>
<p><a href="mailto:hello@amrez.co.th"
className="hover:text-gray-900 transition-colors">hello@amrez.co.th</a></p>
<p><a href="tel:+6620000000" className="hover:text-gray-900 transition-colors">+66 2 000
0000</a></p>
</div>
</div>
</div>
{/* Bottom bar */}
<div
className="mt-8 md:mt-4 border-t border-black/10 pt-5 md:pt-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<p className="text-gray-600">© {year} Amrez Co., Ltd. All rights reserved.</p>
<p className="text-gray-500">Built with <span className="text-gray-800">Next.js</span> · <span
className="text-gray-800">Tailwind CSS</span></p>
</div>
</div>
</footer>
);
}

View File

@@ -1,52 +1,137 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState } from "react";
import {usePathname} from "next/navigation";
import {useState} from "react";
const links = [
{ href: "/", label: "Home" },
{ href: "/about", label: "About" },
{ href: "/founders", label: "Founders" },
{ href: "/products", label: "Products" },
{ href: "/milestones", label: "Milestones" },
{ href: "/performance", label: "Performance" },
{ href: "/distribution", label: "Distribution" },
{ href: "/companies", label: "Group" },
{ href: "/future", label: "Future" },
{ href: "/contact", label: "Contact" },
{href: "/", label: "Home"},
{href: "/about", label: "About"},
{href: "/founders", label: "Founders"},
{href: "/products", label: "Products"},
{href: "/milestones", label: "Milestones"},
{href: "/performance", label: "Performance"},
{href: "/distribution", label: "Distribution"},
{href: "/companies", label: "Group"},
{href: "/future", label: "Future"},
{href: "/contact", label: "Contact"},
];
export default function Nav() {
const pathname = usePathname();
const [open, setOpen] = useState(false);
const pathname = usePathname();
const [open, setOpen] = useState(false);
return (
<header className="sticky top-0 z-50 bg-black/50 backdrop-blur border-b border-white/10">
<div className="container-xxl flex items-center justify-between py-3">
<Link href="/" className="flex items-center gap-2">
<img src="/logo.svg" alt="KATHY AMREZ" className="h-8 w-auto" />
</Link>
<nav className="hidden md:flex items-center gap-4">
{links.map(l => (
<Link key={l.href} href={l.href} className={`text-sm px-3 py-1 rounded-full transition ${pathname===l.href ? "bg-white/10 text-white" : "text-white/80 hover:text-white"}`}>
{l.label}
</Link>
))}
</nav>
<button className="md:hidden text-white/80" onClick={() => setOpen(o=>!o)} aria-label="Toggle menu"></button>
</div>
{open && (
<div className="md:hidden border-t border-white/10">
<div className="container-xxl py-3 flex flex-col gap-2">
{links.map(l => (
<Link key={l.href} href={l.href} onClick={()=>setOpen(false)} className={`text-base px-3 py-2 rounded-lg transition ${pathname===l.href ? "bg-white/10 text-white" : "text-white/80 hover:text-white"}`}>
{l.label}
</Link>
))}
</div>
</div>
)}
</header>
);
}
return (
<header
className="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-neutral-200/80"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
<div className="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8">
{/* ใช้ grid คุมตำแหน่งให้โลโก้ไม่ชนปุ่ม */}
<div className="grid items-center h-16 md:h-20 grid-cols-[1fr_auto_1fr] md:grid-cols-[auto_1fr_auto]">
{/* โลโก้: กลางบนมือถือ, ซ้ายบนเดสก์ท็อป */}
<Link
href="/"
aria-label="KATHY AMREZ"
className="col-start-2 md:col-start-1 justify-self-center md:justify-self-start flex items-center min-w-0"
>
<img
src="/images/logo.jpg"
alt="KATHY AMREZ"
className="h-8 w-auto max-w-[60vw] md:max-w-none select-none"
draggable={false}
/>
</Link>
{/* เมนูเดสก์ท็อป: คอลัมน์กลาง (พื้นที่ยืด) ชิดขวา */}
<nav className="hidden md:flex col-start-2 justify-self-end items-center gap-7 lg:gap-8">
{links.map(({href, label}) => {
const active = pathname === href;
return (
<Link
key={href}
href={href}
aria-current={active ? "page" : undefined}
className={[
"relative inline-flex items-center",
"text-[15px] lg:text-[16px] tracking-[0.01em]",
active ? "text-neutral-900" : "text-neutral-600 hover:text-neutral-900",
"px-1 py-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900/20 rounded",
].join(" ")}
>
{label}
<span
aria-hidden
className={[
"absolute left-1/2 -bottom-2 h-[5px] w-[5px] -translate-x-1/2 rounded-full bg-neutral-900",
"transition-opacity duration-300",
active ? "opacity-100" : "opacity-0 group-hover:opacity-50",
].join(" ")}
/>
</Link>
);
})}
</nav>
{/* ปุ่มเมนูมือถือ: คอลัมน์ขวา ชิดขวา */}
<button
onClick={() => setOpen((o) => !o)}
aria-label="Toggle menu"
aria-expanded={open}
className="md:hidden col-start-3 justify-self-end h-9 w-9 inline-flex items-center justify-center rounded-md text-neutral-700 hover:text-neutral-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900/20 transition"
>
{open ? (
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden>
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" strokeWidth="1.5"/>
</svg>
) : (
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden>
<path d="M4 7h16M4 12h16M4 17h16" stroke="currentColor" strokeWidth="1.5"/>
</svg>
)}
</button>
</div>
</div>
{/* เมนูมือถือ (overlay) — ยึดใต้แถบ 64px */}
<div
className={[
"md:hidden fixed inset-x-0 z-40 transition-all duration-250 ease-out",
open
? "top-16 opacity-100 pointer-events-auto"
: "top-14 -translate-y-2 opacity-0 pointer-events-none",
].join(" ")}
>
<div className="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8">
<div
className="rounded-2xl border border-neutral-200/80 bg-white/92 backdrop-blur-xl shadow-[0_10px_28px_rgba(0,0,0,0.06)] overflow-hidden">
<nav className="py-1">
{links.map(({href, label}) => {
const active = pathname === href;
return (
<Link
key={href}
href={href}
onClick={() => setOpen(false)}
aria-current={active ? "page" : undefined}
className={[
"block px-4 sm:px-6 lg:px-8 py-4",
"text-[19px] tracking-[0.01em]",
active ? "text-neutral-900" : "text-neutral-700 hover:text-neutral-900",
"border-t first:border-t-0 border-neutral-200/70",
"focus:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900/20 transition-colors",
].join(" ")}
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
{label}
</Link>
);
})}
</nav>
</div>
<div className="h-3"/>
</div>
</div>
</header>
);
}

View File

@@ -1,16 +1,68 @@
import { Product } from "@/lib/data";
// src/app/components/ProductGrid.tsx
import type {Product} from "@/app/lib/data";
export default function ProductGrid({ products }: { products: Product[] }) {
return (
<div className="grid md:grid-cols-2 gap-6">
{products.map((p, idx) => (
<div key={idx} className="card">
<h3 className="text-xl font-semibold">{p.category}</h3>
<ul className="mt-3 grid sm:grid-cols-2 gap-2 text-white/80">
{p.items.map((item, i) => <li key={i} className="px-3 py-2 bg-white/5 rounded-lg">{item}</li>)}
</ul>
type Props = { products: Product[] };
export default function ProductGrid({products}: Props) {
const has = Array.isArray(products) && products.length > 0;
if (!has) {
return (
<div
className="rounded-2xl border border-black/10 bg-white p-8 text-center text-neutral-600"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
Products will appear here.
</div>
);
}
return (
<div
className="grid gap-5 sm:gap-6 md:grid-cols-2"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
{products.map((p, idx) => {
const count = Array.isArray(p.items) ? p.items.length : 0;
return (
<section
key={idx}
className="rounded-[24px] border border-black/10 bg-white shadow-[0_8px_28px_rgba(0,0,0,0.05)] overflow-hidden"
>
{/* hairline บน-ล่างแบบ editorial */}
<div className="h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
{/* header */}
<header
className="px-6 md:px-8 py-5 border-b border-black/10 flex items-baseline justify-between gap-4">
<h3 className="text-2xl md:text-[26px] font-semibold leading-tight text-neutral-900">
{p.category}
</h3>
<span className="text-sm text-neutral-500">
{count.toLocaleString()}
</span>
</header>
{/* list */}
<div className="px-6 md:px-8 py-5">
<ul className="grid grid-cols-2 sm:grid-cols-3 gap-2.5">
{(p.items ?? []).map((item, i) => (
<li key={i}>
<span
className="inline-flex w-full items-center rounded-lg border border-black/10
bg-neutral-50 px-3 py-2 text-sm text-neutral-800
hover:bg-neutral-100 transition-colors"
>
{item}
</span>
</li>
))}
</ul>
</div>
<div className="h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
</section>
);
})}
</div>
))}
</div>
);
}
);
}

View File

@@ -1,15 +1,29 @@
import { ReactNode } from "react";
import {ReactNode} from "react";
export default function Section({ id, title, subtitle, children }: { id?: string; title: string; subtitle?: string; children?: ReactNode }) {
return (
<section id={id} className="section">
<div className="container-xxl">
<div className="mb-6 md:mb-10">
<h2 className="text-2xl md:text-4xl font-bold text-white">{title}</h2>
{subtitle && <p className="text-white/70 mt-2 max-w-2xl">{subtitle}</p>}
</div>
{children}
</div>
</section>
);
}
export default function Section({
id,
title,
subtitle,
children,
}: {
id?: string;
title: string;
subtitle?: string;
children?: ReactNode;
}) {
return (
<section id={id} className="bg-white">
<div className="mx-auto max-w-screen-2xl px-4 sm:px-6 lg:px-8 py-12 md:py-14">
<div className="mb-8 md:mb-10">
<h2 className="text-3xl md:text-4xl font-bold text-gray-900">{title}</h2>
{subtitle ? (
<p className="mt-3 text-base md:text-lg text-gray-600 max-w-3xl">
{subtitle}
</p>
) : null}
</div>
{children}
</div>
</section>
);
}

View File

@@ -1,9 +1,22 @@
export default function StatCard({ title, value, note }: { title: string; value: string; note?: string }) {
return (
<div className="card">
<div className="text-3xl font-bold">{value}</div>
<div className="text-white/70 mt-1">{title}</div>
{note && <div className="text-xs text-white/60 mt-2">{note}</div>}
</div>
);
}
// src/app/components/StatCard.tsx
type Props = {
title: string;
value: string;
note?: string;
};
export default function StatCard({title, value, note}: Props) {
return (
<div
className="rounded-2xl border border-black/10 bg-white p-5 md:p-6 shadow-sm hover:shadow transition-shadow"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
aria-label={`${title}: ${value}`}
>
<div className="text-[28px] md:text-[32px] leading-none font-semibold text-neutral-900 tabular-nums">
{value || "-"}
</div>
<div className="mt-2 text-sm md:text-base text-neutral-600">{title}</div>
{note ? <div className="mt-2 text-sm text-neutral-500">{note}</div> : null}
</div>
);
}

View File

@@ -1,17 +1,63 @@
import { Milestone } from "@/lib/data";
import {Milestone} from "@/app/lib/data";
export default function Timeline({ entries }: { entries: Milestone[] }) {
return (
<ol className="relative border-s border-white/10 space-y-8">
{entries.map((ms, idx) => (
<li key={idx} className="ms-6">
<span className="absolute -left-1.5 flex items-center justify-center w-3 h-3 rounded-full ring-4 ring-black bg-white/80"></span>
<h3 className="text-lg md:text-xl font-semibold">{ms.year}</h3>
<ul className="list-disc list-inside text-white/80 mt-2 space-y-1">
{ms.items.map((it, i) => <li key={i}>{it}</li>)}
</ul>
</li>
))}
</ol>
);
}
export default function Timeline({entries}: { entries: Milestone[] }) {
return (
<ol className="grid grid-cols-[24px_1fr] gap-x-4">
{entries.map((ms, idx) => (
<li
key={idx}
className="col-span-2 grid grid-cols-[24px_1fr] grid-rows-[auto_1fr] gap-x-4"
>
{/* ============ แถวบน: จุด + เส้นท่อนบน + ปี ============ */}
<div className="relative row-start-1">
{/* เส้นท่อนบน: ยาวถึงขอบนอกของจุด (50% - 11px) */}
{idx > 0 && (
<span
aria-hidden="true"
className="absolute left-1/2 -translate-x-1/2 top-0 h-[calc(50%-11px)] w-px bg-neutral-200"
/>
)}
{/* จุด */}
<span
aria-hidden="true"
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-3.5 w-3.5 rounded-full bg-neutral-900 ring-4 ring-white z-10 shadow-[0_0_0_1px_rgba(0,0,0,0.06)]"
/>
</div>
{/* ปี — อยู่แถวเดียวกับจุด เพื่อให้ “ตรงแนว” กันเสมอ */}
<div className="row-start-1">
<h3 className="text-xl md:text-2xl font-semibold text-neutral-900 tabular-nums">
{ms.year}
</h3>
</div>
{/* ============ แถวล่าง: เส้นท่อนล่าง + รายการ ============ */}
{/* ดันเส้นล่างขึ้นมา 11px ให้ชนขอบจุดพอดี */}
<div className="relative row-start-2 -mt-[11px]">
{idx < entries.length - 1 && (
<span
aria-hidden="true"
className="absolute left-1/2 -translate-x-1/2 top-0 bottom-0 w-px bg-neutral-200"
/>
)}
</div>
<div className="row-start-2 pb-4">
<ul className="mt-2 space-y-2">
{ms.items.map((it, i) => (
<li key={i} className="flex gap-3">
<span
aria-hidden="true"
className="mt-2 h-1.5 w-1.5 flex-none rounded-full bg-neutral-300"
/>
<p className="text-neutral-700">{it}</p>
</li>
))}
</ul>
</div>
</li>
))}
</ol>
);
}

View File

@@ -0,0 +1,10 @@
import localFont from "next/font/local";
export const dbHeavent = localFont({
src: [
{path: "./font/dbheavent/DBHeavent-Regular.woff2", weight: "400", style: "normal"},
{path: "./font/dbheavent/DBHeavent-Bold.woff2", weight: "700", style: "normal"},
],
display: "swap",
preload: true,
});

View File

@@ -1,34 +1,27 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import type {Metadata} from "next";
import Nav from "@/app/components/Nav";
import Footer from "@/app/components/Footer";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
import {dbHeavent} from "./fonts";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
return (
<html lang="en">
{/* ✅ ต้องมี <body> ไม่งั้น Next.js จะเตือน/พัง hydration */}
<body className={`${dbHeavent.className} min-h-dvh bg-white text-gray-900 antialiased`}>
<Nav/>
<main>{children}</main>
<Footer/>
</body>
</html>
);
}

View File

@@ -1,65 +1,198 @@
import Image from "next/image";
// src/app/page.tsx
import Link from "next/link";
import {company, milestones, products} from "@/app/lib/data";
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main>
</div>
);
// แปลง milestones เป็นรายการเรียบ ๆ สำหรับโชว์หน้าโฮม (เอาแค่ 5 รายการแรก)
function getHighlights() {
try {
const list = (milestones ?? [])
.flatMap((ms) => (ms.items ?? []).map((text) => ({year: ms.year, text})))
.slice(0, 5);
return list;
} catch {
return [];
}
}
export default function HomePage() {
const highlights = getHighlights();
const productLines = Array.isArray(products) ? products.length : 0;
const years = (milestones ?? [])
.map((m) => Number(m.year))
.filter((n) => Number.isFinite(n));
const minYear = years.length ? Math.min(...years) : null;
const maxYear = years.length ? Math.max(...years) : null;
const milestonesCount = (milestones ?? []).reduce(
(acc, m) => acc + (m.items ?? []).length,
0
);
return (
<>
{/* ===== HERO: Split (ซ้ายข้อความ / ขวาสื่อ) — ขาว-แพง พร้อม hairline & media fallback ===== */}
<section className="bg-white">
<div className="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div
className="grid grid-cols-1 md:grid-cols-12 gap-0 rounded-[28px] border border-black/10 bg-white shadow-[0_8px_36px_rgba(0,0,0,0.05)] overflow-hidden"
style={{fontFamily: "var(--font-dbheavent, ui-sans-serif, system-ui)"}}
>
{/* hairline บน */}
<div
className="md:col-span-12 h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
{/* Left: Text */}
<div className="md:col-span-6 px-6 md:px-8 lg:px-12 py-8 md:py-12">
<div
className="inline-flex items-center gap-2 rounded-full border border-black/10 bg-white px-3 py-1 text-[12px] md:text-[13px] tracking-[0.14em] text-neutral-600 uppercase">
AMREZ Group
<span className="h-1 w-1 rounded-full bg-neutral-400"/>
Portfolio
</div>
<h1 className="mt-4 md:mt-5 text-[44px] leading-[1.06] md:text-6xl font-extrabold tracking-tight text-neutral-900">
KATHY · AMREZ
</h1>
<p className="mt-4 text-lg md:text-xl text-neutral-700 max-w-3xl">
{company.tagline}
</p>
<div className="mt-7">
<Link
href="/contact"
className="inline-flex items-center px-6 py-3 rounded-full bg-neutral-900 text-white font-semibold transition-colors hover:bg-black"
>
</Link>
</div>
{/* At a glance — จริงจากข้อมูล ไม่รก */}
<div className="mt-8 grid gap-3 sm:grid-cols-3">
<div className="rounded-xl border border-black/10 bg-neutral-50/80 px-4 py-3">
<div className="text-xs uppercase tracking-[0.12em] text-neutral-500">
Product Lines
</div>
<div className="mt-1 text-xl font-semibold text-neutral-900 tabular-nums">
{productLines}
</div>
</div>
<div className="rounded-xl border border-black/10 bg-neutral-50/80 px-4 py-3">
<div className="text-xs uppercase tracking-[0.12em] text-neutral-500">
Years
</div>
<div className="mt-1 text-xl font-semibold text-neutral-900 tabular-nums">
{minYear && maxYear ? `${minYear}${maxYear}` : "—"}
</div>
</div>
<div className="rounded-xl border border-black/10 bg-neutral-50/80 px-4 py-3">
<div className="text-xs uppercase tracking-[0.12em] text-neutral-500">
Milestones
</div>
<div className="mt-1 text-xl font-semibold text-neutral-900 tabular-nums">
{milestonesCount.toLocaleString()}
</div>
</div>
</div>
</div>
{/* Right: Media (มี fallback ไม่ต้องพึ่งรูปก็ยังดูแพง) */}
<div className="md:col-span-6 relative">
<div
className="h-72 md:h-full"
style={{
// ชั้นล่าง: พื้นขาวมี texture เบาๆ (radial gradient)
// ชั้นบน: พยายามโหลด /images/hero.jpg ถ้ามี; ถ้าไม่มีจะเห็นแต่ gradient (ไม่ขึ้นรูปแตก)
backgroundImage:
"radial-gradient(1200px 600px at 60% 20%, rgba(0,0,0,0.06), transparent 55%), radial-gradient(900px 400px at 20% 80%, rgba(0,0,0,0.04), transparent 50%), url('/images/hero.png')",
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
}}
aria-hidden="true"
/>
{/* กรอบในแบบ editorial ให้มิติขึ้น */}
<div className="pointer-events-none absolute inset-0">
<div
className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
<div
className="absolute inset-x-0 bottom-0 h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
</div>
</div>
{/* hairline ล่าง */}
<div
className="md:col-span-12 h-px bg-gradient-to-r from-transparent via-black/10 to-transparent"/>
</div>
</div>
</section>
{/* ===== WHAT WE DO ===== */}
<section className="bg-white border-t border-black/10">
<div className="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900"></h2>
<p className="mt-4 text-base md:text-lg text-neutral-700 max-w-3xl">
{company.about}
</p>
<div className="mt-8 grid gap-6 md:grid-cols-3">
<div className="rounded-xl border border-black/10 bg-white p-5">
<h3 className="text-xl md:text-2xl font-semibold text-neutral-900">Cosmetics</h3>
<p className="mt-2 text-neutral-700 md:text-lg">
</p>
</div>
<div className="rounded-xl border border-black/10 bg-white p-5">
<h3 className="text-xl md:text-2xl font-semibold text-neutral-900">Skincare</h3>
<p className="mt-2 text-neutral-700 md:text-lg">
</p>
</div>
<div className="rounded-xl border border-black/10 bg-white p-5">
<h3 className="text-xl md:text-2xl font-semibold text-neutral-900">Supplements</h3>
<p className="mt-2 text-neutral-700 md:text-lg">
</p>
</div>
</div>
</div>
</section>
{/* ===== HIGHLIGHTS (list) ===== */}
<section className="bg-white border-t border-black/10">
<div className="mx-auto max-w-screen-xl px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<h2 className="text-3xl md:text-4xl font-bold text-neutral-900">Highlights</h2>
{highlights.length === 0 ? (
<p className="mt-4 text-neutral-700">
<code className="text-neutral-900">milestones</code>
</p>
) : (
<ul className="mt-6 divide-y divide-black/10 rounded-2xl border border-black/10 bg-white">
{highlights.map((h, i) => (
<li key={`${h.year}-${i}`} className="px-5 py-4 md:px-6 md:py-5">
<div className="flex items-start gap-4">
<div
className="mt-[2px] text-sm md:text-base font-semibold text-neutral-900 tabular-nums">
{h.year}
</div>
<p className="text-neutral-700 md:text-lg">{h.text}</p>
</div>
</li>
))}
</ul>
)}
<div className="mt-10">
<Link
href="/contact"
className="inline-flex items-center px-6 py-3 rounded-full bg-neutral-900 text-white font-semibold transition-colors hover:bg-black"
>
</Link>
</div>
</div>
</section>
</>
);
}