6
Halaman Recent dan Pengurutan
Halaman /recent menampilkan koleksi tools yang diurutkan dari createdAt terbaru. Berkasnya pendek: satu getCollection, satu sort, satu <select> dua opsi, lalu map ke Card. Pengambilan koleksi dan pengurutan berjalan di frontmatter saat build, sedangkan satu-satunya skrip di browser adalah handler change pada pemilih urutan src/pages/recent.astro:43-541.
Menyaring tool berbayar lebih dulu
getCollection('tools', ...) dipanggil dengan filter data.isPaid !== true, sehingga tool yang frontmatter-nya bertanda isPaid: true tidak pernah masuk daftar recent src/pages/recent.astro:7-102. Pada schema koleksi, isPaid bersifat opsional dan createdAt wajib bertipe z.date() src/content/config.ts:10-123. Contoh berkas yang ditandai berbayar adalah static.app, dengan createdAt: 2024-12-06T15:10:48.662Z dan isPaid: true src/content/tools/static-app.md:7-84.
Filter ini sama persis dengan filter di beranda. Karena itu /recent dan beranda memuat himpunan tool yang sama, dan yang berbeda hanya urutannya; rincian penyaringan di beranda ada di Beranda dan Pencarian Klien.
Mengurutkan createdAt menurun
Setelah koleksi terkumpul, tools.sort membandingkan dua tanggal lewat new Date(a.data.createdAt) dan new Date(b.data.createdAt), keduanya di-cast as any, lalu mengembalikan dateB - dateA src/pages/recent.astro:12-175. Pengurangan dua Date menghasilkan angka, sehingga tanggal yang lebih baru keluar lebih dulu. Cast as any dipakai agar TypeScript mengizinkan pengurangan tersebut, meskipun nilai createdAt sudah berupa Date dari schema src/content/config.ts:10-123.
Judul dan deskripsi halaman ditetapkan sebagai string tetap, bukan dihitung dari koleksi src/pages/recent.astro:19-206.
graph TD
collection["getCollection('tools')"] --> filter{"filter: data.isPaid !== true"}
filter -->|"isPaid true"| excluded["tool tidak masuk daftar"]
filter -->|"lolos"| sort["tools.sort: dateB - dateA"]
sort --> map["tools.map"]
map --> card["Card tool={tool}"]
card --> badge{"tool.data.isPaid?"}
badge -->|"false"| fields["title, snippet, tags, body"]
style collection fill:#336791,color:#fff
style filter fill:#f59e0b,color:#000
style sort fill:#2d7d46,color:#fff
style map fill:#2d7d46,color:#fff
style card fill:#8b5cf6,color:#fff
style fields fill:#8b5cf6,color:#fff
style badge fill:#f59e0b,color:#000
style excluded fill:#dc2626,color:#fff
%% fig: Alur getCollection sampai render kartu di /recent
%% desc: Koleksi disaring, diurutkan menurun berdasarkan createdAt, lalu setiap tool dirender sebagai Card tanpa badge berbayar.Pemilih urutan dan arah navigasinya
Markup pemilih berupa <select id="sort"> dengan dua <option>: newest dan random. Opsi pertama, yaitu Newest, tampil terpilih karena tidak ada atribut selected pada opsi mana pun src/pages/recent.astro:34-417.
Handler change membaca (e.target as any).value, lalu mengarahkan browser: newest menuju ?sort=newest, nilai lain menuju / src/pages/recent.astro:43-541.
| Nilai opsi | Label | location.href | Halaman tujuan |
|---|---|---|---|
newest | Newest | '?sort=newest' (relatif) | /recent?sort=newest |
random | Random | '/' | beranda |
Satu-satunya rujukan ke /recent di src/ berada pada pemilih urutan di beranda, yang mengarahkan pembaca ke /recent ketika memilih newest src/pages/index.astro:59-608. Sidebar tidak memuat tautan ke halaman ini.
graph LR
select["#sort change"] --> check{"nilai opsi"}
check -->|"newest"| newest["location.href = '?sort=newest'"]
check -->|"random"| random["location.href = '/'"]
newest --> same["query string diabaikan"]
style select fill:#f59e0b,color:#000
style check fill:#f59e0b,color:#000
style newest fill:#8b5cf6,color:#fff
style random fill:#2d7d46,color:#fff
style same fill:#dc2626,color:#fff
%% fig: Cabang navigasi pemilih urutan
%% desc: Pilihan Newest hanya menambah query string relatif, sedangkan Random kembali ke beranda; query string itu tidak dibaca oleh halaman recent.⚠️ Opsi Newest mengarah ke
?sort=newest, tetapirecent.astrotidak pernah membaca query string; tidak ada pemakaianAstro.url.searchParamsdi berkas itu. Akibatnya/recent?sort=newesttetap dirender dengan urutancreatedAtmenurun yang sama, dan penambahan parameter itu tidak mengubah apa pun selain URL src/pages/recent.astro:43-541. Untuk benar-benar kembali ke urutan acak, pembaca harus memilih Random atau memakai pemilih di beranda.
Kartu yang dirender
tools.map merender satu <Card tool={tool} /> untuk setiap tool yang lolos filter, di dalam pembungkus div berjarak mt-10 src/pages/recent.astro:57-639. Card menerima prop tool beserta isHidden yang default-nya false, dan prop itu tidak dikirim oleh halaman ini.
| Elemen | Sumber data | Perilaku |
|---|---|---|
input[name="tool-slug"] | tool.slug | menjadi penanda kartu bagi skrip favorit src/components/Card.astro:14-1610 |
img thumbnail | tool.data.thumbnail | onerror="removeMe(this)" mengganti elemen gambar dengan div selebar 100px src/components/Card.astro:6-1211 |
| judul, snippet, tags | tool.data.title, tool.data.snippet, tool.data.tags.join(', ') | tag digabung dengan koma src/components/Card.astro:27-4412 |
| badge "Paid Tool" | tool.data.isPaid | tidak pernah tampil di /recent karena filter pada frontmatter src/components/Card.astro:18-2513 |
| blok "Free Offer:" | tool.body | hanya dirender bila body tidak kosong src/components/Card.astro:46-5714 |
| baris tautan aksi | tool.slug, tool.data.link | Save, Edit, Visit, Alternatives src/components/Card.astro:61-6915 |
Tiga tautan di baris aksi menuju halaman lain. Edit membuka berkas src/content/tools/${tool.slug}.md di GitHub, yang aturannya dibahas di Format Berkas dan Aturan Kontribusi. Visit menambahkan ?utm_source=freestuffdev ke tool.data.link. Alternatives menuju /alternative/${tool.slug} seperti dijelaskan pada Halaman Alternatif per Tool. Tombol Save dan Remove from Favorites memanggil fungsi dari Layout, yang dijelaskan di Fitur Favorit di Local Storage.