Raw
1 import type { Metadata } from "next";
2 import { allKtis } from "contentlayer/generated";
3 import Link from "next/link";
4 import { StructuredData } from "@/components/structured-data";
5 import siteMetadata from "@/content/siteMetadata";
6 import { absoluteUrl, genPageMetadata } from "../seo";
7
8 export const metadata: Metadata = {
9 ...genPageMetadata({
10 title: "KTI - Kamus Teknologi Informasi",
11 description:
12 "Kamus Teknologi Informasi lengkap untuk tenaga kerja muda Indonesia. Pelajari istilah-istilah IT, pemrograman, cloud computing, dan teknologi terkini dengan penjelasan dalam Bahasa Indonesia.",
13 path: "/kti",
14 locale: "id_ID",
15 }),
16 keywords: [
17 "kamus teknologi informasi",
18 "istilah IT",
19 "glosarium teknologi",
20 "bahasa Indonesia",
21 "pembelajaran IT",
22 "karir IT Indonesia",
23 "cloud computing",
24 "pemrograman",
25 "software development",
26 "teknologi informasi",
27 ],
28 openGraph: {
29 title: "KTI - Kamus Teknologi Informasi untuk Profesional IT Indonesia",
30 description:
31 "Panduan lengkap istilah teknologi informasi dalam Bahasa Indonesia untuk membantu karir IT Anda",
32 url: `${siteMetadata.siteUrl}/kti`,
33 siteName: siteMetadata.title,
34 locale: "id_ID",
35 type: "website",
36 },
37 };
38
39 const difficultyConfig = {
40 pemula: {
41 label: "Pemula",
42 badge:
43 "bg-emerald-50 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-500/20",
44 bar: "bg-emerald-500",
45 tag: "bg-emerald-50 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 hover:bg-emerald-100 dark:hover:bg-emerald-500/20",
46 },
47 menengah: {
48 label: "Menengah",
49 badge:
50 "bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-400 border border-amber-200 dark:border-amber-500/20",
51 bar: "bg-amber-500",
52 tag: "bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-400 hover:bg-amber-100 dark:hover:bg-amber-500/20",
53 },
54 lanjut: {
55 label: "Lanjut",
56 badge:
57 "bg-indigo-50 dark:bg-indigo-500/10 text-indigo-700 dark:text-indigo-400 border border-indigo-200 dark:border-indigo-500/20",
58 bar: "bg-indigo-500",
59 tag: "bg-indigo-50 dark:bg-indigo-500/10 text-indigo-700 dark:text-indigo-400 hover:bg-indigo-100 dark:hover:bg-indigo-500/20",
60 },
61 } as const;
62
63 type Difficulty = keyof typeof difficultyConfig;
64
65 export default function KtiPage() {
66 let publishedKtis = allKtis.filter((kti) => kti.published);
67
68 publishedKtis = publishedKtis.sort((a, b) =>
69 a.term.toLowerCase().localeCompare(b.term.toLowerCase()),
70 );
71
72 const categories = Array.from(
73 new Set(publishedKtis.map((kti) => kti.category)),
74 ).sort();
75
76 const difficultyGroups = {
77 pemula: publishedKtis.filter((kti) => kti.difficulty === "pemula"),
78 menengah: publishedKtis.filter((kti) => kti.difficulty === "menengah"),
79 lanjut: publishedKtis.filter((kti) => kti.difficulty === "lanjut"),
80 };
81
82 const ktiStructuredData = {
83 "@context": "https://schema.org",
84 "@graph": [
85 {
86 "@type": "CollectionPage",
87 "@id": absoluteUrl("/kti#webpage"),
88 url: absoluteUrl("/kti"),
89 name: "KTI - Kamus Teknologi Informasi",
90 description:
91 "Kamus Teknologi Informasi lengkap untuk tenaga kerja muda Indonesia.",
92 inLanguage: "id-ID",
93 isPartOf: {
94 "@id": absoluteUrl("/#website"),
95 },
96 },
97 {
98 "@type": "BreadcrumbList",
99 itemListElement: [
100 {
101 "@type": "ListItem",
102 position: 1,
103 name: "Home",
104 item: absoluteUrl("/"),
105 },
106 {
107 "@type": "ListItem",
108 position: 2,
109 name: "KTI",
110 item: absoluteUrl("/kti"),
111 },
112 ],
113 },
114 {
115 "@type": "DefinedTermSet",
116 name: "Kamus Teknologi Informasi",
117 url: absoluteUrl("/kti"),
118 description:
119 "Glosarium istilah teknologi informasi dalam Bahasa Indonesia.",
120 inLanguage: "id-ID",
121 hasDefinedTerm: publishedKtis.map((kti) => ({
122 "@type": "DefinedTerm",
123 name: kti.title,
124 description: kti.description,
125 url: absoluteUrl(kti.slug),
126 inLanguage: "id-ID",
127 })),
128 },
129 ],
130 };
131
132 return (
133 <div className="mx-auto max-w-5xl px-6 py-16 lg:px-8">
134 <StructuredData data={ktiStructuredData} />
135 {/* ── Page header ─────────────────────────────────── */}
136 <div className="mb-16">
137 <p className="mb-4 text-xs font-bold uppercase tracking-[0.2em] text-slate-400 dark:text-slate-600">
138 Referensi
139 </p>
140 <h1 className="text-4xl font-bold tracking-tight text-slate-900 dark:text-white sm:text-5xl">
141 Kamus Teknologi Informasi
142 </h1>
143 <p className="mt-4 max-w-2xl text-lg text-slate-500 dark:text-slate-400">
144 Panduan lengkap istilah teknologi informasi dalam Bahasa Indonesia
145 untuk tenaga kerja muda dan profesional IT.
146 </p>
147 </div>
148
149 {/* ── Stats row ───────────────────────────────────── */}
150 <div className="mb-16 grid grid-cols-2 gap-4 sm:grid-cols-4">
151 {[
152 { value: publishedKtis.length, label: "Total Istilah" },
153 { value: categories.length, label: "Kategori" },
154 { value: difficultyGroups.pemula.length, label: "Level Pemula" },
155 { value: difficultyGroups.lanjut.length, label: "Level Lanjut" },
156 ].map((stat) => (
157 <div
158 key={stat.label}
159 className="rounded-xl border border-slate-200 dark:border-white/[0.06] bg-white dark:bg-white/[0.02] p-5"
160 >
161 <p className="text-3xl font-bold tracking-tight text-slate-900 dark:text-white">
162 {stat.value}
163 </p>
164 <p className="mt-1 text-xs text-slate-500 dark:text-slate-500">
165 {stat.label}
166 </p>
167 </div>
168 ))}
169 </div>
170
171 {/* ── Browse by category ──────────────────────────── */}
172 <section className="mb-16">
173 <p className="mb-8 text-xs font-bold uppercase tracking-[0.2em] text-slate-400 dark:text-slate-600">
174 Berdasarkan Kategori
175 </p>
176 <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
177 {categories.map((category) => {
178 const categoryKtis = publishedKtis.filter(
179 (kti) => kti.category === category,
180 );
181 return (
182 <div
183 key={category}
184 className="rounded-xl border border-slate-200 dark:border-white/[0.06] bg-white dark:bg-white/[0.02] p-5 transition-colors hover:border-indigo-300 dark:hover:border-indigo-700/50"
185 >
186 <div className="mb-4 flex items-center justify-between">
187 <h3 className="font-semibold text-slate-900 dark:text-white">
188 {category}
189 </h3>
190 <span className="text-xs text-slate-400 dark:text-slate-600">
191 {categoryKtis.length} istilah
192 </span>
193 </div>
194 <div className="flex flex-wrap gap-1.5">
195 {categoryKtis.slice(0, 6).map((kti) => (
196 <Link
197 key={kti.term}
198 href={kti.slug}
199 className="rounded-md bg-slate-100 dark:bg-white/[0.05] px-2.5 py-1 text-xs text-slate-600 dark:text-slate-400 transition-colors hover:bg-indigo-50 dark:hover:bg-indigo-500/10 hover:text-indigo-700 dark:hover:text-indigo-400"
200 >
201 {kti.term}
202 </Link>
203 ))}
204 {categoryKtis.length > 6 && (
205 <span className="px-2.5 py-1 text-xs text-slate-400 dark:text-slate-600">
206 +{categoryKtis.length - 6} lainnya
207 </span>
208 )}
209 </div>
210 </div>
211 );
212 })}
213 </div>
214 </section>
215
216 {/* ── Browse by difficulty ────────────────────────── */}
217 <section className="mb-16">
218 <p className="mb-8 text-xs font-bold uppercase tracking-[0.2em] text-slate-400 dark:text-slate-600">
219 Berdasarkan Level
220 </p>
221 <div className="space-y-6">
222 {(["pemula", "menengah", "lanjut"] as Difficulty[]).map((level) => {
223 const config = difficultyConfig[level];
224 const group = difficultyGroups[level];
225 return (
226 <div
227 key={level}
228 className="rounded-xl border border-slate-200 dark:border-white/[0.06] bg-white dark:bg-white/[0.02] p-5"
229 >
230 <div className="mb-4 flex items-center gap-3">
231 <span
232 className={`rounded-full px-2.5 py-0.5 text-xs font-semibold ${config.badge}`}
233 >
234 {config.label}
235 </span>
236 <span className="text-xs text-slate-400 dark:text-slate-600">
237 {group.length} istilah
238 </span>
239 </div>
240 <div className="flex flex-wrap gap-1.5">
241 {group.slice(0, 12).map((kti) => (
242 <Link
243 key={kti.term}
244 href={kti.slug}
245 className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${config.tag}`}
246 >
247 {kti.term}
248 </Link>
249 ))}
250 {group.length > 12 && (
251 <span className="px-2.5 py-1 text-xs text-slate-400 dark:text-slate-600">
252 +{group.length - 12} lainnya
253 </span>
254 )}
255 </div>
256 </div>
257 );
258 })}
259 </div>
260 </section>
261
262 {/* ── All terms A–Z ───────────────────────────────── */}
263 <section className="mb-16">
264 <p className="mb-8 text-xs font-bold uppercase tracking-[0.2em] text-slate-400 dark:text-slate-600">
265 Semua Istilah (A–Z)
266 </p>
267 <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
268 {publishedKtis.map((kti) => {
269 const config =
270 difficultyConfig[kti.difficulty as Difficulty] ??
271 difficultyConfig.pemula;
272 return (
273 <Link
274 key={kti.term}
275 href={kti.slug}
276 className="group rounded-xl border border-slate-200 dark:border-white/[0.06] bg-white dark:bg-white/[0.02] p-4 transition-colors hover:border-indigo-300 dark:hover:border-indigo-700/50 hover:bg-slate-50 dark:hover:bg-white/[0.04]"
277 >
278 <div className="mb-2 flex items-start justify-between gap-2">
279 <h3 className="font-semibold text-slate-900 dark:text-white transition-colors group-hover:text-indigo-600 dark:group-hover:text-indigo-400">
280 {kti.term}
281 </h3>
282 <span
283 className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold ${config.badge}`}
284 >
285 {config.label}
286 </span>
287 </div>
288 {kti.englishTerm && (
289 <p className="mb-1.5 text-xs text-slate-400 dark:text-slate-600">
290 {kti.englishTerm}
291 </p>
292 )}
293 <p className="line-clamp-2 text-sm leading-relaxed text-slate-500 dark:text-slate-400">
294 {kti.description}
295 </p>
296 </Link>
297 );
298 })}
299 </div>
300 </section>
301
302 {/* ── CTA ─────────────────────────────────────────── */}
303 <section>
304 <div className="relative overflow-hidden rounded-2xl bg-indigo-600 px-8 py-14 text-center shadow-2xl shadow-indigo-500/20">
305 <div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(255,255,255,0.1),transparent)]" />
306 <h2 className="relative text-2xl font-bold tracking-tight text-white">
307 Siap membangun dengan smbCloud?
308 </h2>
309 <p className="relative mt-3 text-indigo-200">
310 Deploy aplikasi Node.js, Ruby on Rails, dan Swift/Vapor dengan satu
311 perintah.
312 </p>
313 <Link
314 href="https://console.smbcloud.xyz/login"
315 target="_blank"
316 rel="noopener noreferrer"
317 className="relative mt-8 inline-block rounded-lg bg-white px-8 py-3.5 text-sm font-semibold text-indigo-700 shadow-sm transition-colors hover:bg-indigo-50"
318 >
319 Mulai dengan smbCloud →
320 </Link>
321 </div>
322 </section>
323 </div>
324 );
325 }