Initial Kelola Bumi website
7
.env.example
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
SMTP_HOST=mail.unified.co.id
|
||||||
|
SMTP_PORT=465
|
||||||
|
SMTP_SECURE=true
|
||||||
|
SMTP_USER=mailer@unified.co.id
|
||||||
|
SMTP_PASS=your-password-here
|
||||||
|
CONTACT_TO_EMAIL=info@kelolabumi.com
|
||||||
|
CONTACT_CAPTCHA_SECRET=change-this-secret
|
||||||
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.DS_Store
|
||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
.env*
|
||||||
|
!.env.example
|
||||||
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Kelola Bumi Web
|
||||||
|
|
||||||
|
Website perusahaan PT Kelola Bumi Nusantara berbasis Next.js.
|
||||||
|
|
||||||
|
## Requirement
|
||||||
|
|
||||||
|
- Node.js 20 atau lebih baru
|
||||||
|
- npm
|
||||||
|
|
||||||
|
## Menjalankan lokal
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Server produksi lokal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
Salin `.env.example` menjadi `.env.local` lalu isi nilainya.
|
||||||
|
|
||||||
|
Variabel yang dipakai:
|
||||||
|
|
||||||
|
- `SMTP_HOST`
|
||||||
|
- `SMTP_PORT`
|
||||||
|
- `SMTP_SECURE`
|
||||||
|
- `SMTP_USER`
|
||||||
|
- `SMTP_PASS`
|
||||||
|
- `CONTACT_TO_EMAIL`
|
||||||
|
- `CONTACT_CAPTCHA_SECRET`
|
||||||
|
|
||||||
|
## Deploy ke server
|
||||||
|
|
||||||
|
1. Clone repository di server.
|
||||||
|
2. Buat file `.env.local`.
|
||||||
|
3. Jalankan `npm install`.
|
||||||
|
4. Jalankan `npm run build`.
|
||||||
|
5. Jalankan `npm run start`.
|
||||||
|
|
||||||
|
Untuk production, disarankan memakai process manager seperti PM2 atau systemd, dan reverse proxy seperti Nginx.
|
||||||
|
|
||||||
|
## Catatan
|
||||||
|
|
||||||
|
- Jangan commit `.env.local`.
|
||||||
|
- Folder `.next` dan `node_modules` tidak perlu di-upload ke git.
|
||||||
235
app/about/page.tsx
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import aboutHeroImage from "../../images/file8.jpg";
|
||||||
|
import historyImage from "../../images/kelor.jpg";
|
||||||
|
import strategyImage from "../../images/alpukat.jpg";
|
||||||
|
|
||||||
|
const aboutNavItems = [
|
||||||
|
{ label: "Home", href: "/" },
|
||||||
|
{ label: "Tentang", href: "/about", active: true },
|
||||||
|
{ label: "Layanan", href: "/services" },
|
||||||
|
{ label: "Kontak", href: "/contact" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const missions = [
|
||||||
|
{
|
||||||
|
title: "1. Edukasi & Konsultasi",
|
||||||
|
description:
|
||||||
|
"Memberikan pendampingan teknis, perencanaan kebun, dan transfer pengetahuan yang relevan dengan kondisi lapangan."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "2. Input & Pengembangan",
|
||||||
|
description:
|
||||||
|
"Mendukung penyediaan bibit, sarana produksi, dan penguatan sistem budidaya yang efektif dan bertanggung jawab."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "3. Rantai Pasok",
|
||||||
|
description:
|
||||||
|
"Merapikan alur distribusi komoditas agar hasil usaha agraria terserap pasar dengan kualitas dan nilai yang lebih baik."
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<main className="page-shell">
|
||||||
|
<header className="topbar">
|
||||||
|
<div className="container topbar-inner">
|
||||||
|
<a className="brand" href="/" aria-label="Kelola Bumi">
|
||||||
|
<Image
|
||||||
|
src="/logo_kelolabumi.png"
|
||||||
|
alt="Kelola Bumi Logo"
|
||||||
|
width={240}
|
||||||
|
height={40}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav className="nav desktop-nav" aria-label="Navigasi utama">
|
||||||
|
{aboutNavItems.map((item) => (
|
||||||
|
<a
|
||||||
|
key={item.label}
|
||||||
|
href={item.href}
|
||||||
|
className={item.active ? "nav-active" : undefined}
|
||||||
|
aria-current={item.active ? "page" : undefined}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<a className="button button-primary topbar-cta" href="/contact">
|
||||||
|
Konsultasi Sekarang
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="about-hero">
|
||||||
|
<div className="about-hero-media" aria-hidden="true">
|
||||||
|
<Image
|
||||||
|
src={aboutHeroImage}
|
||||||
|
alt=""
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
className="hero-image"
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
<div className="about-hero-overlay" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container about-hero-content">
|
||||||
|
<span className="section-kicker">Tentang Kami</span>
|
||||||
|
<h1>Membangun negeri melalui sektor agraria yang terkelola lebih baik.</h1>
|
||||||
|
<p>
|
||||||
|
Kelola Bumi berdedikasi memperkuat rantai nilai agribisnis Indonesia
|
||||||
|
melalui integrasi disiplin operasional, pendekatan budidaya presisi,
|
||||||
|
dan pemahaman lokal yang nyata sejak 2015.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-light">
|
||||||
|
<div className="container about-history-grid">
|
||||||
|
<div className="about-copy">
|
||||||
|
<p className="section-kicker">Sejarah Perjalanan</p>
|
||||||
|
<h2>Berawal dari inisiatif lapangan, tumbuh menjadi mitra agraria.</h2>
|
||||||
|
<p>
|
||||||
|
Kelola Bumi dibangun dengan fokus pada pengelolaan usaha agraria
|
||||||
|
yang lebih terstruktur. Dari pengembangan komoditas hingga dukungan
|
||||||
|
distribusi, kami bertumbuh bersama kebutuhan pelaku usaha tani,
|
||||||
|
kebun, peternakan, dan perikanan di berbagai wilayah.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="about-stats">
|
||||||
|
<div className="about-stat">
|
||||||
|
<strong>2015</strong>
|
||||||
|
<span>Tahun Berdiri</span>
|
||||||
|
</div>
|
||||||
|
<div className="about-stat">
|
||||||
|
<strong>500+</strong>
|
||||||
|
<span>Inisiatif & Proyek</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="about-image-card">
|
||||||
|
<Image
|
||||||
|
src={historyImage}
|
||||||
|
alt="Aktivitas pengembangan komoditas Kelola Bumi"
|
||||||
|
fill
|
||||||
|
className="media-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 50vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-soft">
|
||||||
|
<div className="container">
|
||||||
|
<div className="section-heading section-heading-centered">
|
||||||
|
<p className="section-kicker">Visi & Misi</p>
|
||||||
|
<h2>Landasan kerja untuk pertumbuhan agribisnis yang sehat.</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="about-bento-grid">
|
||||||
|
<article className="about-vision-card">
|
||||||
|
<span className="about-mark">Visi</span>
|
||||||
|
<h3>
|
||||||
|
Menjadi perusahaan agrikultur yang dipercaya untuk menghubungkan
|
||||||
|
potensi lahan, komoditas, dan pasar secara berkelanjutan.
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Kami ingin menghadirkan sistem kerja agraria yang modern namun
|
||||||
|
tetap berpijak pada realitas lapangan dan nilai kemitraan jangka
|
||||||
|
panjang.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article className="about-mission-intro">
|
||||||
|
<p className="section-kicker section-kicker-inverse">Misi Strategis</p>
|
||||||
|
<h3>Langkah nyata untuk sektor agraria yang lebih tangguh.</h3>
|
||||||
|
<p>
|
||||||
|
Pendekatan kami dirancang agar konsultasi, input, dan distribusi
|
||||||
|
berjalan sebagai satu sistem yang saling menguatkan.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{missions.map((mission, index) => (
|
||||||
|
<article key={mission.title} className="about-mission-card">
|
||||||
|
<div className="mini-icon">{String(index + 1).padStart(2, "0")}</div>
|
||||||
|
<h4>{mission.title}</h4>
|
||||||
|
<p>{mission.description}</p>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-showcase">
|
||||||
|
<div className="container about-purpose-grid">
|
||||||
|
<div className="about-copy">
|
||||||
|
<p className="section-kicker">Cara Kami Bekerja</p>
|
||||||
|
<h2>Strategi yang dekat dengan kebutuhan riil di lapangan.</h2>
|
||||||
|
<p>
|
||||||
|
Setiap proyek kami awali dengan pemahaman konteks: komoditas,
|
||||||
|
kondisi lahan, kesiapan pasokan, dan target pasar. Dari sana,
|
||||||
|
solusi dibangun agar lebih presisi dan dapat dijalankan.
|
||||||
|
</p>
|
||||||
|
<a className="button button-primary" href="/contact">
|
||||||
|
Hubungi Kami
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="about-purpose-card">
|
||||||
|
<div className="about-purpose-media">
|
||||||
|
<Image
|
||||||
|
src={strategyImage}
|
||||||
|
alt="Komoditas unggulan Kelola Bumi"
|
||||||
|
fill
|
||||||
|
className="showcase-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 50vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="about-purpose-body">
|
||||||
|
<h3>Kolaborasi, presisi, dan kesinambungan.</h3>
|
||||||
|
<p>
|
||||||
|
Kami memadukan kualitas input, disiplin pengelolaan, dan
|
||||||
|
orientasi pasar agar setiap tahapan usaha agraria bergerak lebih
|
||||||
|
efisien dan bernilai.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="cta-section">
|
||||||
|
<div className="container cta-inner">
|
||||||
|
<div>
|
||||||
|
<p className="section-kicker cta-kicker">Bertumbuh Bersama</p>
|
||||||
|
<h2>Siap mendiskusikan kebutuhan agrikultur Anda?</h2>
|
||||||
|
<p>
|
||||||
|
Kami siap mendampingi langkah Anda melalui konsultasi, penguatan
|
||||||
|
operasional, dan strategi pengembangan komoditas yang lebih terarah.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a className="button button-accent" href="/services">
|
||||||
|
Lihat Layanan Kami
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="container footer-inner">
|
||||||
|
<div>
|
||||||
|
<div className="footer-brand">Kelola Bumi</div>
|
||||||
|
<p>© 2026 PT Kelola Bumi Nusantara. An Agricultural Company.</p>
|
||||||
|
</div>
|
||||||
|
<div className="footer-links">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<a href="/about">Tentang</a>
|
||||||
|
<a href="/services">Layanan</a>
|
||||||
|
<a href="/contact">Kontak</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
app/api/contact/captcha/route.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { generateCaptchaChallenge } from "../../../../lib/contact-captcha";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const challenge = generateCaptchaChallenge();
|
||||||
|
return NextResponse.json(challenge);
|
||||||
|
}
|
||||||
147
app/api/contact/route.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import nodemailer from "nodemailer";
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { verifyCaptchaToken } from "../../../lib/contact-captcha";
|
||||||
|
|
||||||
|
const WINDOW_MS = 30 * 60 * 1000;
|
||||||
|
const MAX_REQUESTS = 5;
|
||||||
|
const MIN_FILL_MS = 3000;
|
||||||
|
|
||||||
|
type RateLimitEntry = {
|
||||||
|
count: number;
|
||||||
|
resetAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rateLimitStore = globalThis as typeof globalThis & {
|
||||||
|
__contactRateLimit?: Map<string, RateLimitEntry>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const requests = rateLimitStore.__contactRateLimit ?? new Map<string, RateLimitEntry>();
|
||||||
|
rateLimitStore.__contactRateLimit = requests;
|
||||||
|
|
||||||
|
function getClientIp(request: NextRequest) {
|
||||||
|
const forwardedFor = request.headers.get("x-forwarded-for");
|
||||||
|
if (forwardedFor) {
|
||||||
|
return forwardedFor.split(",")[0]?.trim() ?? "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.headers.get("x-real-ip") ?? "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRateLimited(ip: string) {
|
||||||
|
const now = Date.now();
|
||||||
|
const existing = requests.get(ip);
|
||||||
|
|
||||||
|
if (!existing || existing.resetAt <= now) {
|
||||||
|
requests.set(ip, { count: 1, resetAt: now + WINDOW_MS });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing.count >= MAX_REQUESTS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
existing.count += 1;
|
||||||
|
requests.set(ip, existing);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const ip = getClientIp(request);
|
||||||
|
|
||||||
|
if (isRateLimited(ip)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Terlalu banyak percobaan. Silakan coba lagi beberapa saat." },
|
||||||
|
{ status: 429 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = (await request.json()) as {
|
||||||
|
fullName?: string;
|
||||||
|
email?: string;
|
||||||
|
subject?: string;
|
||||||
|
message?: string;
|
||||||
|
website?: string;
|
||||||
|
startedAt?: string;
|
||||||
|
captchaAnswer?: string;
|
||||||
|
captchaToken?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (body.website) {
|
||||||
|
return NextResponse.json({ message: "Permintaan ditolak." }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const startedAt = Number(body.startedAt ?? 0);
|
||||||
|
if (!startedAt || Date.now() - startedAt < MIN_FILL_MS) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Form dikirim terlalu cepat. Silakan isi kembali dengan benar." },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullName = body.fullName?.trim();
|
||||||
|
const email = body.email?.trim();
|
||||||
|
const subject = body.subject?.trim();
|
||||||
|
const message = body.message?.trim();
|
||||||
|
const captchaAnswer = body.captchaAnswer?.trim();
|
||||||
|
const captchaToken = body.captchaToken?.trim();
|
||||||
|
|
||||||
|
if (!fullName || !email || !subject || !message) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Semua field wajib diisi sebelum mengirim." },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!captchaAnswer || !captchaToken || !verifyCaptchaToken(captchaToken, captchaAnswer)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Captcha tidak valid atau sudah kedaluwarsa." },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.SMTP_HOST,
|
||||||
|
port: Number(process.env.SMTP_PORT ?? 465),
|
||||||
|
secure: process.env.SMTP_SECURE !== "false",
|
||||||
|
auth: {
|
||||||
|
user: process.env.SMTP_USER,
|
||||||
|
pass: process.env.SMTP_PASS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: `"Kelola Bumi Contact Form" <${process.env.SMTP_USER}>`,
|
||||||
|
to: process.env.CONTACT_TO_EMAIL,
|
||||||
|
replyTo: email,
|
||||||
|
subject: `[Kelola Bumi] ${subject}`,
|
||||||
|
text: [
|
||||||
|
`Nama: ${fullName}`,
|
||||||
|
`Email: ${email}`,
|
||||||
|
`Subjek: ${subject}`,
|
||||||
|
"",
|
||||||
|
"Pesan:",
|
||||||
|
message
|
||||||
|
].join("\n"),
|
||||||
|
html: `
|
||||||
|
<div style="font-family: Arial, sans-serif; color: #111d23; line-height: 1.6;">
|
||||||
|
<h2>Pesan Baru dari Form Kontak Kelola Bumi</h2>
|
||||||
|
<p><strong>Nama:</strong> ${fullName}</p>
|
||||||
|
<p><strong>Email:</strong> ${email}</p>
|
||||||
|
<p><strong>Subjek:</strong> ${subject}</p>
|
||||||
|
<p><strong>Pesan:</strong></p>
|
||||||
|
<p>${message.replace(/\n/g, "<br />")}</p>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
message: "Pesan berhasil dikirim. Tim kami akan menghubungi Anda."
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: "Email gagal dikirim. Periksa konfigurasi mailer." },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
183
app/contact/contact-form.tsx
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FormEvent, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
|
type SubmitState =
|
||||||
|
| { type: "idle"; message: string }
|
||||||
|
| { type: "success"; message: string }
|
||||||
|
| { type: "error"; message: string };
|
||||||
|
|
||||||
|
const initialState: SubmitState = {
|
||||||
|
type: "idle",
|
||||||
|
message: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ContactForm() {
|
||||||
|
const [submitState, setSubmitState] = useState<SubmitState>(initialState);
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [captchaPrompt, setCaptchaPrompt] = useState("");
|
||||||
|
const [captchaToken, setCaptchaToken] = useState("");
|
||||||
|
const [captchaLoading, setCaptchaLoading] = useState(true);
|
||||||
|
const startedAt = useMemo(() => Date.now().toString(), []);
|
||||||
|
|
||||||
|
async function loadCaptcha() {
|
||||||
|
setCaptchaLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/contact/captcha", {
|
||||||
|
cache: "no-store"
|
||||||
|
});
|
||||||
|
const data = (await response.json()) as { prompt?: string; token?: string };
|
||||||
|
|
||||||
|
setCaptchaPrompt(data.prompt ?? "");
|
||||||
|
setCaptchaToken(data.token ?? "");
|
||||||
|
} finally {
|
||||||
|
setCaptchaLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void loadCaptcha();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsSubmitting(true);
|
||||||
|
setSubmitState(initialState);
|
||||||
|
|
||||||
|
const form = event.currentTarget;
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/contact", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
fullName: formData.get("fullName"),
|
||||||
|
email: formData.get("email"),
|
||||||
|
subject: formData.get("subject"),
|
||||||
|
message: formData.get("message"),
|
||||||
|
website: formData.get("website"),
|
||||||
|
startedAt: formData.get("startedAt"),
|
||||||
|
captchaAnswer: formData.get("captchaAnswer"),
|
||||||
|
captchaToken
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = (await response.json()) as { message?: string };
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
setSubmitState({
|
||||||
|
type: "error",
|
||||||
|
message: data.message ?? "Pesan gagal dikirim. Silakan coba lagi."
|
||||||
|
});
|
||||||
|
await loadCaptcha();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.reset();
|
||||||
|
await loadCaptcha();
|
||||||
|
setSubmitState({
|
||||||
|
type: "success",
|
||||||
|
message: data.message ?? "Pesan berhasil dikirim."
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
setSubmitState({
|
||||||
|
type: "error",
|
||||||
|
message: "Terjadi kendala jaringan saat mengirim pesan."
|
||||||
|
});
|
||||||
|
await loadCaptcha();
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className="contact-form" onSubmit={handleSubmit}>
|
||||||
|
<input type="hidden" name="startedAt" value={startedAt} />
|
||||||
|
<div className="contact-honeypot" aria-hidden="true">
|
||||||
|
<label htmlFor="website">
|
||||||
|
Website
|
||||||
|
<input
|
||||||
|
id="website"
|
||||||
|
name="website"
|
||||||
|
type="text"
|
||||||
|
tabIndex={-1}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="contact-form-grid">
|
||||||
|
<label className="contact-field">
|
||||||
|
<span>Nama Lengkap</span>
|
||||||
|
<input name="fullName" type="text" placeholder="Masukkan nama Anda" required />
|
||||||
|
</label>
|
||||||
|
<label className="contact-field">
|
||||||
|
<span>Alamat Email</span>
|
||||||
|
<input name="email" type="email" placeholder="nama@email.com" required />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label className="contact-field">
|
||||||
|
<span>Subjek Layanan</span>
|
||||||
|
<select name="subject" defaultValue="Konsultasi Pertanian">
|
||||||
|
<option>Konsultasi Pertanian</option>
|
||||||
|
<option>Teknologi Smart Farming</option>
|
||||||
|
<option>Kemitraan Bisnis</option>
|
||||||
|
<option>Lainnya</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="contact-field">
|
||||||
|
<span>Pesan Anda</span>
|
||||||
|
<textarea
|
||||||
|
name="message"
|
||||||
|
rows={6}
|
||||||
|
placeholder="Ceritakan kebutuhan pertanian Anda..."
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="contact-captcha-card">
|
||||||
|
<div className="contact-captcha-copy">
|
||||||
|
<span>Verifikasi Anti-Bot</span>
|
||||||
|
<strong>{captchaLoading ? "Memuat captcha..." : `Berapa hasil ${captchaPrompt}`}</strong>
|
||||||
|
</div>
|
||||||
|
<div className="contact-captcha-row">
|
||||||
|
<input
|
||||||
|
name="captchaAnswer"
|
||||||
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
placeholder="Jawaban captcha"
|
||||||
|
required
|
||||||
|
disabled={captchaLoading}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="button button-secondary contact-captcha-refresh"
|
||||||
|
type="button"
|
||||||
|
onClick={() => void loadCaptcha()}
|
||||||
|
disabled={captchaLoading || isSubmitting}
|
||||||
|
>
|
||||||
|
Ulangi
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{submitState.message ? (
|
||||||
|
<p
|
||||||
|
className={`contact-submit-message contact-submit-message-${submitState.type}`}
|
||||||
|
role="status"
|
||||||
|
>
|
||||||
|
{submitState.message}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<button className="button button-primary contact-submit" type="submit" disabled={isSubmitting}>
|
||||||
|
{isSubmitting ? "Mengirim..." : "Kirim Pesan Sekarang"}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
145
app/contact/page.tsx
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import contactHeroImage from "../../images/file8.jpg";
|
||||||
|
import mapImage from "../../images/alpukat.jpg";
|
||||||
|
import { ContactForm } from "./contact-form";
|
||||||
|
|
||||||
|
const contactNavItems = [
|
||||||
|
{ label: "Home", href: "/" },
|
||||||
|
{ label: "Tentang", href: "/about" },
|
||||||
|
{ label: "Layanan", href: "/services" },
|
||||||
|
{ label: "Kontak", href: "/contact", active: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
const contactDetails = [
|
||||||
|
{
|
||||||
|
title: "Alamat Kantor",
|
||||||
|
value:
|
||||||
|
"Jl. Raya Petir Kp. Babakan RT. 001 RW. 001 Babakan Dramaga Kabupaten Bogor Jawa Barat 16680"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "WhatsApp/Telepon",
|
||||||
|
value: "+0852-8403-6641"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Email",
|
||||||
|
value: "info@kelolabumi.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Media Sosial",
|
||||||
|
value: "@kelolabumiofficial"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
return (
|
||||||
|
<main className="page-shell">
|
||||||
|
<header className="topbar">
|
||||||
|
<div className="container topbar-inner">
|
||||||
|
<a className="brand" href="/" aria-label="Kelola Bumi">
|
||||||
|
<Image
|
||||||
|
src="/logo_kelolabumi.png"
|
||||||
|
alt="Kelola Bumi Logo"
|
||||||
|
width={240}
|
||||||
|
height={40}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav className="nav desktop-nav" aria-label="Navigasi utama">
|
||||||
|
{contactNavItems.map((item) => (
|
||||||
|
<a
|
||||||
|
key={item.label}
|
||||||
|
href={item.href}
|
||||||
|
className={item.active ? "nav-active" : undefined}
|
||||||
|
aria-current={item.active ? "page" : undefined}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<a className="button button-primary topbar-cta" href="mailto:info@kelolabumi.com">
|
||||||
|
Konsultasi Sekarang
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="contact-hero">
|
||||||
|
<div className="contact-hero-media" aria-hidden="true">
|
||||||
|
<Image
|
||||||
|
src={contactHeroImage}
|
||||||
|
alt=""
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
className="contact-hero-image"
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
<div className="contact-hero-overlay" />
|
||||||
|
</div>
|
||||||
|
<div className="container contact-hero-content">
|
||||||
|
<h1>Mari Bertumbuh Bersama PT. Kelola Bumi Nusantara</h1>
|
||||||
|
<p>
|
||||||
|
Solusi teknologi pertanian presisi untuk masa depan yang lebih hijau
|
||||||
|
dan berkelanjutan.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-light">
|
||||||
|
<div className="container contact-grid">
|
||||||
|
<div className="contact-sidebar">
|
||||||
|
<div className="contact-list">
|
||||||
|
{contactDetails.map((item, index) => (
|
||||||
|
<article key={item.title} className="contact-item">
|
||||||
|
<div className="contact-icon">{String(index + 1).padStart(2, "0")}</div>
|
||||||
|
<div>
|
||||||
|
<h3>{item.title}</h3>
|
||||||
|
<p>{item.value}</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="contact-map-card">
|
||||||
|
<Image
|
||||||
|
src={mapImage}
|
||||||
|
alt="Lokasi Kelola Bumi di Bogor"
|
||||||
|
fill
|
||||||
|
className="contact-map-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 42vw"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
className="contact-map-badge"
|
||||||
|
href="https://www.google.com/maps?q=-6.3580933,106.8108942"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Kunjungi Kami di Bogor
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="contact-form-card">
|
||||||
|
<h2>Kirim Pesan</h2>
|
||||||
|
<ContactForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="container footer-inner">
|
||||||
|
<div>
|
||||||
|
<div className="footer-brand">Kelola Bumi</div>
|
||||||
|
<p>© 2026 PT Kelola Bumi Nusantara. An Agricultural Company.</p>
|
||||||
|
</div>
|
||||||
|
<div className="footer-links">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<a href="/about">Tentang</a>
|
||||||
|
<a href="/services">Layanan</a>
|
||||||
|
<a href="/contact">Kontak</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
BIN
app/favicon.ico
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
1591
app/globals.css
Normal file
25
app/layout.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Kelola Bumi - An Agricultural Company",
|
||||||
|
description:
|
||||||
|
"Kelola Bumi menghadirkan konsultasi, sarana produksi, dan akses pasar untuk pertanian berkelanjutan di Indonesia.",
|
||||||
|
icons: {
|
||||||
|
icon: "/favicon.ico",
|
||||||
|
shortcut: "/favicon.ico",
|
||||||
|
apple: "/favicon.ico"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="id">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
343
app/page.tsx
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
import Image, { type StaticImageData } from "next/image";
|
||||||
|
import alpukatImage from "../images/alpukat.jpg";
|
||||||
|
import fileEightImage from "../images/file8.jpg";
|
||||||
|
import kelorImage from "../images/kelor.jpg";
|
||||||
|
import kambingImage from "../images/kambing-pe.jpg";
|
||||||
|
import udangImage from "../images/udang-1.jpg";
|
||||||
|
|
||||||
|
type MiniCard = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: string;
|
||||||
|
tone: "primary" | "secondary" | "tertiary";
|
||||||
|
};
|
||||||
|
|
||||||
|
type ServiceCard = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image: StaticImageData;
|
||||||
|
badgeLeft: string;
|
||||||
|
badgeRight: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ label: "Home", href: "#home" },
|
||||||
|
{ label: "Tentang", href: "/about" },
|
||||||
|
{ label: "Layanan", href: "/services" },
|
||||||
|
{ label: "Kontak", href: "/contact" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const miniCards: MiniCard[] = [
|
||||||
|
{
|
||||||
|
title: "Konsultasi Ahli",
|
||||||
|
description:
|
||||||
|
"Strategi agronomis berbasis data untuk menaikkan produktivitas lahan dengan pendekatan terukur.",
|
||||||
|
icon: "01",
|
||||||
|
tone: "primary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Sarana Produksi",
|
||||||
|
description:
|
||||||
|
"Benih, pupuk, bibit, dan dukungan budidaya yang dipilih untuk menjaga kualitas serta efisiensi usaha tani.",
|
||||||
|
icon: "02",
|
||||||
|
tone: "secondary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Distribusi Unggul",
|
||||||
|
description:
|
||||||
|
"Pengelolaan hasil panen dan komoditas agribisnis agar sampai ke pasar dengan nilai ekonomi terbaik.",
|
||||||
|
icon: "03",
|
||||||
|
tone: "tertiary"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const serviceCards: ServiceCard[] = [
|
||||||
|
{
|
||||||
|
title: "Agronomy Consulting",
|
||||||
|
description:
|
||||||
|
"Pemetaan kebutuhan kebun, evaluasi varietas, dan pendampingan tanam sampai panen untuk lahan produktif.",
|
||||||
|
image: kelorImage,
|
||||||
|
badgeLeft: "Certified Experts",
|
||||||
|
badgeRight: "Data Driven"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Integrated Supply",
|
||||||
|
description:
|
||||||
|
"Rantai pasok input agribisnis dari pembibitan, penguatan kebun, sampai pengembangan komoditas unggulan.",
|
||||||
|
image: alpukatImage,
|
||||||
|
badgeLeft: "Premium Input",
|
||||||
|
badgeRight: "Reliable Network"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Market Access",
|
||||||
|
description:
|
||||||
|
"Konektivitas pasar untuk komoditas hasil kebun, peternakan, dan perikanan melalui jejaring distribusi yang rapi.",
|
||||||
|
image: udangImage,
|
||||||
|
badgeLeft: "Direct Market",
|
||||||
|
badgeRight: "Fair Value"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
|
return (
|
||||||
|
<main className="page-shell" id="home">
|
||||||
|
<header className="topbar">
|
||||||
|
<div className="container topbar-inner">
|
||||||
|
<a className="brand" href="#home" aria-label="Kelola Bumi">
|
||||||
|
<Image
|
||||||
|
src="/logo_kelolabumi.png"
|
||||||
|
alt="Kelola Bumi Logo"
|
||||||
|
width={240}
|
||||||
|
height={40}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav className="nav desktop-nav" aria-label="Navigasi utama">
|
||||||
|
{navItems.map((item) => (
|
||||||
|
<a key={item.label} href={item.href}>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<a className="button button-primary topbar-cta" href="/contact">
|
||||||
|
Konsultasi Sekarang
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="hero">
|
||||||
|
<div className="hero-image-wrap" aria-hidden="true">
|
||||||
|
<Image
|
||||||
|
src={fileEightImage}
|
||||||
|
alt=""
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
className="hero-image"
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
<div className="hero-overlay" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container hero-content">
|
||||||
|
<div className="hero-copy">
|
||||||
|
<span className="eyebrow">SINCE 2015 • PRECISION AGRICULTURE</span>
|
||||||
|
<h1>Solusi terintegrasi untuk pertanian berkelanjutan dan inovatif.</h1>
|
||||||
|
<p>
|
||||||
|
Kelola Bumi membangun ekosistem agribisnis dari hulu ke hilir
|
||||||
|
melalui konsultasi ahli, pengadaan sarana produksi, dan distribusi
|
||||||
|
komoditas unggulan Indonesia.
|
||||||
|
</p>
|
||||||
|
<div className="hero-actions">
|
||||||
|
<a className="button button-primary" href="#layanan">
|
||||||
|
Mulai Transformasi
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
<a className="button button-secondary" href="/about">
|
||||||
|
Lihat Profil Perusahaan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hero-panel">
|
||||||
|
<div className="hero-stat-card">
|
||||||
|
<span className="stat-label">Fokus Ekosistem</span>
|
||||||
|
<strong>Tanaman, peternakan, dan perikanan</strong>
|
||||||
|
<p>
|
||||||
|
Portofolio aset lokal diintegrasikan dalam pendekatan budidaya
|
||||||
|
yang relevan dengan kondisi lapangan.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hero-highlight-grid">
|
||||||
|
<article>
|
||||||
|
<span>01</span>
|
||||||
|
<h3>Produktivitas</h3>
|
||||||
|
<p>Optimasi lahan berbasis kebutuhan spesifik komoditas.</p>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<span>02</span>
|
||||||
|
<h3>Keberlanjutan</h3>
|
||||||
|
<p>Pendekatan budidaya yang menjaga mutu tanah dan hasil.</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-light" id="tentang">
|
||||||
|
<div className="container">
|
||||||
|
<div className="section-heading section-heading-centered">
|
||||||
|
<p className="section-kicker">Tentang Kami</p>
|
||||||
|
<h2>Pionir agribisnis modern yang membumi.</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bento-grid">
|
||||||
|
<article className="bento-identity">
|
||||||
|
<div className="media-backdrop">
|
||||||
|
<Image
|
||||||
|
src={kelorImage}
|
||||||
|
alt="Komoditas tanaman Kelola Bumi"
|
||||||
|
fill
|
||||||
|
className="media-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 66vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="identity-copy">
|
||||||
|
<p className="section-kicker">Profil Singkat</p>
|
||||||
|
<h3>Menjembatani potensi lokal dengan sistem budidaya presisi.</h3>
|
||||||
|
<p>
|
||||||
|
Kelola Bumi hadir untuk mempertemukan pengalaman lapangan,
|
||||||
|
jaringan distribusi, dan orientasi kualitas dalam satu rantai
|
||||||
|
nilai agribisnis yang lebih sehat.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article className="bento-years">
|
||||||
|
<div className="icon-badge">KB</div>
|
||||||
|
<div>
|
||||||
|
<strong>9+</strong>
|
||||||
|
<h3>Tahun Berdedikasi</h3>
|
||||||
|
<p>
|
||||||
|
Mengawal pengembangan komoditas dengan disiplin operasional
|
||||||
|
sejak 2015.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{miniCards.map((card) => (
|
||||||
|
<article
|
||||||
|
key={card.title}
|
||||||
|
className={`mini-card mini-card-${card.tone}`}
|
||||||
|
id={card.title === "Distribusi Unggul" ? "keunggulan" : undefined}
|
||||||
|
>
|
||||||
|
<div className="mini-icon">{card.icon}</div>
|
||||||
|
<h4>{card.title}</h4>
|
||||||
|
<p>{card.description}</p>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-soft" id="layanan">
|
||||||
|
<div className="container">
|
||||||
|
<div className="section-heading section-heading-split">
|
||||||
|
<div>
|
||||||
|
<p className="section-kicker">Layanan Inti</p>
|
||||||
|
<h2>Layanan terintegrasi untuk setiap tahap pengembangan usaha.</h2>
|
||||||
|
</div>
|
||||||
|
<p className="section-side-note">
|
||||||
|
Pendekatan holistik untuk pembibitan, penguatan kebun, pengolahan
|
||||||
|
komoditas, dan akses pasar yang lebih stabil.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="services-grid">
|
||||||
|
{serviceCards.map((card) => (
|
||||||
|
<article key={card.title} className="service-card">
|
||||||
|
<div className="service-image-wrap">
|
||||||
|
<Image
|
||||||
|
src={card.image}
|
||||||
|
alt={card.title}
|
||||||
|
fill
|
||||||
|
className="service-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="service-body">
|
||||||
|
<h3>{card.title}</h3>
|
||||||
|
<p>{card.description}</p>
|
||||||
|
<div className="service-badges">
|
||||||
|
<span>{card.badgeLeft}</span>
|
||||||
|
<span>{card.badgeRight}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="section section-showcase">
|
||||||
|
<div className="container showcase-grid">
|
||||||
|
<div className="showcase-copy">
|
||||||
|
<p className="section-kicker">Portofolio Komoditas</p>
|
||||||
|
<h2>Dukungan visual dan lapangan yang dekat dengan realitas agribisnis.</h2>
|
||||||
|
<p>
|
||||||
|
Aset lokal dari folder `images` kami gunakan untuk membangun
|
||||||
|
identitas visual yang terasa relevan dengan sektor pertanian,
|
||||||
|
peternakan, dan perikanan.
|
||||||
|
</p>
|
||||||
|
<a className="button button-primary" href="/contact">
|
||||||
|
Diskusikan Kebutuhan Anda
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="showcase-stack" aria-label="Galeri komoditas unggulan">
|
||||||
|
<figure className="showcase-tall">
|
||||||
|
<Image
|
||||||
|
src={alpukatImage}
|
||||||
|
alt="Komoditas alpukat"
|
||||||
|
fill
|
||||||
|
className="showcase-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 30vw"
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
<figure className="showcase-small">
|
||||||
|
<Image
|
||||||
|
src={kambingImage}
|
||||||
|
alt="Peternakan kambing"
|
||||||
|
fill
|
||||||
|
className="showcase-image"
|
||||||
|
sizes="(max-width: 1024px) 50vw, 20vw"
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
<figure className="showcase-small">
|
||||||
|
<Image
|
||||||
|
src={udangImage}
|
||||||
|
alt="Budidaya udang"
|
||||||
|
fill
|
||||||
|
className="showcase-image"
|
||||||
|
sizes="(max-width: 1024px) 50vw, 20vw"
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="cta-section" id="cta-konsultasi">
|
||||||
|
<div className="container cta-inner">
|
||||||
|
<div>
|
||||||
|
<p className="section-kicker cta-kicker">Mulai Konsultasi</p>
|
||||||
|
<h2>Siap memajukan pertanian bersama Kelola Bumi?</h2>
|
||||||
|
<p>
|
||||||
|
Hubungi tim kami untuk membahas strategi budidaya, kebutuhan
|
||||||
|
pasokan, atau pengembangan pasar yang paling sesuai.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a className="button button-accent" href="/contact">
|
||||||
|
Jadwalkan Konsultasi Gratis
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="container footer-inner">
|
||||||
|
<div>
|
||||||
|
<div className="footer-brand">Kelola Bumi</div>
|
||||||
|
<p>© 2026 PT Kelola Bumi Nusantara. An Agricultural Company.</p>
|
||||||
|
</div>
|
||||||
|
<div className="footer-links">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<a href="/about">Tentang</a>
|
||||||
|
<a href="/services">Layanan</a>
|
||||||
|
<a href="/contact">Kontak</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
216
app/services/page.tsx
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import alpukatImage from "../../images/alpukat.jpg";
|
||||||
|
import kelorImage from "../../images/kelor.jpg";
|
||||||
|
import udangImage from "../../images/udang-1.jpg";
|
||||||
|
import kambingImage from "../../images/kambing-pe.jpg";
|
||||||
|
|
||||||
|
const servicesNavItems = [
|
||||||
|
{ label: "Home", href: "/" },
|
||||||
|
{ label: "Tentang", href: "/about" },
|
||||||
|
{ label: "Layanan", href: "/services", active: true },
|
||||||
|
{ label: "Kontak", href: "/contact" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const serviceHighlights = [
|
||||||
|
"Analisis tanah dan lingkungan",
|
||||||
|
"Pendampingan pengendalian hama terpadu",
|
||||||
|
"Implementasi teknologi smart farming"
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ServicesPage() {
|
||||||
|
return (
|
||||||
|
<main className="page-shell">
|
||||||
|
<header className="topbar">
|
||||||
|
<div className="container topbar-inner">
|
||||||
|
<a className="brand" href="/" aria-label="Kelola Bumi">
|
||||||
|
<Image
|
||||||
|
src="/logo_kelolabumi.png"
|
||||||
|
alt="Kelola Bumi Logo"
|
||||||
|
width={240}
|
||||||
|
height={40}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav className="nav desktop-nav" aria-label="Navigasi utama">
|
||||||
|
{servicesNavItems.map((item) => (
|
||||||
|
<a
|
||||||
|
key={item.label}
|
||||||
|
href={item.href}
|
||||||
|
className={item.active ? "nav-active" : undefined}
|
||||||
|
aria-current={item.active ? "page" : undefined}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<a className="button button-primary topbar-cta" href="/contact">
|
||||||
|
Konsultasi Sekarang
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="services-page-shell">
|
||||||
|
<div className="container">
|
||||||
|
<section className="services-hero">
|
||||||
|
<span className="services-kicker">Ekosistem Kami</span>
|
||||||
|
<h1>
|
||||||
|
Pertanian berkelanjutan, <span>dikelola dengan presisi.</span>
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
Kelola Bumi mendukung agribisnis Indonesia melalui konsultasi
|
||||||
|
modern, sarana produksi unggul, dan integrasi pasar yang rapi.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="services-bento-grid">
|
||||||
|
<article className="services-feature services-feature-large">
|
||||||
|
<div className="services-feature-copy">
|
||||||
|
<div className="services-badge-icon">01</div>
|
||||||
|
<h3>Jasa Konsultasi Pertanian</h3>
|
||||||
|
<p>
|
||||||
|
Pendampingan strategis berbasis data lapangan untuk membantu
|
||||||
|
optimalisasi hasil dan pengelolaan usaha yang lebih terukur.
|
||||||
|
</p>
|
||||||
|
<ul className="services-checklist">
|
||||||
|
{serviceHighlights.map((item) => (
|
||||||
|
<li key={item}>{item}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<a className="services-inline-link" href="/contact">
|
||||||
|
Pelajari Lebih Lanjut
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="services-feature-media">
|
||||||
|
<Image
|
||||||
|
src={kelorImage}
|
||||||
|
alt="Konsultasi pertanian"
|
||||||
|
fill
|
||||||
|
className="services-media-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 25vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article className="services-feature services-feature-card">
|
||||||
|
<div className="services-card-image">
|
||||||
|
<Image
|
||||||
|
src={alpukatImage}
|
||||||
|
alt="Benih dan bibit unggul"
|
||||||
|
fill
|
||||||
|
className="services-media-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||||
|
/>
|
||||||
|
<span className="services-card-tag">Premium Grade</span>
|
||||||
|
</div>
|
||||||
|
<div className="services-card-body">
|
||||||
|
<div className="services-badge-icon services-badge-secondary">02</div>
|
||||||
|
<h3>Benih & Bibit Unggul</h3>
|
||||||
|
<p>
|
||||||
|
Bibit dan bahan tanam terpilih untuk produktivitas tinggi,
|
||||||
|
adaptif terhadap iklim lokal, dan cocok untuk pengembangan
|
||||||
|
komoditas strategis.
|
||||||
|
</p>
|
||||||
|
<a className="services-inline-link services-link-secondary" href="/contact">
|
||||||
|
Lihat Katalog
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article className="services-feature services-feature-card">
|
||||||
|
<div className="services-card-image">
|
||||||
|
<Image
|
||||||
|
src={kambingImage}
|
||||||
|
alt="Pupuk dan nutrisi tanaman"
|
||||||
|
fill
|
||||||
|
className="services-media-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="services-card-body">
|
||||||
|
<div className="services-badge-icon services-badge-tertiary">03</div>
|
||||||
|
<h3>Pupuk & Nutrisi</h3>
|
||||||
|
<p>
|
||||||
|
Distribusi nutrisi organik dan anorganik yang disesuaikan
|
||||||
|
dengan profil lahan, tahap tumbuh, dan target produktivitas.
|
||||||
|
</p>
|
||||||
|
<a className="services-inline-link services-link-tertiary" href="/contact">
|
||||||
|
Panduan Nutrisi
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article className="services-feature services-feature-market">
|
||||||
|
<div className="services-feature-copy">
|
||||||
|
<div className="services-badge-icon services-badge-market">04</div>
|
||||||
|
<h3>Penjualan Hasil Pertanian</h3>
|
||||||
|
<p>
|
||||||
|
Kami menjembatani produsen dengan pasar yang lebih luas agar
|
||||||
|
penyerapan hasil, harga, dan distribusi berjalan lebih efisien.
|
||||||
|
</p>
|
||||||
|
<div className="services-stats-grid">
|
||||||
|
<div>
|
||||||
|
<strong>85%</strong>
|
||||||
|
<span>Efisiensi rantai pasok</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>120+</strong>
|
||||||
|
<span>Mitra pasar</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a className="button button-secondary" href="/contact">
|
||||||
|
Ajukan Kemitraan
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="services-market-media">
|
||||||
|
<Image
|
||||||
|
src={udangImage}
|
||||||
|
alt="Penjualan hasil pertanian"
|
||||||
|
fill
|
||||||
|
className="services-media-image"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 45vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="services-cta">
|
||||||
|
<div className="services-cta-content">
|
||||||
|
<h2>Siap mengoptimalkan hasil panen Anda?</h2>
|
||||||
|
<p>
|
||||||
|
Bergabunglah dengan pendekatan pertanian presisi bersama PT.
|
||||||
|
Kelola Bumi Nusantara.
|
||||||
|
</p>
|
||||||
|
<div className="services-cta-actions">
|
||||||
|
<a className="button services-cta-primary" href="/contact">
|
||||||
|
Mulai Sekarang
|
||||||
|
</a>
|
||||||
|
<a className="button services-cta-secondary" href="/contact">
|
||||||
|
Bicara dengan Spesialis
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="services-cta-orb services-cta-orb-green" />
|
||||||
|
<div className="services-cta-orb services-cta-orb-orange" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer className="footer">
|
||||||
|
<div className="container footer-inner">
|
||||||
|
<div>
|
||||||
|
<div className="footer-brand">Kelola Bumi</div>
|
||||||
|
<p>© 2026 PT Kelola Bumi Nusantara. An Agricultural Company.</p>
|
||||||
|
</div>
|
||||||
|
<div className="footer-links">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<a href="/about">Tentang</a>
|
||||||
|
<a href="/services">Layanan</a>
|
||||||
|
<a href="/contact">Kontak</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
BIN
favicon.ico
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
images/CC.jpg
Executable file
|
After Width: | Height: | Size: 340 KiB |
BIN
images/CM.jpg
Executable file
|
After Width: | Height: | Size: 248 KiB |
BIN
images/CM2.jpg
Executable file
|
After Width: | Height: | Size: 184 KiB |
BIN
images/CP.jpg
Executable file
|
After Width: | Height: | Size: 132 KiB |
BIN
images/CP2.jpg
Executable file
|
After Width: | Height: | Size: 283 KiB |
BIN
images/CRY-(orok-orok).jpg
Executable file
|
After Width: | Height: | Size: 438 KiB |
BIN
images/MC-(mucuna).jpg
Executable file
|
After Width: | Height: | Size: 328 KiB |
BIN
images/MC2.jpg
Executable file
|
After Width: | Height: | Size: 200 KiB |
BIN
images/PJ-3.jpg
Executable file
|
After Width: | Height: | Size: 428 KiB |
BIN
images/PJ.jpg
Executable file
|
After Width: | Height: | Size: 205 KiB |
BIN
images/PJ2.jpg
Executable file
|
After Width: | Height: | Size: 486 KiB |
BIN
images/Solobium-Parahiba-2.jpg
Executable file
|
After Width: | Height: | Size: 348 KiB |
BIN
images/akasia-krasikarpa.jpg
Executable file
|
After Width: | Height: | Size: 238 KiB |
BIN
images/akasia-krasikarpa2.jpg
Executable file
|
After Width: | Height: | Size: 274 KiB |
BIN
images/akasia-krasikarpa3.jpg
Executable file
|
After Width: | Height: | Size: 369 KiB |
BIN
images/akasia-krasikarpa4.jpg
Executable file
|
After Width: | Height: | Size: 156 KiB |
BIN
images/akasia-mangium-2.jpg
Executable file
|
After Width: | Height: | Size: 320 KiB |
BIN
images/akasia-mangium.jpg
Executable file
|
After Width: | Height: | Size: 278 KiB |
BIN
images/alligator.jpg
Executable file
|
After Width: | Height: | Size: 136 KiB |
BIN
images/alpukat-miki.jpg
Executable file
|
After Width: | Height: | Size: 131 KiB |
BIN
images/alpukat.jpg
Executable file
|
After Width: | Height: | Size: 216 KiB |
BIN
images/alpukat2.jpg
Executable file
|
After Width: | Height: | Size: 239 KiB |
BIN
images/alpukat3.jpg
Executable file
|
After Width: | Height: | Size: 374 KiB |
BIN
images/alpukat4.jpg
Executable file
|
After Width: | Height: | Size: 703 KiB |
BIN
images/alpukat5.jpg
Executable file
|
After Width: | Height: | Size: 710 KiB |
BIN
images/alpukat6.jpg
Executable file
|
After Width: | Height: | Size: 634 KiB |
BIN
images/bakau.jpg
Executable file
|
After Width: | Height: | Size: 152 KiB |
BIN
images/balsa.jpg
Executable file
|
After Width: | Height: | Size: 187 KiB |
BIN
images/balsa2.jpg
Executable file
|
After Width: | Height: | Size: 387 KiB |
BIN
images/balsa3.jpg
Executable file
|
After Width: | Height: | Size: 587 KiB |
BIN
images/balsa4.jpg
Executable file
|
After Width: | Height: | Size: 540 KiB |
BIN
images/damar.jpg
Executable file
|
After Width: | Height: | Size: 659 KiB |
BIN
images/damar2.jpg
Executable file
|
After Width: | Height: | Size: 400 KiB |
BIN
images/domba-merino.jpg
Executable file
|
After Width: | Height: | Size: 254 KiB |
BIN
images/domba-merino2.jpg
Executable file
|
After Width: | Height: | Size: 235 KiB |
BIN
images/domba-merino3.jpg
Executable file
|
After Width: | Height: | Size: 198 KiB |
BIN
images/durian.jpg
Executable file
|
After Width: | Height: | Size: 333 KiB |
BIN
images/ekaliptus-pelita-asal-banjarmasin.jpg
Executable file
|
After Width: | Height: | Size: 518 KiB |
BIN
images/ekaliptus-pelita.jpg
Executable file
|
After Width: | Height: | Size: 245 KiB |
BIN
images/ekaliptus.jpg
Executable file
|
After Width: | Height: | Size: 559 KiB |
BIN
images/ekaliptus2.jpg
Executable file
|
After Width: | Height: | Size: 667 KiB |
BIN
images/file1.jpg
Executable file
|
After Width: | Height: | Size: 206 KiB |
BIN
images/file2.jpg
Executable file
|
After Width: | Height: | Size: 359 KiB |
BIN
images/file3.jpg
Executable file
|
After Width: | Height: | Size: 258 KiB |
BIN
images/file4.jpg
Executable file
|
After Width: | Height: | Size: 235 KiB |
BIN
images/file5.jpg
Executable file
|
After Width: | Height: | Size: 405 KiB |
BIN
images/file6.jpg
Executable file
|
After Width: | Height: | Size: 319 KiB |
BIN
images/file7.jpg
Executable file
|
After Width: | Height: | Size: 258 KiB |
BIN
images/file8.jpg
Executable file
|
After Width: | Height: | Size: 305 KiB |
BIN
images/flamboyan.jpg
Executable file
|
After Width: | Height: | Size: 203 KiB |
BIN
images/gaharu-aquilaria-malacensis.jpg
Executable file
|
After Width: | Height: | Size: 70 KiB |
BIN
images/gaharu-aquilaria-malacensis2.jpg
Executable file
|
After Width: | Height: | Size: 123 KiB |
BIN
images/gaharu-aquilaria-malacensis3.jpg
Executable file
|
After Width: | Height: | Size: 89 KiB |
BIN
images/gamal.jpg
Executable file
|
After Width: | Height: | Size: 212 KiB |
BIN
images/gmelina.jpg
Executable file
|
After Width: | Height: | Size: 102 KiB |
BIN
images/indigofera.jpg
Executable file
|
After Width: | Height: | Size: 257 KiB |
BIN
images/jabon-putih-2.jpg
Executable file
|
After Width: | Height: | Size: 339 KiB |
BIN
images/jabon-putih.jpg
Executable file
|
After Width: | Height: | Size: 409 KiB |
BIN
images/jengkol.jpg
Executable file
|
After Width: | Height: | Size: 555 KiB |
BIN
images/johar.jpg
Executable file
|
After Width: | Height: | Size: 173 KiB |
BIN
images/kaliandra-merah.jpg
Executable file
|
After Width: | Height: | Size: 81 KiB |
BIN
images/kambing-pe.jpg
Executable file
|
After Width: | Height: | Size: 90 KiB |
BIN
images/kambing-pe2.jpg
Executable file
|
After Width: | Height: | Size: 90 KiB |
BIN
images/kapuk.jpg
Executable file
|
After Width: | Height: | Size: 134 KiB |
BIN
images/kayu-putih.jpg
Executable file
|
After Width: | Height: | Size: 276 KiB |
BIN
images/kayu-putih2.jpg
Executable file
|
After Width: | Height: | Size: 352 KiB |
BIN
images/kelor.jpg
Executable file
|
After Width: | Height: | Size: 255 KiB |
BIN
images/kemiri-sunan.jpg
Executable file
|
After Width: | Height: | Size: 478 KiB |
BIN
images/kemiri-sunan2.jpg
Executable file
|
After Width: | Height: | Size: 439 KiB |
BIN
images/lengkeng.jpg
Executable file
|
After Width: | Height: | Size: 618 KiB |
BIN
images/mahoni-2.jpg
Executable file
|
After Width: | Height: | Size: 689 KiB |
BIN
images/mahoni.jpg
Executable file
|
After Width: | Height: | Size: 205 KiB |
BIN
images/mahoni3.jpg
Executable file
|
After Width: | Height: | Size: 328 KiB |
BIN
images/mahoni4.jpg
Executable file
|
After Width: | Height: | Size: 273 KiB |
BIN
images/manggis.jpg
Executable file
|
After Width: | Height: | Size: 730 KiB |
BIN
images/manggis2.jpg
Executable file
|
After Width: | Height: | Size: 692 KiB |
BIN
images/manggis3.jpg
Executable file
|
After Width: | Height: | Size: 650 KiB |
BIN
images/markus.jpg
Executable file
|
After Width: | Height: | Size: 129 KiB |
BIN
images/mb-india.jpg
Executable file
|
After Width: | Height: | Size: 384 KiB |
BIN
images/mete.jpg
Executable file
|
After Width: | Height: | Size: 192 KiB |
BIN
images/rambutan.jpg
Executable file
|
After Width: | Height: | Size: 890 KiB |
BIN
images/secang.jpg
Executable file
|
After Width: | Height: | Size: 159 KiB |
BIN
images/sengon-buto.jpg
Executable file
|
After Width: | Height: | Size: 263 KiB |
BIN
images/sengon-laut.jpg
Executable file
|
After Width: | Height: | Size: 190 KiB |
BIN
images/seno-keling.jpg
Executable file
|
After Width: | Height: | Size: 138 KiB |
BIN
images/solobium-parahiba.jpg
Executable file
|
After Width: | Height: | Size: 224 KiB |