Initial Kelola Bumi website

This commit is contained in:
Wira Basalamah
2026-04-23 01:43:48 +07:00
commit 65435dd167
129 changed files with 5434 additions and 0 deletions

7
.env.example Normal file
View 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
View File

@ -0,0 +1,7 @@
.DS_Store
.next
node_modules
out
dist
.env*
!.env.example

51
README.md Normal file
View 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
View 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>
);
}

View 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
View 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 }
);
}
}

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

1591
app/globals.css Normal file

File diff suppressed because it is too large Load Diff

25
app/layout.tsx Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/CC.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

BIN
images/CM.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
images/CM2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

BIN
images/CP.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
images/CP2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

BIN
images/CRY-(orok-orok).jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

BIN
images/MC-(mucuna).jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

BIN
images/MC2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

BIN
images/PJ-3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

BIN
images/PJ.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

BIN
images/PJ2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

BIN
images/Solobium-Parahiba-2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
images/akasia-krasikarpa.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
images/akasia-krasikarpa2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

BIN
images/akasia-krasikarpa3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

BIN
images/akasia-krasikarpa4.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
images/akasia-mangium-2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

BIN
images/akasia-mangium.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

BIN
images/alligator.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
images/alpukat-miki.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
images/alpukat.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

BIN
images/alpukat2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

BIN
images/alpukat3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

BIN
images/alpukat4.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 KiB

BIN
images/alpukat5.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

BIN
images/alpukat6.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

BIN
images/bakau.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

BIN
images/balsa.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
images/balsa2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

BIN
images/balsa3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

BIN
images/balsa4.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

BIN
images/damar.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

BIN
images/damar2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

BIN
images/domba-merino.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

BIN
images/domba-merino2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

BIN
images/domba-merino3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
images/durian.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

BIN
images/ekaliptus-pelita.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
images/ekaliptus.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

BIN
images/ekaliptus2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 KiB

BIN
images/file1.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

BIN
images/file2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

BIN
images/file3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

BIN
images/file4.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

BIN
images/file5.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

BIN
images/file6.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

BIN
images/file7.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

BIN
images/file8.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

BIN
images/flamboyan.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
images/gamal.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
images/gmelina.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
images/indigofera.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

BIN
images/jabon-putih-2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

BIN
images/jabon-putih.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

BIN
images/jengkol.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

BIN
images/johar.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
images/kaliandra-merah.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
images/kambing-pe.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
images/kambing-pe2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
images/kapuk.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
images/kayu-putih.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

BIN
images/kayu-putih2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
images/kelor.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

BIN
images/kemiri-sunan.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

BIN
images/kemiri-sunan2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

BIN
images/lengkeng.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

BIN
images/mahoni-2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 KiB

BIN
images/mahoni.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

BIN
images/mahoni3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

BIN
images/mahoni4.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

BIN
images/manggis.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

BIN
images/manggis2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 KiB

BIN
images/manggis3.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

BIN
images/markus.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
images/mb-india.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

BIN
images/mete.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
images/rambutan.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

BIN
images/secang.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
images/sengon-buto.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

BIN
images/sengon-laut.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
images/seno-keling.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
images/solobium-parahiba.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Some files were not shown because too many files have changed in this diff Show More