Tugas Personal Website
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import type { Metadata } from "next";
|
||||
import Script from "next/script";
|
||||
import { NavExternal } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, education, skills, quote } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Tentang Saya | Personal Website" };
|
||||
|
||||
// css internal: satu blok <style> (di-hoist react ke <head>) dengan selector id dan class
|
||||
const internalCss = `
|
||||
#about-page {
|
||||
min-height: 100vh;
|
||||
background: #eaf3f4;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
}
|
||||
.quote {
|
||||
margin: 0;
|
||||
padding: 16px 20px;
|
||||
border-left: 4px solid #9aa9ad;
|
||||
color: #55666b;
|
||||
font-style: italic;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div id="about-page">
|
||||
{/* external css: file public/styles.css dimuat lewat link, dipakai untuk nav dan wrapper main */}
|
||||
<link rel="stylesheet" href="/styles.css" precedence="default" />
|
||||
{/* internal css: blok style untuk latar halaman dan font (selector #about-page dan .quote) */}
|
||||
<style href="about-internal" precedence="default">{internalCss}</style>
|
||||
{/* tailwind css: script cdn, dipakai untuk komponen badge di bagian biodata */}
|
||||
<Script src="https://cdn.tailwindcss.com" />
|
||||
|
||||
<HtmlComment text="about.html memakai 4 metode css sekaligus, tiap metode diberi label komentar di elemennya" />
|
||||
|
||||
<header className="site-header">
|
||||
<p className="brand">{profile.nickname}</p>
|
||||
<HtmlComment text="external css: kelas site-nav di styles.css mengatur layout navigasi" />
|
||||
<NavExternal current="/about" />
|
||||
</header>
|
||||
|
||||
<HtmlComment text="external css: kelas container di styles.css mengatur lebar dan padding wrapper main" />
|
||||
<main className="container">
|
||||
<section className="panel">
|
||||
<h1>Tentang Saya</h1>
|
||||
<p>
|
||||
Saya {profile.name}, biasa dipanggil {profile.nickname}. Saya tinggal di {profile.city} dan sedang
|
||||
menempuh pendidikan di Program Studi Informatika.
|
||||
</p>
|
||||
<HtmlComment text="tailwind css: kelas utilitas membentuk badge berbentuk pil" />
|
||||
<p>
|
||||
<span className="inline-block rounded-full bg-teal-800 px-3 py-1 text-sm font-semibold text-white">
|
||||
Informatika
|
||||
</span>{" "}
|
||||
<span className="inline-block rounded-full bg-amber-500 px-3 py-1 text-sm font-semibold text-white">
|
||||
Semester 3
|
||||
</span>{" "}
|
||||
<span className="inline-block rounded-full bg-slate-600 px-3 py-1 text-sm font-semibold text-white">
|
||||
{profile.city}
|
||||
</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Pendidikan</h2>
|
||||
<HtmlComment text="ol dipakai karena riwayat pendidikan berurutan dari yang paling awal" />
|
||||
<ol>
|
||||
{education.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Keahlian</h2>
|
||||
<table className="data-table">
|
||||
<caption className="sr-only">Daftar keahlian saya</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Keahlian</th>
|
||||
<th scope="col">Level</th>
|
||||
<th scope="col">Catatan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{skills.map((row) => (
|
||||
<tr key={row.skill}>
|
||||
<td>{row.skill}</td>
|
||||
<td>{row.level}</td>
|
||||
<td>{row.note}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Kutipan favorit</h2>
|
||||
<HtmlComment text="inline css: atribut style pada blockquote menimpa warna dan border dari css internal (.quote)" />
|
||||
<blockquote
|
||||
className="quote"
|
||||
style={{ color: "#0f4c5c", borderLeft: "6px solid #e09f3e", background: "#fff7e6", fontSize: "1.2rem" }}
|
||||
>
|
||||
<p>“{quote.text}”</p>
|
||||
<footer>— {quote.author}</footer>
|
||||
</blockquote>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="site-footer">
|
||||
<p>
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Metadata } from "next";
|
||||
import { NavExternal } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, contacts } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Kontak | Personal Website" };
|
||||
|
||||
export default function ContactPage() {
|
||||
return (
|
||||
<>
|
||||
{/* pilihan css: external. alasannya, halaman ini memakai ulang kelas panel, container, dan nav
|
||||
yang sudah ada di styles.css. inline akan mengulang gaya, tailwind butuh script cdn tambahan
|
||||
padahal halaman ini sederhana dan tidak perlu komponen khusus */}
|
||||
<link rel="stylesheet" href="/styles.css" precedence="default" />
|
||||
<HtmlComment text="contact.html memakai external css karena gayanya sama dengan halaman lain dan cukup satu stylesheet bersama" />
|
||||
|
||||
<header className="site-header">
|
||||
<p className="brand">{profile.nickname}</p>
|
||||
<NavExternal current="/contact" />
|
||||
</header>
|
||||
|
||||
<main className="container">
|
||||
<h1>Kontak</h1>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Hubungi saya</h2>
|
||||
<p>Silakan hubungi saya lewat salah satu kanal berikut. Tidak ada formulir, langsung saja kirim pesan.</p>
|
||||
<ul className="contact-list">
|
||||
{contacts.map((item) => (
|
||||
<li key={item.label}>
|
||||
<span className="contact-label">{item.label}</span>
|
||||
<a
|
||||
href={item.href}
|
||||
{...(item.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Lokasi</h2>
|
||||
<p>Saya berdomisili di {profile.city} dan kuliah di Universitas Katolik Widya Mandala Surabaya.</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="site-footer">
|
||||
<p>
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Metadata } from "next";
|
||||
import Script from "next/script";
|
||||
import { NavTailwind } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, gallery } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Galeri | Personal Website" };
|
||||
|
||||
export default function GalleryPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 font-sans text-slate-800">
|
||||
{/* halaman ini WAJIB tailwind css saja: script cdn dimuat di sini, semua gaya berupa kelas utilitas */}
|
||||
<Script src="https://cdn.tailwindcss.com" />
|
||||
|
||||
<header className="mx-auto max-w-5xl px-6 pt-4">
|
||||
<p className="m-0 font-serif text-xl font-bold text-teal-800">{profile.nickname}</p>
|
||||
<NavTailwind current="/gallery" />
|
||||
</header>
|
||||
|
||||
<main className="mx-auto max-w-5xl px-6 pb-6">
|
||||
<section>
|
||||
<h1 className="mb-2 mt-6 font-serif text-4xl font-bold leading-tight text-teal-800">Galeri Foto</h1>
|
||||
<p className="mb-6 max-w-prose leading-relaxed">Beberapa potret dari keseharian saya.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="mb-4 font-serif text-2xl font-bold text-teal-800">Koleksi terbaru</h2>
|
||||
<HtmlComment text="grid responsif: 1 kolom di ponsel, 2 di tablet, 3 di layar lebar" />
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{gallery.map((photo) => (
|
||||
<figure key={photo.src} className="m-0 overflow-hidden rounded-xl bg-white shadow">
|
||||
<img
|
||||
src={photo.src}
|
||||
alt={photo.alt}
|
||||
width="400"
|
||||
height="300"
|
||||
className="aspect-[4/3] w-full object-cover"
|
||||
/>
|
||||
<figcaption className="px-4 py-3 text-sm font-semibold text-slate-700">{photo.caption}</figcaption>
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="mx-auto max-w-5xl border-t border-slate-300 px-6 pb-8 pt-4 text-sm text-slate-600">
|
||||
<p className="m-0">
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { Metadata } from "next";
|
||||
import { NavExternal } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, hobbies } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Hobi | Personal Website" };
|
||||
|
||||
export default function HobbiesPage() {
|
||||
return (
|
||||
<>
|
||||
{/* halaman ini WAJIB external css saja: semua gaya ada di public/styles.css */}
|
||||
<link rel="stylesheet" href="/styles.css" precedence="default" />
|
||||
|
||||
<header className="site-header">
|
||||
<p className="brand">{profile.nickname}</p>
|
||||
<NavExternal current="/hobbies" />
|
||||
</header>
|
||||
|
||||
<main className="container">
|
||||
<h1>Hobi Saya</h1>
|
||||
<p>Tiga hal yang biasa saya lakukan di waktu luang.</p>
|
||||
|
||||
<HtmlComment text="tiap hobi dibuat sebagai section sendiri agar h2 dan h3 tetap berurutan" />
|
||||
{hobbies.map((hobby) => (
|
||||
<section className="hobby" key={hobby.title}>
|
||||
<img src={hobby.photo} alt={hobby.alt} width="260" height="195" />
|
||||
<div className="hobby-text">
|
||||
<h2>{hobby.title}</h2>
|
||||
<p>{hobby.description}</p>
|
||||
<h3>Yang biasa saya lakukan</h3>
|
||||
<ul>
|
||||
{hobby.details.map((detail) => (
|
||||
<li key={detail}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
|
||||
<section className="panel">
|
||||
<h2>Ingin mencoba juga?</h2>
|
||||
<p>
|
||||
Untuk pemula yang mau belajar ngegym silahkan{" "}
|
||||
<a href="https://www.tiktok.com/@daddygeeeeee?is_from_webapp=1&sender_device=pc" target="_blank" rel="noopener noreferrer">
|
||||
tonton dia ini
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="site-footer">
|
||||
<p>
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// root layout sengaja polos: tidak ada css global di sini
|
||||
// setiap halaman membawa metode css-nya sendiri sesuai ketentuan tugas
|
||||
import type { Metadata } from "next";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Personal Website",
|
||||
description: "Situs personal multipage untuk tugas Pemrograman Web",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="id">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { CSSProperties } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { NavInline } from "../components/nav";
|
||||
import HtmlComment from "../components/html-comment";
|
||||
import { profile } from "../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Beranda | Personal Website" };
|
||||
|
||||
// halaman ini WAJIB inline css saja: semua gaya memakai atribut style
|
||||
const s: Record<string, CSSProperties> = {
|
||||
page: { maxWidth: "960px", margin: "0 auto", padding: "24px", fontFamily: "system-ui, -apple-system, 'Segoe UI', sans-serif", color: "#1c2b33", background: "#f3f7f7", borderRadius: "20px", minHeight: "90vh" },
|
||||
brand: { margin: 0, fontFamily: "Georgia, 'Times New Roman', serif", fontSize: "1.25rem", fontWeight: 700, color: "#0f4c5c" },
|
||||
hero: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "32px", padding: "32px 0" },
|
||||
photo: { width: "280px", maxWidth: "100%", height: "auto", borderRadius: "16px", border: "6px solid #e09f3e" },
|
||||
heroText: { flex: "1 1 300px" },
|
||||
h1: { margin: "0 0 12px", fontFamily: "Georgia, 'Times New Roman', serif", fontSize: "2.6rem", lineHeight: 1.15, color: "#0f4c5c" },
|
||||
tagline: { margin: "0 0 16px", fontSize: "1.2rem", lineHeight: 1.5 },
|
||||
section: { padding: "24px", background: "#ffffff", borderRadius: "12px", borderLeft: "6px solid #0f4c5c" },
|
||||
h2: { margin: "0 0 8px", fontFamily: "Georgia, 'Times New Roman', serif", fontSize: "1.5rem", color: "#0f4c5c" },
|
||||
p: { margin: 0, lineHeight: 1.7, maxWidth: "65ch" },
|
||||
footer: { marginTop: "32px", paddingTop: "16px", borderTop: "1px solid #c9d8db", fontSize: "0.9rem", color: "#4a5d66" },
|
||||
};
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div style={s.page}>
|
||||
<HtmlComment text="inline css: seluruh halaman ini distyle lewat atribut style sesuai aturan index.html" />
|
||||
<header>
|
||||
<p style={s.brand}>{profile.nickname}</p>
|
||||
<NavInline current="/" />
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section style={s.hero}>
|
||||
<img src={profile.photo} alt={profile.photoAlt} width="280" height="210" style={s.photo} />
|
||||
<div style={s.heroText}>
|
||||
<h1 style={s.h1}>Halo, saya {profile.name}</h1>
|
||||
<p style={s.tagline}>{profile.tagline}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={s.section}>
|
||||
<h2 style={s.h2}>Sekilas tentang saya</h2>
|
||||
<p style={s.p}>{profile.intro}</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style={s.footer}>
|
||||
<p style={{ margin: 0 }}>
|
||||
© {profile.year} {profile.name}. Dibuat di {profile.city}.
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user