12
Website dan Playground
Website proyek ini adalah satu halaman Astro di rute tunggal. web/src/pages/index.astro merangkai lima section di bawah satu Layout, dan astro.config.mjs menetapkan site serta base /api-wilayah-indonesia/ (web/astro.config.mjs:7-101). README menautkan playground lewat anchor #playground pada halaman yang sama (README.md:4752), jadi pengunjung yang ingin mencoba API langsung diarahkan ke section pertama, bukan ke halaman terpisah.
Satu rute, lima section
Urutan section di main bersifat tetap. Playground berada paling atas, lalu API, CLI, skill, dan FAQ (web/src/pages/index.astro:12-213). Bedanya hanya directive hidrasi: playground memakai client:only="react" (web/src/pages/index.astro:154), sedangkan section lain memakai client:load.
| Section | Komponen | Anchor | Directive | Halaman terkait |
|---|---|---|---|---|
| Playground | PlaygroundSection | playground | client:only="react" | halaman ini |
| API | ApiSection | api | client:load | Referensi Endpoint |
| CLI | CliSection | cli | client:load | CLI @emsifa/wilayah |
| Skill | SkillSection | skill | client:load | Skill untuk AI Agent |
| FAQ | FaqSection | faq | client:load | Kelengkapan Data dan missings.json |
Anchor cli dan skill masing-masing dipasang di section-nya (web/src/components/sections/CliSection.tsx:715, web/src/components/sections/SkillSection.tsx:226), dan anchor faq dipasang di section terakhir (web/src/components/sections/FaqSection.tsx:2037). ApiSection memakai basis URL yang sama dengan playground, https://www.emsifa.com/api-wilayah-indonesia/v2 (web/src/components/sections/ApiSection.tsx:29-338).
⚠️ Karena playground memakai
client:only="react", section itu tidak dirender saat build. Kalau JavaScript gagal dimuat, peta dan dropdown tidak muncul sama sekali, sementara section lain tetap tampil sebagai HTML.
State playground dan request bertingkat
PlaygroundSection menyimpan empat kode pilihan (provCode, regCode, distCode, villCode), enam daftar hasil fetch, dua detail terpilih, dan satu polygon multi-ring (web/src/components/sections/PlaygroundSection.tsx:66-939). Basis URL-nya konstanta absolut (web/src/components/sections/PlaygroundSection.tsx:810), sehingga astro dev di mesin lokal tetap memanggil API produksi.
Setiap tingkat punya efek sendiri: provinsi diambil sekali saat mount (web/src/components/sections/PlaygroundSection.tsx:95-12111), regencies mengikuti provCode (web/src/components/sections/PlaygroundSection.tsx:123-15412), districts mengikuti regCode (web/src/components/sections/PlaygroundSection.tsx:156-185), dan villages mengikuti distCode (web/src/components/sections/PlaygroundSection.tsx:187-216). Kecamatan dan kelurahan di-fetch sekali lagi untuk mendapat detail lengkap (web/src/components/sections/PlaygroundSection.tsx:218-252), lalu polygon diambil dari kode terdalam yang tersedia (web/src/components/sections/PlaygroundSection.tsx:254-279).
Berikut urutan request saat pengguna memilih wilayah, sampai peta bergerak dan polygon muncul.
sequenceDiagram
actor User
participant DD as RegionDropdowns
participant PS as PlaygroundSection
participant API as Static JSON v2
participant Map as MapLibreIndonesiaMap
User->>DD: pilih Provinsi
DD->>PS: handleProv mengisi provCode
PS->>PS: reset regCode distCode villCode
PS->>API: GET /regencies/provCode.json
API-->>PS: data daftar kabupaten kota
PS->>PS: toRegion dan toRegionDetail
PS->>Map: selectedForMap selectedDetail zoom
Map->>Map: marker popup dan flyTo 1200 ms
PS->>API: GET /paths/code.json
API-->>PS: data path
PS->>PS: normalizeRings menerima ring valid
PS->>Map: polygon
Map->>Map: addSource wilayah-polygon dan fitBounds
%% fig: Siklus request playground saat wilayah dipilih
%% desc: Pilihan dropdown memicu fetch daftar wilayah, lalu fetch paths, dan hasilnya diteruskan ke peta sebagai marker serta polygon.| Pemicu | Request | State yang diisi |
|---|---|---|
| mount komponen | GET /provinces.json | provinces, provincesDetail |
provCode terisi | GET /regencies/{provCode}.json | regencies, regenciesDetail |
regCode terisi | GET /districts/{regCode}.json | districts |
distCode terisi | GET /villages/{distCode}.json | villages |
distCode terisi | GET /districts/{distCode}.json | selectedDist |
villCode terisi | GET /villages/{villCode}.json | selectedVill |
| salah satu kode terisi | GET /paths/{code}.json | polygon |
Request yang sedang dipakai selalu ditampilkan di panel bawah sebagai tautan GET {dataUrl}, lengkap dengan durasi fetchMs (web/src/components/sections/PlaygroundSection.tsx:412-41917, web/src/components/sections/PlaygroundSection.tsx:456-46618). Efek-efek itu tidak memakai AbortController; pembatalan dilakukan dengan flag cancelled di dalam closure, seperti pada efek provinsi (web/src/components/sections/PlaygroundSection.tsx:95-12111).
Dropdown bertingkat: desktop berantai, mobile bertumpuk
Di layar lebar, empat Select disusun sejajar dan dikunci berantai: kab/kota nonaktif tanpa provCode, kecamatan nonaktif tanpa regCode (web/src/components/playground/RegionDropdowns.tsx:391-41619). Di layar sempit, MobileStackCarousel menggantinya dengan tumpukan kartu untuk empat level (web/src/components/playground/RegionDropdowns.tsx:84-8920) yang memakai disabledByUnlock sebagai penentu kunci (web/src/components/playground/RegionDropdowns.tsx:208-21321).
Pemilihan nilai langsung memajukan kartu aktif, dan efek lanjutan memajukan lagi ketika kode berubah (web/src/components/playground/RegionDropdowns.tsx:99-10722, web/src/components/playground/RegionDropdowns.tsx:156-16623). Kalau nilai tingkat atas dihapus, posisi aktif dijepit kembali ke langkah yang masih bisa dijangkau (web/src/components/playground/RegionDropdowns.tsx:137-14524). Kartu bisa digeser; ambang geser 48 piksel menentukan pindah langkah (web/src/components/playground/RegionDropdowns.tsx:190-19725).
stateDiagram-v2
[*] --> Provinsi
Provinsi --> Kabupaten: handleProv mengisi provCode
Kabupaten --> Kecamatan: handleReg mengisi regCode
Kecamatan --> Kelurahan: handleDist mengisi distCode
Kabupaten --> Provinsi: handleProv mereset kode di bawahnya
Kecamatan --> Kabupaten: handleReg mereset distCode dan villCode
Kelurahan --> Kecamatan: handleDist mereset villCode
classDef unlocked fill:#2d7d46,color:#fff
classDef locked fill:#f59e0b,color:#000
class Provinsi unlocked
class Kabupaten,Kecamatan,Kelurahan locked
%% fig: Keadaan pilihan wilayah bertingkat
%% desc: Tingkat terdalam terbuka hanya setelah kode induknya terisi, dan memilih ulang tingkat atas mengosongkan kode di bawahnya.Reset disediakan sebagai satu tombol di panel desktop. handleReset mengosongkan keempat kode sekaligus dan membuang polygon (web/src/components/sections/PlaygroundSection.tsx:327-34926).
Peta MapLibre, tile ganda, dan polygon
Peta memakai MapLibre. Dua sumber tile tersedia: gaya vektor OSM_VECTOR_STYLE dari OpenFreeMap Liberty, dan gaya raster Esri World Imagery yang dibangun getEsriSatelliteStyle (web/src/components/playground/MapLibreIndonesiaMap.tsx:9-1627, web/src/components/playground/MapLibreIndonesiaMap.tsx:43-6528). Peta diinisialisasi sekali dengan pusat [118.0, -2.5] dan zoom 5 (web/src/components/playground/MapLibreIndonesiaMap.tsx:102-14329). NavigationControl hanya dipasang di desktop lewat useIsMobile dengan breakpoint 768 piksel (web/src/components/playground/MapLibreIndonesiaMap.tsx:67-7730).
Mengganti tile memanggil map.setStyle, yang menghapus source dan layer, lalu menambahkannya lagi setelah styledata; ada timer 300 ms sebagai jaring pengaman (web/src/components/playground/MapLibreIndonesiaMap.tsx:183-21331). Marker dan popup dibangun ulang tiap kali wilayah terpilih berubah (web/src/components/playground/MapLibreIndonesiaMap.tsx:216-27532), dan flyTo memakai durasi 1200 ms (web/src/components/playground/MapLibreIndonesiaMap.tsx:277-30633).
Polygon digambar sebagai source wilayah-polygon dengan layer fill #10b981 beropasitas 0.18 dan layer garis #059669 selebar 2 (web/src/components/playground/MapLibreIndonesiaMap.tsx:346-37134). Setelah polygon masuk, peta memanggil fitBounds dengan padding 24, maxZoom 19, dan durasi 1000 ms (web/src/components/playground/MapLibreIndonesiaMap.tsx:394-43835).
| Aspek | Nilai | Rujukan |
|---|---|---|
| Tile default | osm (vektor Liberty) | web/src/components/playground/MapLibreIndonesiaMap.tsx:9-1627 |
| Tile alternatif | esri (raster World Imagery) | web/src/components/playground/MapLibreIndonesiaMap.tsx:43-6528 |
Durasi flyTo | 1200 ms | web/src/components/playground/MapLibreIndonesiaMap.tsx:277-30633 |
| Warna fill polygon | #10b981, opasitas 0.18 | web/src/components/playground/MapLibreIndonesiaMap.tsx:346-37134 |
Padding fitBounds | 24, maxZoom 19 | web/src/components/playground/MapLibreIndonesiaMap.tsx:394-43835 |
Zoom ditentukan dari tingkat terdalam yang terpilih: kelurahan 13, kecamatan 12, kab/kota 10, provinsi 8, dan 5 ketika belum ada pilihan (web/src/components/sections/PlaygroundSection.tsx:31836). Target peta memakai prioritas kelurahan, kecamatan, kab/kota, lalu provinsi (web/src/components/sections/PlaygroundSection.tsx:281-30437), dan seluruh nilai itu diteruskan ke komponen peta sebagai prop (web/src/components/sections/PlaygroundSection.tsx:353-36138).
Normalisasi ring sebelum digambar
Bentuk path dari API tidak seragam kedalamannya, jadi playground menormalkannya lebih dulu. Komentar di mapUtils.ts mencatat tiga kasus: single ring kedalaman 2, multi ring kedalaman 3, dan penyarangan dalam seperti Papua Barat 92 pada kedalaman 4 (web/src/components/playground/mapUtils.ts:1-2639). normalizeRings menelusuri node secara rekursif, dan hanya menyimpan ring dengan lebih dari dua titik (web/src/components/playground/mapUtils.ts:1-2639).
Setelah normalisasi, toLngLat menukar pasangan [lat,lng] menjadi [lng,lat] karena MapLibre memakai urutan longitude lebih dulu (web/src/components/playground/mapUtils.ts:28-3140). Helper ringsToGeoJSON juga tersedia untuk membangun FeatureCollection (web/src/components/playground/mapUtils.ts:33-4641). Detail format path di response API dibahas di Polygon dan Boundary.
Kontrak data dan sisa dependensi
Playground memakai dua tipe: Region minimal dan RegionDetail yang menambahkan level 1 sampai 4 beserta capital, elv, tz, population, total_area, postal_code, has_path, dan objek induk (web/src/components/playground/types.ts:9-2142). toRegion dan toRegionDetail memetakan response API ke tipe ini, dengan fallback lat/lng ke 0 ketika daftar tingkat kecamatan atau kelurahan tidak membawa koordinat (web/src/components/sections/PlaygroundSection.tsx:29-5743).
Peta yang dipakai sekarang adalah MapLibre (web/src/components/sections/PlaygroundSection.tsx:353-36138), tetapi leaflet dan react-leaflet masih tercatat sebagai dependency (web/package.json:21-2644), dan stylesheet Leaflet 1.9.4 masih dimuat di head Layout.astro (web/src/layouts/Layout.astro:47-5245). Halaman ini tidak menggambarkan isi playground/IndonesiaMap.tsx; yang pasti, ia bukan komponen yang dipasang di index.astro.
Build dan server pengembangan memakai skrip Astro standar: dev, build, preview, dan astro (web/package.json:8-1346). Dependensi runtime mencakup astro, @astrojs/react, tailwindcss, maplibre-gl, framer-motion, lucide-react, dan highlight.js (web/package.json:14-2847). Langkah build dan deploy situs ini dibahas di Deploy dan CI.