Berita (News)
Ringkasan
Modul Berita mengelola artikel, publikasi, dan pengumuman yang ditampilkan di laman publik. Ini adalah CMS lengkap: kategori artikel, konten dwibahasa (terlokalisasi), penyunting teks kaya, tag SEO, gambar unggulan (thumbnail), pratinjau perangkat langsung, analitik pembaca, dan mesin rekomendasi yang dipersonalisasi.
Modul ini memiliki dua sisi:
- Panel admin — cluster Filament di
/portal/news(label navigasi News, grup Master). - Situs publik — laman
/articlesbeserta endpoint analitik/API.
Entitas Utama
| Entitas | Deskripsi |
|---|---|
| Article | Konten berita — judul/konten/ringkasan terlokalisasi, slug, tag, kategori, sekolah, penulis, thumbnail, SEO meta, tanggal terbit, status aktif |
| ArticleCategory | Pengelompokan kategori artikel — nama/deskripsi terlokalisasi, slug, status aktif |
| ArticleAnalytic | Analitik sesi baca per artikel — fingerprint, durasi, kedalaman scroll, selesai/tidak, perangkat, lokasi geografis |
Panel Admin (/portal/news)
Cluster ini berisi tiga area:
| Area | URL | Fungsi |
|---|---|---|
| Articles | /portal/news/articles | Kelola artikel (daftar, buat, ubah, lihat) |
| Article Categories | /portal/news/article-categories | Kelola kategori |
| Popular Articles | /portal/news/popular-articles-page | Dasbor analitik pembaca |
1. Articles — Halaman Daftar
Kolom
| Kolom | Catatan |
|---|---|
| Name (judul) | Dapat dicari, terlokalisasi |
| School | Menampilkan nama sekolah atau placeholder General |
| Category | Nama kategori atau - |
| Slug | Dapat dicari |
| Author | Dapat dicari, - jika kosong |
| Status | Badge — Active (hijau) / Inactive (abu-abu) |
Filter
- Status — filter ternary: Aktif / Nonaktif
- School — pilihan dengan opsi General
- Category — pilihan dari kategori yang ada
- Published Date — rentang tanggal (dari / sampai)
- Created Date — rentang tanggal (dari / sampai)
Pencarian global menggunakan pencarian teks penuh (Meilisearch) — mencocokkan judul, konten, dan ringkasan dalam kedua bahasa, plus penulis dan tag, dengan peringkat relevansi.
Aksi per baris: Lihat, Ubah. Aksi massal: Hapus (baris terpilih).
2. Articles — Formulir Buat / Ubah
Formulir terbagi menjadi kolom Umum / Konten terlokalisasi (lebar 3) dan sidebar Thumbnail / SEO (lebar 1).
Bagian Umum
| Kolom | Wajib | Catatan |
|---|---|---|
| School (opsional) | Tidak | Default General — tanpa sekolah |
| Category | Tidak | Pilihan dengan + buat opsi (buat inline: slug + nama/deskripsi terlokalisasi) |
| Slug | Ya | Harus unik (divalidasi) — mis. panduan-pendaftaran |
| Author | Tidak | Teks bebas, mis. Admin |
| Published At | Tidak | Pilih tanggal, tanggal minimum = hari ini (penjadwalan) |
| Tags | Tidak | Input tag dipisah koma/enter |
Konten terlokalisasi (tab per bahasa, EN/ID)
| Kolom | Wajib (bahasa dasar) | Catatan |
|---|---|---|
| Name (judul) | Ya | Teks biasa |
| Excerpt | Ya | Textarea, dirender sebagai pratinjau HTML di halaman detail publik |
| Content | Ya | Penyunting teks kaya headless (heading, kutipan, media, menu cepat /) |
Tab lokalisasi menyediakan tombol Auto-translate yang menyalin konten bahasa dasar ke bahasa saat ini.
Bagian Thumbnail
| Kolom | Catatan |
|---|---|
| Upload Image | Unggah gambar (disk publik, folder articles/, editor gambar), disimpan sebagai Asset, pratinjau via ImageKit |
| Alt text | Maks 150 karakter, untuk aksesibilitas |
Bagian SEO
- Meta tags repeater (modal) — daftar pasangan
{name, content}. nameadalah pilihan kunci SEO yang telah ditentukan (mis.description,keywords,og:title,og:image,twitter:...) dengan opsi + buat untuk nama kustom; setiap nama hanya boleh dipakai sekali (divalidasiUniqueSeoName).contentteks bebas, maks 255 karakter.
3. Articles — Halaman Lihat
Menampilkan:
- Info umum — slug, kategori, sekolah, penulis, badge status, tag
- Nilai terlokalisasi — judul, ringkasan (dirender sebagai prosa/pratinjau HTML), konten (dirender dari penyunting teks kaya)
- Kartu Pratinjau Langsung — iframe halaman artikel publik dengan pemilih perangkat Desktop / Tablet / Mobile, mockup berbingkai, skala responsif, dan status memuat.
4. Article Categories — Halaman Kelola
Formulir
| Kolom | Wajib | Catatan |
|---|---|---|
| Slug | Ya | Unik, maks 100 karakter |
| Active | Tidak | Switcher, default aktif |
| Name (terlokalisasi) | Ya | Maks 255 karakter |
| Description (terlokalisasi) | Tidak | Textarea |
Kolom tabel: Name, Slug (badge, dapat dicari/diurutkan), Articles (jumlah per kategori, dapat diurutkan), Active (ikon boolean), Created Date (dapat diurutkan, tersembunyi secara default, dapat diaktifkan).
Aksi per baris: Ubah, Hapus. Urutan default: berdasarkan slug.
Hanya kategori Aktif yang muncul di situs publik.
5. Popular Articles — Dasbor Analitik
- Kartu statistik (polling tiap 60 dtk): Total Kategori Artikel, Artikel Belum Dipublikasikan (
published_atnull atau di masa depan), Artikel Sudah Dipublikasikan (published_at <= now). - Geografi Pembaca — grafik treemap pembacaan per negara (dari geo-IP; grup "Tidak Diketahui").
- Tabel Artikel Populer — per artikel: Dibaca, Pembaca Unik, Total Waktu (dibaca manusia:
45s/3m 20s/1h 5m), Selesai. Urutan default: total waktu menurun. Pagination 5 / 10 / 25. State kosong saat belum ada data analitik.
Alur Publikasi
Diagram berikut menunjukkan bagaimana sebuah artikel bergerak dari formulir admin ke situs publik:
Aturan kunci
- Artikel terlihat publik hanya jika
is_active = truedanpublished_atterisi dan tidak di masa depan. - Menetapkan
published_atmasa depan menjadwalkan artikel — tetap tersembunyi (belum terbit) hingga tanggal tersebut. - Setiap penyimpanan membatalkan cache rekomendasi/analitik sehingga perubahan langsung terlihat.
Situs Publik
Rute
| Rute | Halaman | Deskripsi |
|---|---|---|
GET /articles | Articles/Index | Jelajahi + filter + cari artikel |
GET /articles/{slug} | Articles/Show | Detail artikel dengan rekomendasi |
GET /articles/{slug}/iframe | Articles/IFrameShow | Tampilan bersih untuk embed (tanpa kerangka layout), dipakai kartu pratinjau admin |
/articles — Daftar
- Kotak pencarian — pencarian teks penuh atas judul/konten/ringkasan (kedua bahasa), penulis, tag.
- Sidebar filter — Kategori (multi-pilih), Penulis, Tahun; Urutkan: Terbaru / Terlama / A–Z / Z–A.
- Jumlah hasil + Pagination, state kosong dengan tombol reset, bilah pencarian lengket dengan scroll-balik halus saat filter berubah.
- Daftar sisi server dibatasi 100 artikel aktif terbaru; filter/urutkan/pagination terjadi di sisi klien atas set tersebut. Artikel yang lebih lama tetap bisa diakses via URL langsung.
- Kategori dimuat lambat via
Inertia::defer(skeleton hingga siap).
/articles/{slug} — Detail
Hanya artikel aktif yang ditampilkan; selain itu mengembalikan 404.
- Header: badge kategori, judul, meta (penulis / tanggal terbit / waktu baca — disembunyikan bila kosong), tautan kembali.
- Excerpt dirender sebagai pratinjau HTML di atas konten.
- Badan: konten teks kaya dengan penanda selesai-baca.
- Lucky Me interstitial — scroll memicu artikel acak lainnya.
- Sidebar: artikel yang direkomendasikan (dipersonalisasi), artikel terkait per penulis.
- Bagian seluler: rekomendasi + artikel penulis yang sama.
- SEO: tag meta kustom dari repeater admin dirender sebagai
<meta>(og:*memakai atributproperty, lainnyaname); fallback wajar (description, og:title, og:description, og:type, og:image, article:author, article:section, article:published_time, og:url).
/articles/{slug}/iframe
Tampilan artikel minimal untuk embed (pratinjau admin, embed pihak ketiga).
Alur SEO
Diagram berikut menunjukkan bagaimana tag meta yang dikonfigurasi di formulir admin dirender di halaman artikel publik:
Kunci bawaan (dari enum SEO): description, keywords, author, robots, og:title, og:description, og:image, og:url, og:type, og:site_name, twitter:card, twitter:title, twitter:description, twitter:image — plus nama kustom apa pun yang ditambahkan via opsi + buat.
Fallback yang selalu dirender di halaman detail: og:title, og:type (article), og:url, og:image (jika ada), article:author, article:section, article:published_time, plus description / og:description yang diturunkan dari ringkasan jika tidak ada tag SEO kustom yang menimpanya.
Analitik
Sesi baca dilacak di sisi klien (mulai / scroll / selesai) lalu dikirim ke server.
Endpoint: POST /api/articles/track
Payload: article_id (wajib), fingerprint_id (wajib), session_id, read_duration_seconds, scroll_depth (0–100), completed, referrer_url.
Perilaku:
updateOrCreatepada (artikel, fingerprint, sesi) — satu sesi per pembaca per artikel.- Durasi dan kedalaman scroll hanya bertambah (monotonik).
- IP di-resolve ke negara/kota/zona waktu via
ip-api.com(cache 24 jam, best-effort, dilewati untuk localhost). - Tipe perangkat dideteksi dari user-agent (desktop / tablet / mobile).
- Status selesai ditandai saat pembaca melewati penanda akhir artikel (IntersectionObserver).
Alur analitik — dari sesi baca ke dasbor
Mesin Rekomendasi
RoundupService menghasilkan daftar "artikel yang direkomendasikan" (default 8) di halaman detail.
Alur
Skor dasar (cache Redis 10 menit, dibagi antar pengunjung):
| Sinyal | Bobot |
|---|---|
| Kategori sama | 0,35 |
| Sekolah sama | 0,15 |
| Popularitas (pembacaan) | 0,15 |
| Kebaruan (peluruhan selama 6 bulan) | 0,10 |
Personalisasi (diterapkan per fingerprint di atas skor dasar cache):
| Sinyal | Bobot |
|---|---|
| Kolaboratif — pembaca artikel ini juga membaca... | hingga 0,25 |
| Riwayat kategori pengguna ini | hingga 0,15 |
Setiap buat / ubah / hapus artikel membatalkan cache roundup dan analitik (ArticleObserver).
Basis Data
| Tabel | Deskripsi |
|---|---|
articles | PK UUID; title/content/excerpt (JSON terlokalisasi), slug (unik, route key), tags, category_id, school_id, author, is_active, published_at, image_asset_id, asset_alt_text, meta (JSON SEO) |
article_categories | PK UUID; name/description terlokalisasi, slug, is_active |
article_analytics | PK UUID; article_id, fingerprint_id, session_id, ip_address, user_agent, referrer_url, started_at, last_active_at, read_duration_seconds, scroll_depth, completed, device_type, country, city, timezone |
Fitur
Admin — Manajemen Artikel
- CRUD — buat, ubah, lihat, dan hapus artikel; hapus massal dari daftar.
- Konten dwibahasa — judul, ringkasan, dan konten terlokalisasi (EN/ID) dengan tab per bahasa.
- Auto-translate — salin konten bahasa dasar ke bahasa lain dengan satu klik.
- Penyunting teks kaya — heading, subjudul, kutipan, tautan, dan menu cepat
/. - Sisipkan media di konten — unggah dan sematkan gambar langsung di dalam badan artikel.
- Thumbnail — unggah gambar unggulan (dengan editor gambar) plus teks alternatif untuk aksesibilitas, dilayani via ImageKit.
- Tag SEO meta — repeater pasangan
{name, content}dengan kunci bawaan + kustom dan validasi nama unik; dirender sebagai tag<meta>di halaman publik. - Penjadwalan — tetapkan tanggal terbit di masa depan; artikel tetap belum dipublikasikan hingga tanggal tersebut.
- Tags — input tag bebas pada artikel.
- Kategori — kelola kategori dengan nama terlokalisasi, atau buat inline dari formulir artikel.
- Pencarian & filter — pencarian teks penuh (Meilisearch) plus filter status/sekolah/kategori/rentang tanggal.
- Pratinjau perangkat langsung — lihat artikel publik di dalam bingkai desktop/tablet/mobile.
Admin — Dasbor Analitik
- Ringkasan statistik — total kategori, jumlah artikel terbit, dan belum terbit.
- Geografi Pembaca — treemap pembacaan per negara.
- Artikel Populer — pembacaan, pembaca unik, total waktu baca, dan selesai per artikel.
Publik — Artikel
- Daftar — jelajahi
/articlesdengan pencarian, filter kategori/penulis/tahun, urutan (terbaru/terlama/A–Z/Z–A), pagination, dan state kosong. - Detail artikel — badge kategori, judul, meta penulis/tanggal/waktu baca, ringkasan sebagai pratinjau HTML, dan badan teks kaya.
- Rekomendasi — daftar "disarankan" yang dipersonalisasi plus artikel terkait per penulis.
- Lucky Me — interstitial saat scroll yang melompat ke artikel acak.
- SEO — tag meta kustom, tag Open Graph, dan fallback yang dirender di
<head>. - Lokalisasi — ganti EN ↔ ID dengan cepat tanpa reload.
- Tampilan dapat disematkan —
/articles/{slug}/iframebersih untuk embed dan pratinjau admin.
Analitik (pelacakan)
- Sesi baca — lacak mulai, durasi, kedalaman scroll, dan selesai per pembaca.
- Geo-lokasi — negara/kota/zona waktu di-resolve dari IP (cache, best-effort).
- Deteksi perangkat — desktop / tablet / mobile dari user-agent.
Berkas Utama
| Berkas | Fungsi |
|---|---|
app/Filament/Clusters/Article/ArticleCluster.php | Definisi cluster — slug news, navigasi |
app/Filament/Clusters/Article/Resources/Articles/ArticleResource.php | Resource artikel — halaman, mutate/fill data terlokalisasi |
app/Filament/Clusters/Article/Resources/Articles/Schemas/ArticleForm.php | Formulir buat/ubah artikel |
app/Filament/Clusters/Article/Resources/Articles/Schemas/ArticleInfolist.php | Halaman lihat artikel + kartu pratinjau langsung |
app/Filament/Clusters/Article/Resources/Articles/Tables/ArticlesTable.php | Daftar artikel — kolom, filter, pencarian Meilisearch |
app/Filament/Clusters/Article/Resources/ArticleCategories/ArticleCategoryResource.php | CRUD kategori |
app/Filament/Clusters/Article/Pages/PopularArticlesPage.php | Halaman dasbor analitik |
app/Filament/Clusters/Article/Resources/Articles/Widgets/*.php | Widget statistik, tabel populer, treemap geografi |
app/Filament/Schemas/Components/Cards/ArticlePreview.php | Komponen pratinjau perangkat langsung |
app/Http/Controllers/Landing/ArticleController.php | Index / show / iframe publik |
app/Http/Controllers/Api/ArticleAnalyticController.php | Endpoint pelacakan sesi |
app/Http/Controllers/Api/ArticleContentController.php | Ambil konten mentah |
app/Http/Controllers/Api/ArticleLuckyController.php | Endpoint artikel acak |
app/Services/News/RoundupService.php | Skor rekomendasi + personalisasi |
app/Services/News/AnalysisService.php | Agregasi analitik (cache) |
app/Observers/News/ArticleObserver.php | Pembatalan cache saat artikel berubah |
app/Models/News/Article/Article.php | Model Artikel |
app/Models/News/ArticleCategory/ArticleCategory.php | Model Kategori |
app/Models/News/ArticleAnalytic/ArticleAnalytic.php | Model Analitik |
resources/js/pages/landing/Articles/*.svelte | Halaman publik daftar / detail / iframe |
resources/js/modules/landing/articles/ | Store frontend, filter, pelacakan analitik, UI rekomendasi |
Glosarium
| Istilah | Definisi |
|---|---|
| Article (Artikel) | Entri berita yang dikelola di panel admin dan ditampilkan di situs publik |
| Slug | Identitas unik ramah-URL untuk artikel atau kategori (mis. panduan-pendaftaran); dipakai sebagai route key — artikel dapat diakses di /articles/{slug} |
| Excerpt (Ringkasan) | Ringkasan singkat artikel (terlokalisasi, per bahasa) yang ditampilkan sebagai pratinjau HTML di halaman detail publik dan dipakai sebagai fallback meta-description |
| Content (Konten) | Isi utama artikel, ditulis di penyunting teks kaya (terlokalisasi, per bahasa) |
Published At (published_at) | Tanggal/waktu artikel menjadi (atau sudah) publik; jika di masa depan, artikel tetap tersembunyi hingga saat tersebut |
Is Active (is_active) | Flag yang mengontrol visibilitas publik — hanya artikel aktif yang muncul di /articles dan di pencarian |
| Tags (Tag) | Kata kunci bebas yang dilampirkan ke artikel; dapat dicari via pencarian teks penuh |
| Category (Kategori) | Pengelompokan yang mengorganisir artikel; kategori terlokalisasi dan bisa aktif/nonaktif |
| Thumbnail | Gambar unggulan artikel (sebuah Asset); ditampilkan di kartu dan dipakai untuk og:image |
| Alt text (Teks alternatif) | Deskripsi aksesibilitas untuk gambar unggulan |
Meta (meta) | Kolom JSON pada artikel yang menyimpan data repeater SEO (meta.seo — daftar pasangan {name, content}) |
| Tag SEO meta | Satu entri {name, content} yang dikonfigurasi di repeater admin, dirender sebagai <meta> di halaman publik (kunci og:* memakai property) |
| Localization (Lokalisasi) | Dukungan kolom yang sama dalam beberapa bahasa (EN/ID) disimpan sebagai JSON; halaman publik me-re-lokalisasi tanpa reload |
Fingerprint (fingerprint_id) | Identitas stabil sisi klien (cookie/dibangkitkan) yang mengenali pembaca lintas sesi tanpa akun; dipakai untuk analitik dan personalisasi rekomendasi |
Session (session_id) | Identitas satu sesi baca artikel oleh seorang pembaca |
Read duration (read_duration_seconds) | Total detik pembaca menghabiskan waktu di sebuah artikel; hanya bertambah (monotonik) |
Scroll depth (scroll_depth) | Seberapa jauh (0–100%) pembaca menggulir artikel |
Completed (completed) | Flag yang disetel saat pembaca melewati penanda akhir artikel — dipakai untuk metrik selesai |
| Sentinel (Penanda) | Elemen tersembunyi di akhir badan artikel yang diamati via IntersectionObserver untuk mendeteksi selesai membaca |
| ArticleAnalytic | Satu baris per sesi baca artikel, mengagregasi metrik di atas |
| Lucky Me | Interstitial pemicu-scroll di halaman detail yang menavigasi ke artikel acak lain |
| Recommended articles (Artikel disarankan) | Daftar terpersonalisasi yang dibangkitkan RoundupService (sinyal kategori/sekolah/popularitas/kebaruan + kolaboratif/riwayat) |
| Reader Geography (Geografi Pembaca) | Treemap dasbor pembacaan per negara, di-resolve dari IP pembaca |
| Meilisearch | Mesin pencarian teks penuh yang mendukung pencarian global admin dan kotak pencarian publik |