Lompat ke konten
dokumentasi.id
Commit 314576d
v1
Star2 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

8

Halaman Alternatif per Tool

Halaman alternatif menjawab satu pertanyaan sederhana: kalau tool ini tidak dipakai, tool lain apa yang mirip? Route alternative/[slug].astro membangun satu halaman statis untuk setiap dokumen di koleksi tools. getStaticPaths mengembalikan params.slug berisi tool.slug dan meneruskan seluruh objek tool lewat props, sehingga halaman dapat membaca Astro.props.tool tanpa query tambahan. src/pages/alternative/[slug].astro:8-20

Halaman ini tidak memakai relasi alternatif yang ditulis manual. Tidak ada kolom khusus di schema: tags dideklarasikan sebagai z.array(z.string()) pada koleksi tools, dan hanya array itu yang dipakai untuk mencari kandidat. src/content/config.ts:4-13

Dari tag ke grup alternatif

Mula-mula halaman menyalin tag milik tool ke relatedTags apa adanya, tanpa mengubah huruf besar-kecil atau memangkas spasi. src/pages/alternative/[slug].astro:22-27 Satu tag kemudian menjadi satu grup. Loop for mengambil relatedTags[i].trim() sebagai nilai tag, lalu memanggil getCollection('tools', ({ data }) => data.tags.includes(tag)) untuk mengumpulkan semua tool yang memiliki tag tersebut. Kode bahkan menaruh peringatan eksplisit di baris 31, // WARNING: Don't lowercase the tag here, agar nilai tag tidak dinormalkan. src/pages/alternative/[slug].astro:30-35

graph TD
    Tool["tool dari getStaticPaths"] --> Tags["relatedTags = tool.data.tags"]
    Tags --> Trim["tag = relatedTags[i].trim()"]
    Trim --> Query["getCollection: data.tags.includes(tag)"]
    Query --> Drop["findIndex + splice: buang tool sendiri"]
    Drop --> Empty{"tools.length === 0 ?"}
    Empty -->|ya| Skip["continue: tag tidak jadi grup"]
    Empty -->|tidak| Group["groupedTools[tag] = tools"]
    Group --> Section["section Alternative to ... as ... tools/services"]
    Section --> Card["Card untuk setiap tool"]
    Skip --> Done["tag tidak dirender"]
    style Tags fill:#8b5cf6,color:#fff
    style Trim fill:#f59e0b,color:#000
    style Query fill:#336791,color:#fff
    style Empty fill:#f59e0b,color:#000
    style Skip fill:#dc2626,color:#fff
    style Done fill:#dc2626,color:#fff
    style Group fill:#2d7d46,color:#fff
    style Section fill:#2d7d46,color:#fff
    style Card fill:#2d7d46,color:#fff
%% fig: Pipeline pembentukan grup alternatif dari tag
%% desc: Setiap tag milik satu tool dijadikan kueri ke koleksi tools, lalu tool itu sendiri dibuang dan grup yang kosong dilewati sebelum dirender sebagai section berisi Card.
Fig. 1: Pipeline pembentukan grup alternatif dari tagSetiap tag milik satu tool dijadikan kueri ke koleksi tools, lalu tool itu sendiri dibuang dan grup yang kosong dilewati sebelum dirender sebagai section berisi Card.

Bahan mentah pipeline ini terlihat jelas pada berkas konten. postman menulis "API-testing" dan "API-client". src/content/tools/postman.md:10 insomnia menambahkan "API-documentation". src/content/tools/insomnia.md:6 bruno hanya memakai "API-client". src/content/tools/bruno.md:6 sementara thunder-client dan httpie menulis tag pertama dengan spasi di depan, yaitu " API-testing". src/content/tools/thunder-client.md:9 src/content/tools/httpie.md:6

Tooltags di frontmatterJumlah grup yang mungkin
postman"API-testing", "API-client"2
insomnia"API-testing", "API-client", "API-documentation"3
bruno"API-client"1
thunder-client" API-testing", "API-client"2
jitsi-meet"video-conference", " conference", " alternative"3

Tool sendiri dibuang, grup kosong dilewati

Setelah kandidat terkumpul, halaman mencari posisi tool yang sedang dibuka dengan tools.findIndex((t) => t.slug === tool.slug) dan mengeluarkannya lewat tools.splice(index, 1). Bila daftar menjadi kosong, continue membuat tag itu tidak pernah masuk ke groupedTools; bila masih ada isinya, groupedTools[tag] = tools menyimpan grup. src/pages/alternative/[slug].astro:36-48

PemeriksaanKodeAkibat
tool ada di daftar kandidatfindIndex + splice(index, 1)tool tidak muncul sebagai alternatif dirinya sendiri
tools.length === 0continuetag tidak mendapat section
tools.length > 0groupedTools[tag] = toolstag menjadi kunci grup

Konsekuensinya bisa dilihat pada dua tool nyata. Untuk postman, tag "API-client" cocok ke insomnia, bruno, thunder-client, dan httpie; setelah postman dibuang, section API-client berisi empat kartu. src/content/tools/insomnia.md:6 src/content/tools/bruno.md:6 src/content/tools/thunder-client.md:9 src/content/tools/httpie.md:6 Sebaliknya jitsi-meet adalah satu-satunya tool yang memakai tag "video-conference"; grup itu kosong setelah tool sendiri dibuang, sehingga tidak dirender. src/content/tools/jitsi-meet.md:7

Jebakan: perbandingan tag yang persis

Array.prototype.includes membandingkan string persis, dan halaman ini hanya memanggil trim() pada sisi pencari, tidak pada sisi data. Akibatnya tag " API-testing" milik thunder-client dan httpie tidak akan pernah cocok dengan "API-testing" milik postman, meski secara maksud sama. src/pages/alternative/[slug].astro:30-35 src/content/tools/thunder-client.md:9 src/content/tools/postman.md:10

graph LR
    Raw["tag mentah di data.tags"] --> Needle["tag = relatedTags[i].trim()"]
    Needle --> Exact["includes: perbandingan persis"]
    Exact -->|sama persis| Hit["tool masuk grup"]
    Exact -->|beda satu spasi| Miss["tool tidak masuk grup"]
    style Raw fill:#8b5cf6,color:#fff
    style Needle fill:#f59e0b,color:#000
    style Exact fill:#f59e0b,color:#000
    style Hit fill:#2d7d46,color:#fff
    style Miss fill:#dc2626,color:#fff
%% fig: Dua jalur hasil perbandingan tag
%% desc: Nilai tag dipangkas hanya di sisi pencari, sehingga tag yang sama tetapi berawalan spasi di data tetap gagal cocok dan tool-nya hilang dari grup.
Fig. 2: Dua jalur hasil perbandingan tagNilai tag dipangkas hanya di sisi pencari, sehingga tag yang sama tetapi berawalan spasi di data tetap gagal cocok dan tool-nya hilang dari grup.

⚠️ Halaman alternatif dan halaman tag memakai normalisasi berbeda. tags/[slug].astro mencari dengan data.tags.includes(tag.trim().toLowerCase()), sedangkan halaman alternatif hanya memakai trim(). Menambah atau menghapus spasi serta huruf besar pada tags mengubah keanggotaan grup di kedua halaman secara tidak sama. src/pages/tags/[slug].astro:26-28 src/pages/alternative/[slug].astro:30-35

Judul, deskripsi, dan susunan halaman

Judul dan deskripsi dirakit dari toolTitle dan tahun berjalan. tagsInString menggabungkan tag dengan ', ', lalu regex replace(/,([^,]*)$/, ' and$1') mengganti koma terakhir menjadi and, sehingga deskripsi berbunyi seperti daftar tag bergaya prosa. src/pages/alternative/[slug].astro:22-23 src/pages/alternative/[slug].astro:50-55

Saat dirender, tool utama tampil lebih dulu sebagai satu Card, lalu setiap kunci groupedTools menghasilkan satu blok berisi h3 bertema 👉 Alternative to ... as ... tools/services, sebuah div dengan grid grid-cols-1 gap-4, dan satu Card per tool. Karena Object.keys(groupedTools) diiterasi menurut urutan penyisipan kunci, urutan section mengikuti urutan tag pada frontmatter. Halaman ditutup komponen Sponsors. src/pages/alternative/[slug].astro:68-68 src/pages/alternative/[slug].astro:70-85

Isi satu kartu alternatif

Kartu memakai komponen Card.astro yang sama dengan beranda dan halaman tag. Akar elemennya berkelas cards, membawa input tersembunyi bernama tool-slug berisi tool.slug, dan menampilkan badge Paid Tool bila tool.data.isPaid bernilai benar. src/components/Card.astro:14-25

Isi teks kartu adalah tool.data.title, tool.data.snippet, dan baris Tags: hasil tool.data.tags.join(', '), jadi tag tampil mentah termasuk spasi di depannya. src/components/Card.astro:39-42 Baris aksi menyediakan tautan Edit ke berkas src/content/tools/${tool.slug}.md, tautan Visit ke tool.data.link dengan tambahan utm_source=freestuffdev, serta tautan Alternatives yang menunjuk kembali ke /alternative/${tool.slug}. src/components/Card.astro:61-69

Karena kartu yang sama dipakai di semua halaman, tombol Save dan Remove from Favorites juga aktif di halaman alternatif. Mekanismenya dijelaskan di Fitur Favorit di Local Storage. Untuk pembentukan daftar tag dan rute /tags/, lihat Indeks Tag dan Halaman Tag; untuk daftar kolom frontmatter, lihat Skema Content Collection tools.