Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions app/Http/Controllers/Web/ArtikelController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Services\ArtikelService;
use Illuminate\Http\Request;

class ArtikelController extends Controller
{
protected ArtikelService $artikelService;

public function __construct(ArtikelService $artikelService)
{
$this->artikelService = $artikelService;
}

/**
* Tampilkan daftar artikel OpenSID.
*
* @param Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
$search = $request->get('search', '');
$categoryId = $request->get('kategori', '');

$filters = [];
if (!empty($search)) {
$filters['filter[search]'] = $search;
}
if (!empty($categoryId)) {
$filters['filter[id_kategori]'] = $categoryId;
}

// Ambil data melalui service
// Format Pagination API json format
$filters['page[number]'] = $request->get('page', 1);
$filters['page[size]'] = 6;
$filters['sort'] = '-tgl_upload'; // Terurut berdasarkan tanggal terbaru

// Caching ditangani oleh ArtikelService
$articles = $this->artikelService->artikel($filters);

// Filter out disabled articles just in case API returns them
$articles = $articles->filter(function ($item) {
return isset($item->enabled) && $item->enabled == 1;
});

return view('web.artikel.index', [
'title' => 'Artikel Berita',
'articles' => $articles,
'search' => $search,
'categoryId' => $categoryId
]);
}

/**
* Tampilkan detail artikel OpenSID.
*
* @param int $id
* @return \Illuminate\View\View
*/
public function show($id)
{
$article = $this->artikelService->artikelById($id);

if (!$article || !isset($article->enabled) || $article->enabled == 0) {
abort(404, 'Artikel tidak ditemukan atau tidak aktif');
}

return view('web.artikel.show', [
'object' => $article
]);
}
}
26 changes: 20 additions & 6 deletions app/Services/ArtikelService.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ public function artikel(array $filters = [])

// Ambil dari cache dulu
return Cache::remember($cacheKey, $this->cacheTtl, function () use ($filters) {
$data = $this->apiRequest('/api/v1/artikel', $filters);
if (! $data) {
$data = $this->apiRequest('/api/v1/artikel/list', $filters);
if (!$data) {
return collect([]);
}

return collect($data)->map(fn ($item) => (object) $item['attributes']);
return collect($data)->map(function ($item) {
// Return 'attributes' but with 'id' populated
$attributes = $item['attributes'] ?? [];
$attributes['id'] = $item['id'] ?? null;

// Fetch detail to enrich with gambar and isi if missing
if (isset($attributes['id']) && (!isset($attributes['gambar']) || !isset($attributes['isi']))) {
$detail = $this->artikelById($attributes['id']);
if ($detail) {
$attributes['gambar'] = $detail->gambar ?? null;
$attributes['isi'] = $detail->isi ?? null;
}
}

return (object) $attributes;
});
});
}

Expand All @@ -32,8 +46,8 @@ public function artikelById(int $id)
'id' => $id,
]);

if (is_array($data) && isset($data['data'])) {
return (object) $data['data'];
if (is_array($data) && count($data) > 0) {
return (object) $data;
}

return null;
Expand Down
89 changes: 89 additions & 0 deletions resources/views/web/artikel/index.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@extends('layouts.web')

@section('content')
<div class="ps-5">
<!-- Header Start -->
<div class="container-fluid header-halaman bg-white mb-3">
<div class="row g-0 align-items-center">
<div class="col-md-12 mt-lg-5">
<h1 class="display-5 animated fadeIn mb-4">{{ $title }}</h1>
<nav aria-label="breadcrumb animated fadeIn">
<ol class="breadcrumb text-uppercase">
<li class="breadcrumb-item"><a href="/">Beranda</a></li>
<li class="breadcrumb-item"><a href="#">Halaman</a></li>
<li class="breadcrumb-item text-body active" aria-current="page">{{ $title }}</li>
</ol>
</nav>
</div>
</div>
</div>
<!-- Header End -->

<!-- Search and Filter -->
<div class="container-fluid mb-4">
<form action="{{ route('web.artikel.index') }}" method="GET" class="row g-3 px-3">
<div class="col-md-5">
<input type="text" name="search" class="form-control" placeholder="Cari artikel..."
value="{{ request('search') }}">
</div>
<!-- Optional: Dropdown kategori bisa ditambahkan jika API kategori publik tersedia -->
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">Cari</button>
</div>
</form>
</div>

<!-- Konten Halaman -->
<div class="container-fluid">
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3 mb-5 px-3">
@forelse ($articles as $article)
<div class="col">
<div class="card shadow-sm h-100">
@if (isset($article->gambar) && !empty($article->gambar))
<img src="{{ $article->gambar }}" width="100%" height="225" class="card-img-top object-fit-cover"
alt="{{ $article->judul ?? '' }}">
@else
<svg class="bd-placeholder-img card-img-top" width="100%" height="225"
xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail"
preserveAspectRatio="xMidYMid slice" focusable="false">
<title>Placeholder</title>
<rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"
dy=".3em">Thumbnail</text>
</svg>
@endif
<div class="card-body d-flex flex-column">
<h5 class="card-title">{{ $article->judul ?? '' }}</h5>
<div class="mb-2">
<span class="badge bg-primary">{{ $article->kategori_nama ?? 'Kategori' }}</span>
</div>
<div class="card-text flex-grow-1">
{!! Str::words(strip_tags($article->isi ?? ''), 20, '...') !!}
</div>
<div class="d-flex justify-content-between align-items-center mt-3">
<div>
<a href="{{ route('web.artikel.show', $article->id ?? '') }}"
class="text-decoration-none btn btn-sm btn-outline-primary">Selengkapnya</a>
</div>
<small class="text-muted">
{{ isset($article->tgl_upload) ? \Carbon\Carbon::parse($article->tgl_upload)->translatedFormat('d F Y') : '' }}
</small>
</div>
</div>
</div>
</div>
@empty
<div class="col-lg-12">
<div class="alert alert-warning text-center">
<strong>Belum ada artikel yang dipublikasikan.</strong>
</div>
</div>
@endforelse
</div>

<!-- Mengingat ini adalah array/collection dari API (bukan standard Laravel paginator),
UI pagination native agak tricky untuk di-render otomatis.
Kita tampilkan tombol Muat Lebih Banyak dengan parameter halaman jika diperlukan -->
</div>
<!-- Konten Halaman -->
</div>
@endsection
74 changes: 74 additions & 0 deletions resources/views/web/artikel/show.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
@extends('layouts.web')

@section('content')
<div class="ps-5">
<!-- Header Start -->
<div class="container-fluid header-halaman bg-white mb-3">
<div class="row g-0 align-items-center">
<div class="col-md-12 mt-lg-5">
<nav aria-label="breadcrumb animated fadeIn">
<ol class="breadcrumb text-uppercase">
<li class="breadcrumb-item"><a href="/">Beranda</a></li>
<li class="breadcrumb-item"><a href="{{ route('web.artikel.index') }}">Artikel</a></li>
<li class="breadcrumb-item text-body active" aria-current="page">
{{ Str::limit($object->judul ?? 'Detail Artikel', 30) }}</li>
</ol>
</nav>
</div>
</div>
</div>
<!-- Header End -->

<!-- Konten Halaman -->
<div class="container-fluid px-4 mb-5">
<div class="row justify-content-center">
<div class="col-lg-10 wow fadeIn" data-wow-delay="0.1s">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white pb-0 border-0 text-center">
<h2 class="mt-4 mb-3">{{ $object->judul ?? '' }}</h2>
<div class="d-flex justify-content-center gap-3 mb-4 text-muted">
<span><i
class="fa fa-folder-open text-primary me-2"></i>{{ $object->kategori_nama ?? 'Kategori' }}</span>
<span><i
class="fa fa-calendar-alt text-primary me-2"></i>{{ isset($object->tgl_upload) ? \Carbon\Carbon::parse($object->tgl_upload)->translatedFormat('d F Y') : '' }}</span>
</div>
</div>

@if (isset($object->gambar) && !empty($object->gambar))
<img src="{{ $object->gambar }}" class="card-img-top img-fluid px-4 mb-3 rounded"
alt="{{ $object->judul ?? '' }}" style="max-height: 500px; object-fit: contain;">
@endif

<div class="card-body p-4 p-md-5">
<div class="artikel-content">
{!! $object->isi ?? '' !!}
</div>
</div>

<div class="card-footer bg-white text-center py-4 border-top">
<a href="{{ route('web.artikel.index') }}" class="btn btn-outline-primary px-4"><i
class="fa fa-arrow-left me-2"></i>Kembali ke Daftar Artikel</a>
</div>
</div>
</div>
</div>
</div>
<!-- Konten Halaman -->
</div>
@endsection

@push('styles')
<style>
.artikel-content img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 1rem 0;
}

.artikel-content {
line-height: 1.8;
font-size: 1.05rem;
}
</style>
@endpush
12 changes: 9 additions & 3 deletions resources/views/web/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
</div>
<!-- Property List End -->

<!-- Artikel Terbaru Start -->
<div class="container-xxl py-5">
@include('web.partials.artikel_terbaru')
</div>
<!-- Artikel Terbaru End -->

<!-- Team Start -->
<div class="container-xxl py-5">
@include('web.partials.team')
Expand All @@ -41,7 +47,7 @@

@push('scripts')
<script nonce="{{ csp_nonce() }}" type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
document.addEventListener("DOMContentLoaded", function (event) {
"use strict";

const header = @include('layouts.components.header_bearer_api_gabungan');
Expand All @@ -52,7 +58,7 @@
method: 'GET',
dataType: 'json',
headers: header,
success: function(result) {
success: function (result) {
let category = result.data.categoriesItems;

for (let index in category) {
Expand All @@ -62,4 +68,4 @@
});
});
</script>
@endpush
@endpush
Loading