feat(seo): add metadata, sitemap, robots, JSON-LD, dynamic OG image

- Centralize brand/keyword config in lib/site.ts (slogan, 22 keywords).
- Root layout: metadataBase, title template, OG/Twitter defaults, robots rules.
- Per-page metadata: home, trips list (filter-aware), trip detail, profile (noindex).
- Layout wrappers add metadata to client-component pages: login, register, create-trip.
- Trip detail: generateMetadata + JSON-LD Event + BreadcrumbList (price, availability, rating).
- Home page: JSON-LD Organization + WebSite + SearchAction (sitelink search).
- app/sitemap.ts: dynamic sitemap pulling OPEN/FULL trips from Prisma.
- app/robots.ts: disallow /api/, /profile, /create-trip; references sitemap.
- app/trips/[id]/opengraph-image.tsx: dynamic 1200x630 OG image per trip with
  cover photo, title, mountain, date, price, brand badge.
- Seeder: switch trip images from local SVG placeholders to real Unsplash CDN URLs.
- Drop 18 obsolete seed SVGs from public/images/seed/.

New env: NEXT_PUBLIC_SITE_URL (defaults to localhost:3000) — set to prod domain on deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
arifal
2026-04-27 02:14:08 +07:00
parent a4508dc828
commit d91e16b6ef
31 changed files with 657 additions and 255 deletions
+39
View File
@@ -0,0 +1,39 @@
export const siteConfig = {
name: "SeTrip",
brand: "SeTrip",
slogan: "Masa cowok sejati, cewek seimut, nggak SeTrip bareng?",
description:
"SeTrip adalah platform open trip pendakian gunung yang menyatukan open trip dari berbagai sosial media ke satu tempat. Cari, gabung, dan mendaki bareng — dari pemula sampai pendaki rutin.",
keywords: [
"setrip",
"se trip",
"open trip",
"open trip gunung",
"open trip pendakian",
"open trip jawa barat",
"hiking",
"hiking bareng",
"mendaki bersama",
"naik gunung bareng",
"pendakian gunung",
"trip gunung",
"trip pendakian",
"gabung open trip",
"cari open trip",
"papandayan",
"ciremai",
"gunung gede",
"gunung pangrango",
"gunung guntur",
"gunung malabar",
"gunung tangkuban parahu",
],
} as const;
export const siteUrl = (
process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000"
).replace(/\/$/, "");
export function absoluteUrl(path = "/"): string {
return `${siteUrl}${path.startsWith("/") ? path : `/${path}`}`;
}