diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..68b2a5b --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,110 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { useLang } from "@/context/LanguageContext"; +import { t } from "@/lib/translations"; + +const statValues = ["10+", "50+", "99%", "5+"]; + +export default function AboutPage() { + const { lang } = useLang(); + const tr = t[lang].about; + + return ( + <> + {/* Hero */} +
+
+
+

{tr.badge}

+

+ {tr.heroTitle} +

+

{tr.heroP1}

+

{tr.heroP2}

+
+
+
+ Tim IPTEK +
+
+
+
+ + {/* Stats */} +
+
+
+ {statValues.map((value, i) => ( +
+
{value}
+
{tr.stats[i]}
+
+ ))} +
+
+
+ + {/* Mission & Vision */} +
+
+
+
+
+ visibility +
+

{tr.visionTitle}

+

{tr.visionDesc}

+
+
+
+ flag +
+

{tr.missionTitle}

+

{tr.missionDesc}

+
+
+
+
+ + {/* Values */} +
+
+
+

{tr.valuesTitle}

+

{tr.valuesSub}

+
+
+ {tr.values.map((v) => ( +
+
+ {v.icon} +
+

{v.title}

+

{v.desc}

+
+ ))} +
+
+
+ + {/* CTA */} +
+
+

{tr.ctaTitle}

+

{tr.ctaSub}

+ + {tr.ctaBtn} + +
+
+ + ); +} diff --git a/app/actions/sendContact.ts b/app/actions/sendContact.ts new file mode 100644 index 0000000..a1625cd --- /dev/null +++ b/app/actions/sendContact.ts @@ -0,0 +1,67 @@ +"use server"; + +import nodemailer from "nodemailer"; + +export type ContactPayload = { + name: string; + email: string; + company: string; + phone: string; + topic: string; + message: string; +}; + +export type SendResult = { ok: true } | { ok: false; error: string }; + +export async function sendContactEmail(data: ContactPayload): Promise { + const { SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS } = process.env; + + if (!SMTP_HOST || !SMTP_USER || !SMTP_PASS) { + return { ok: false, error: "Konfigurasi email server belum diatur." }; + } + + const transporter = nodemailer.createTransport({ + host: SMTP_HOST, + port: Number(SMTP_PORT ?? 587), + secure: Number(SMTP_PORT) === 465, + auth: { user: SMTP_USER, pass: SMTP_PASS }, + }); + + const html = ` +
+
+

Pesan Baru dari Website IPTEK

+
+
+ + + + + + +
Nama${data.name}
Email${data.email}
Perusahaan${data.company || "-"}
WhatsApp${data.phone || "-"}
Topik${data.topic}
+
+

Pesan

+

${data.message}

+
+
+ Dikirim pada ${new Date().toLocaleString("id-ID", { timeZone: "Asia/Jakarta", dateStyle: "long", timeStyle: "short" })} WIB +
+
+
+ `; + + try { + await transporter.sendMail({ + from: `"IPTEK Website" <${SMTP_USER}>`, + to: "support@iptek.co", + replyTo: data.email, + subject: `[${data.topic}] Pesan dari ${data.name} – IPTEK Website`, + html, + }); + return { ok: true }; + } catch (err) { + console.error("Email send error:", err); + return { ok: false, error: "Gagal mengirim pesan. Silakan coba lagi atau hubungi kami langsung." }; + } +} diff --git a/app/contact/ContactForm.tsx b/app/contact/ContactForm.tsx new file mode 100644 index 0000000..31555c3 --- /dev/null +++ b/app/contact/ContactForm.tsx @@ -0,0 +1,277 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { sendContactEmail, type ContactPayload } from "@/app/actions/sendContact"; +import { useLang } from "@/context/LanguageContext"; +import { t } from "@/lib/translations"; + +function generateCaptcha() { + const a = Math.floor(Math.random() * 9) + 1; + const b = Math.floor(Math.random() * 9) + 1; + return { a, b, answer: a + b }; +} + +const inputClass = + "w-full px-4 py-3 rounded-lg border border-outline-variant bg-surface focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all text-on-surface placeholder:text-on-surface-variant/50"; + +export default function ContactForm() { + const { lang } = useLang(); + const tr = t[lang].contact; + + const [formData, setFormData] = useState>({ + name: "", + email: "", + company: "", + phone: "", + topic: "", + message: "", + }); + const [captcha, setCaptcha] = useState({ a: 0, b: 0, answer: 0 }); + const [captchaInput, setCaptchaInput] = useState(""); + const [captchaError, setCaptchaError] = useState(false); + const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle"); + const [errorMsg, setErrorMsg] = useState(""); + + const refreshCaptcha = useCallback(() => { + setCaptcha(generateCaptcha()); + setCaptchaInput(""); + setCaptchaError(false); + }, []); + + useEffect(() => { + refreshCaptcha(); + }, [refreshCaptcha]); + + function handleChange( + e: React.ChangeEvent + ) { + setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value })); + } + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + + if (parseInt(captchaInput, 10) !== captcha.answer) { + setCaptchaError(true); + refreshCaptcha(); + return; + } + + setStatus("loading"); + const result = await sendContactEmail(formData); + + if (result.ok) { + setStatus("success"); + } else { + setStatus("error"); + setErrorMsg(result.error); + refreshCaptcha(); + } + } + + function handleReset() { + setFormData({ name: "", email: "", company: "", phone: "", topic: "", message: "" }); + setStatus("idle"); + setErrorMsg(""); + refreshCaptcha(); + } + + if (status === "success") { + return ( +
+
+ + check_circle + +
+

{tr.successTitle}

+

+ {tr.successDesc}{" "} + {formData.email}. +

+ +
+ ); + } + + return ( + <> +

{tr.formTitle}

+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ +