Compare commits

..

4 Commits

Author SHA1 Message Date
arifal
501a76bc81 partial update circle chart can click 2025-05-06 17:29:39 +07:00
arifal
460267992e handle null value on detail quick search 2025-05-06 15:32:03 +07:00
arifal
becc368069 last update feat quick search 2025-05-06 15:06:59 +07:00
arifal
2618ac06d0 partial update create page quick search 2025-05-05 18:36:56 +07:00
17 changed files with 943 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Dashboards;
use App\Http\Controllers\Controller;
use App\Models\ImportDatasource;
use App\Models\Menu;
use Illuminate\Http\Request;
class BigDataController extends Controller
@@ -12,7 +13,8 @@ class BigDataController extends Controller
$latest_import_datasource = ImportDatasource::latest()->first();
$latest_created = $latest_import_datasource ?
$latest_import_datasource->created_at->format("j F Y H:i:s") : null;
return view('dashboards.bigdata', compact('latest_created'));
$menus = Menu::all();
return view('dashboards.bigdata', compact('latest_created', 'menus'));
}
public function pbg()

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Http\Controllers;
use App\Enums\PbgTaskApplicationTypes;
use App\Enums\PbgTaskStatus;
use App\Http\Resources\TaskAssignmentsResource;
use App\Models\PbgTask;
use App\Models\TaskAssignment;
use Illuminate\Http\Request;
class QuickSearchController extends Controller
{
public function index(){
return view("quick-search.index");
}
public function search_result(Request $request){
$keyword = $request->get("keyword");
return view('quick-search.result', compact('keyword'));
}
public function quick_search_datatable(Request $request)
{
try {
$query = PbgTask::orderBy('id', 'desc');
if ($request->filled('search')) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%")
->orWhere('registration_number', 'LIKE', "%$search%")
->orWhere('address', 'LIKE', "%$search%")
->orWhere('document_number', 'LIKE', "%$search%");
});
}
return response()->json($query->paginate());
} catch (\Throwable $e) {
\Log::error("Error fetching datatable data: " . $e->getMessage());
return response()->json([
'message' => 'Terjadi kesalahan saat mengambil data.',
'error' => $e->getMessage(),
], 500);
}
}
public function show($id)
{
try {
$data = PbgTask::with([
'pbg_task_retributions',
'pbg_task_index_integrations',
'pbg_task_retributions.pbg_task_prasarana'
])->findOrFail($id);
$statusOptions = PbgTaskStatus::getStatuses();
$applicationTypes = PbgTaskApplicationTypes::labels();
return view("quick-search.detail", compact("data", 'statusOptions', 'applicationTypes'));
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
\Log::warning("PbgTask with ID {$id} not found.");
return redirect()->route('quick-search.index')->with('error', 'Data tidak ditemukan.');
} catch (\Throwable $e) {
\Log::error("Error in QuickSearchController@show: " . $e->getMessage());
return response()->view('pages.404', [], 500); // Optional: create `resources/views/errors/500.blade.php`
}
}
public function task_assignments(Request $request, $uuid){
try{
$query = TaskAssignment::query()
->where('pbg_task_uid', $uuid)
->orderBy('id', 'desc');
if ($request->filled('search')) {
$query->where('name', 'like', "%{$request->get('search')}%")
->orWhere('email', 'like', "%{$request->get('search')}%");
}
return TaskAssignmentsResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}catch(\Exception $exception){
return response()->json(['message' => $exception->getMessage()], 500);
}
}
}

View File

@@ -13,13 +13,15 @@ class Circle extends Component
public $document_type;
public $document_id;
public $visible_small_circle;
public function __construct($document_id = "",$document_title = "", $document_type = "", $document_color = "#020c5c", $visible_small_circle = true)
public $document_url;
public function __construct($document_id = "",$document_title = "", $document_type = "", $document_color = "#020c5c", $visible_small_circle = true, $document_url = "#")
{
$this->document_title = $document_title;
$this->document_color = $document_color;
$this->document_type = $document_type;
$this->document_id = $document_id;
$this->visible_small_circle = $visible_small_circle;
$this->document_url = $document_url;
}
/**

View File

@@ -0,0 +1,70 @@
import { Grid } from "gridjs";
class QuickSearchDetail {
init() {
this.initTablePbgTaskAssignments();
}
initTablePbgTaskAssignments() {
let tableContainer = document.getElementById(
"table-pbg-task-assignments"
);
let url_task_assignments = document.getElementById(
"url_task_assignments"
).value;
new Grid({
columns: [
"ID",
"Nama",
"Email",
"Nomor Telepon",
"Keahlian",
"Status",
],
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
server: {
url: `${url_task_assignments}`,
then: (data) => {
return data.data.map((item) => {
const expertiseArray =
typeof item.expertise === "string"
? JSON.parse(item.expertise)
: item.expertise;
return [
item.id,
item.name,
item.email,
item.phone_number,
Array.isArray(expertiseArray)
? expertiseArray.map((e) => e.name).join(", ")
: "-",
item.status_name,
];
});
},
total: (data) => data.meta.total,
},
}).render(tableContainer);
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new QuickSearchDetail().init();
});

View File

@@ -0,0 +1,21 @@
document.addEventListener("DOMContentLoaded", function () {
const searchBtn = document.getElementById("searchBtn");
const searchInput = document.getElementById("searchInput");
searchBtn.addEventListener("click", function () {
const keyword = searchInput.value.trim();
if (keyword !== "") {
// Redirect to the route with query parameter
window.location.href = `/search-result?keyword=${encodeURIComponent(
keyword
)}`;
}
});
// Optional: trigger search on Enter key
searchInput.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
searchBtn.click();
}
});
});

View File

@@ -0,0 +1,135 @@
import { Grid, html } from "gridjs";
class QuickSearchResult {
constructor() {
this.table = null;
const baseInput = document.getElementById("base_url_datatable");
this.baseUrl = baseInput ? baseInput.value.split("?")[0] : "";
this.keywordInput = document.getElementById("search_input");
this.searchButton = document.getElementById("search_button");
this.datatableUrl = this.buildUrl(this.keywordInput.value);
}
init() {
this.bindSearchButton();
this.initDatatable();
}
bindSearchButton() {
const handleSearch = () => {
const newKeyword = this.keywordInput.value.trim();
if (newKeyword !== "") {
// 1. Update datatable URL and reload
this.datatableUrl = this.buildUrl(newKeyword);
this.initDatatable();
// 2. Update URL query string (without reloading the page)
const newUrl = `${
window.location.pathname
}?keyword=${encodeURIComponent(newKeyword)}`;
window.history.pushState({ path: newUrl }, "", newUrl);
// 3. Update visible keyword text in <em>{{ $keyword }}</em>>
const keywordDisplay = document.querySelector(".qs-header em");
if (keywordDisplay) {
keywordDisplay.textContent = newKeyword;
}
}
};
this.searchButton.addEventListener("click", handleSearch);
this.keywordInput.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
event.preventDefault();
handleSearch();
}
});
}
buildUrl(keyword) {
const url = new URL(this.baseUrl, window.location.origin);
url.searchParams.set("search", keyword);
return url.toString();
}
initDatatable() {
const tableContainer = document.getElementById(
"datatable-quick-search-result"
);
const config = {
columns: [
"ID",
{ name: "Name" },
{ name: "Condition" },
"Registration Number",
"Document Number",
{ name: "Address" },
"Status",
"Function Type",
"Consultation Type",
{ name: "Due Date" },
{
name: "Action",
formatter: (cell) => {
return html(`
<a href="/quick-search/${cell.id}"
class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center"
style="white-space: nowrap; line-height: 1;">
<iconify-icon icon="mingcute:eye-2-fill" width="15" height="15" style="vertical-align: middle;"></iconify-icon>
</a>
`);
},
},
],
search: false,
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
server: {
url: this.datatableUrl,
then: (data) =>
data.data.map((item) => [
item.id,
item.name,
item.condition,
item.registration_number,
item.document_number,
item.address,
item.status_name,
item.function_type,
item.consultation_type,
item.due_date,
item,
]),
total: (data) => data.total,
},
};
if (this.table) {
this.table
.updateConfig({
...config,
server: { ...config.server, url: this.datatableUrl },
})
.forceRender();
} else {
tableContainer.innerHTML = "";
this.table = new Grid(config).render(tableContainer);
}
}
}
document.addEventListener("DOMContentLoaded", function () {
const app = new QuickSearchResult();
app.init();
});

View File

@@ -0,0 +1,73 @@
.qs-detail-container {
color: #000; // black text
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
.card {
background-color: #fff;
.card-header {
background-color: #f5f5f5;
font-weight: bold;
color: #000;
}
.card-body {
dt {
font-weight: 600;
color: #000;
}
dd {
margin-bottom: 10px;
color: #000;
}
.nav-tabs {
border-bottom: 1px solid #000;
.nav-link {
color: #000;
border: 1px solid transparent;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
font-weight: 500;
&.active {
background-color: #e0e0e0;
border-color: #000 #000 #fff;
}
&:hover {
color: #000;
}
}
}
.tab-content {
padding: 1rem;
}
.mb-3 {
dt {
font-weight: bold;
}
dd {
margin-left: 0;
}
}
.border {
border-color: #000 !important;
}
.shadow-sm {
box-shadow: none !important;
}
.rounded {
border-radius: 4px !important;
}
}
}
}

View File

@@ -0,0 +1,78 @@
.gsp-body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh; // ensures full vertical centering
background: #f0f2f5;
}
.gsp-icon {
width: 180px; // slightly smaller for better mobile view
height: auto;
margin-bottom: 15px;
}
.gsp-title {
font-size: 36px;
font-weight: 700;
color: #333;
}
.gsp-input {
width: 100%;
height: 44px;
font-size: 16px;
padding: 0 20px;
border-radius: 24px;
border: 1px solid #dfe1e5;
background-color: #fff;
box-shadow: none;
transition: box-shadow 0.2s ease-in-out, border-color 0.2s;
&:focus {
border-color: transparent;
box-shadow: 0 1px 6px rgba(32, 33, 36, 0.28);
outline: none;
}
}
.gsp-btn {
height: 44px;
padding: 0 24px;
font-size: 16px;
border: none;
border-radius: 24px;
background-color: #1a73e8;
color: white;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: #1558d6;
}
}
@media (max-width: 576px) {
.gsp-input-wrapper {
flex-direction: column;
align-items: stretch;
}
.gsp-btn {
width: 100%;
padding: 0 20px;
}
.gsp-input {
padding: 0 20px;
font-size: 16px;
}
.gsp-title {
font-size: 28px;
}
.gsp-icon {
width: 140px;
}
}

View File

@@ -0,0 +1,130 @@
.qs-wrapper {
width: 100%;
margin: 0 auto;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
color: #2c3e50;
}
.qs-toolbar {
border-bottom: 1px solid #e0e0e0;
margin-bottom: 1.5rem;
}
.qs-search-form {
width: 100%;
.gsp-input {
width: 100%;
height: 44px;
font-size: 16px;
padding: 0 20px;
border-radius: 24px;
border: 1px solid #dfe1e5;
background-color: #fff;
box-shadow: none;
transition: box-shadow 0.2s ease-in-out, border-color 0.2s;
&:focus {
border-color: transparent;
box-shadow: 0 1px 6px rgba(32, 33, 36, 0.28);
outline: none;
}
}
.gsp-btn {
height: 44px;
padding: 0 24px;
font-size: 16px;
border: none;
border-radius: 24px;
background-color: #1a73e8;
color: white;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: #1558d6;
}
}
}
.qs-header {
margin-bottom: 30px;
text-align: center;
h2 {
font-size: 24px;
font-weight: 600;
color: #1a237e;
em {
font-style: normal;
color: #0d47a1;
}
}
p {
font-size: 16px;
color: #555;
}
}
.qs-table-wrapper {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
overflow-x: auto; // allow horizontal scroll on small screens
}
/* Grid.js overrides */
.qs-table-wrapper .gridjs {
font-size: 14px;
color: #333;
}
.qs-table-wrapper .gridjs-table {
width: 100%;
border-collapse: collapse;
}
.qs-table-wrapper .gridjs-th,
.qs-table-wrapper .gridjs-td {
padding: 12px 16px;
border: 1px solid #e0e0e0;
text-align: left;
vertical-align: middle;
}
.qs-table-wrapper .gridjs-th {
background-color: #f5f5f5;
font-weight: 600;
color: #1b1b1b;
}
.qs-table-wrapper .gridjs-tr:hover {
background-color: #f9f9f9;
}
.qs-table-wrapper .gridjs-pagination {
margin-top: 16px;
justify-content: center;
}
@media (max-width: 768px) {
.qs-header h2 {
font-size: 20px;
}
.qs-wrapper {
padding: 20px 10px;
}
.qs-table-wrapper {
padding: 15px;
}
.qs-table-wrapper .gridjs-th,
.qs-table-wrapper .gridjs-td {
padding: 10px 12px;
font-size: 13px;
}
}

View File

@@ -21,8 +21,8 @@ class="authentication-bg"
<img src="/images/dputr-kab-bandung.png" height="auto" width="100%" alt="logo light">
</a>
</div>
<h4 class="fw-bold text-dark mb-2">Welcome Back!</h4>
<p class="text-muted">Sign in to your account to continue</p>
<h4 class="fw-bold text-dark mb-2">Selamat Datang!</h4>
<p class="text-muted">Masuk kedalam akun untuk melihat lebih lanjut</p>
</div>
<form method="POST" action="{{ route('login') }}" class="mt-4">
@@ -35,7 +35,7 @@ class="authentication-bg"
@endif
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<label for="email" class="form-label">Email </label>
<input type="email" class="form-control" id="email" name="email" placeholder="Enter your email">
</div>
<div class="mb-3">
@@ -45,6 +45,11 @@ class="authentication-bg"
<div class="d-grid">
<button class="btn btn-dark btn-lg fw-medium" type="submit">Sign In</button>
</div>
<div class="d-flex justify-content-start mt-3">
<a href="{{ route('search') }}" class="">
Pencarian cepat
</a>
</div>
</form>
</div>
</div>

View File

@@ -1,8 +1,11 @@
@props(['document_url' => '#'])
@section('css')
@vite(['resources/scss/components/_circle.scss'])
@endsection
<div class="circle-container" id="{{$document_id}}" style="--circle-color: {{$document_color}};{{$style}}">
<div class="circle-container" id="{{$document_id}}" style="--circle-color: {{$document_color}};{{$style}}" data-url="{{ $document_url }}"
onclick="handleCircleClick(this)">
<div class="circle-content">
<p class="document-title {{$document_id}}" >{{$document_title}}</p>
<p class="document-total {{$document_id}}" >Rp.0</p>
@@ -17,3 +20,12 @@
</div>
@endif
</div>
<script>
function handleCircleClick(element) {
const url = element.getAttribute('data-url') || '#';
if (url !== '#') {
window.location.href = url;
}
}
</script>

View File

@@ -9,22 +9,6 @@
@include('layouts.partials/page-title', ['title' => 'Dashboards', 'subtitle' => 'Dashboard Pimpinan'])
<div id="dashboard-fixed-wrapper" class="row">
<!-- <div class="col-12">
<h2 class="mt-3 ms-2 text-danger">
<span class="float-end fs-6 me-3 text-black d-block d-sm-inline text-end">Terakhir di update - {{$latest_created}}</span>
ANALISA BIG DATA PROSES PBG <br>
MELALUI APLIKASI SIBEDAS PBG
</h2>
</div>
<div class="row d-flex justify-content-end">
<div class="col-12 col-sm-6 col-md-3">
<div class="d-flex flex-sm-nowrap flex-wrap justify-content-end">
<input type="text" class="form-control" style="max-width: 125px;" id="datepicker-dashboard-bigdata" placeholder="Filter Date" />
</div>
</div>
</div> -->
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
<h2 class="text-danger m-0">

View File

@@ -0,0 +1,229 @@
@extends('layouts.base', ['subtitle' => 'Quick Search'])
@section('css')
@vite(['resources/scss/pages/quick-search/detail.scss'])
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
<div class="container qs-detail-container pt-3">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Detail Informasi Permohonan PBG</h5>
<a href="javascript:history.back()" class="btn btn-primary">Back</a>
</div>
<div class="card-body">
<div class="row gy-3 gx-4">
<div class="col-md-6">
<dl class="row mb-0">
<dt class="col-sm-5">Nama Pemohon</dt>
<dd class="col-sm-7">{{ $data->name ?? '-' }}</dd>
<dt class="col-sm-5">Nama Pemilik</dt>
<dd class="col-sm-7">{{ $data->owner_name ?? '-' }}</dd>
<dt class="col-sm-5">Jenis Permohonan</dt>
<dd class="col-sm-7">{{ isset($data->application_type) ? $applicationTypes[$data->application_type] : '-' }}</dd>
<dt class="col-sm-5">Kondisi</dt>
<dd class="col-sm-7">{{ $data->condition ?? '-' }}</dd>
<dt class="col-sm-5">Nomor Registrasi</dt>
<dd class="col-sm-7">{{ $data->registration_number ?? '-'}}</dd>
<dt class="col-sm-5">Nomor Dokumen</dt>
<dd class="col-sm-7">{{ $data->document_number ?? '-' }}</dd>
<dt class="col-sm-5">Status</dt>
<dd class="col-sm-7">{{ isset($data->status) ? $statusOptions[$data->status] : '-' }}</dd>
</dl>
</div>
<div class="col-md-6">
<dl class="row mb-0">
<dt class="col-sm-5">Alamat</dt>
<dd class="col-sm-7">{{ $data->address ?? '-' }}</dd>
<dt class="col-sm-5">Status SLF</dt>
<dd class="col-sm-7">{{ $data->slf_status_name ?? '-' }}</dd>
<dt class="col-sm-5">Fungsi Bangunan</dt>
<dd class="col-sm-7">{{ $data->function_type ?? '-' }}</dd>
<dt class="col-sm-5">Jenis Konsultasi</dt>
<dd class="col-sm-7">{{ $data->consultation_type ?? '-' }}</dd>
<dt class="col-sm-5">Jatuh Tempo</dt>
<dd class="col-sm-7">{{$data->due_date ? \Carbon\Carbon::parse($data->due_date)->format('d M Y') : '-' }}</dd>
<dt class="col-sm-5">Tanggal Dibuat</dt>
<dd class="col-sm-7">{{ \Carbon\Carbon::parse($data->task_created_at)->format('d M Y H:i') }}</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs nav-justified">
<li class="nav-item">
<a href="#pbgTaskRetributions" data-bs-toggle="tab" aria-expanded="false"
class="nav-link active">
<span class="d-sm-block">PBG Task Retributions</span>
</a>
</li>
<li class="nav-item">
<a href="#pbgTaskIntegration" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
<span class="d-sm-block">PBG Task Index Integrations</span>
</a>
</li>
<li class="nav-item">
<a href="#pbgTaskPrasarana" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
<span class="d-sm-block">PBG Task Prasarana</span>
</a>
</li>
<li class="nav-item">
<a href="#pbgTaskAssignments" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
<span class="d-sm-block">Penugasan</span>
</a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane active" id="pbgTaskRetributions">
@if ($data->pbg_task_retributions)
<div class="row">
<div class="col-md-6">
<dl class="row mb-0">
<dt class="col-sm-4">Luas Bangunan</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->luas_bangunan ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Lokalitas</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->indeks_lokalitas ?? '-'}}</dd>
<dt class="col-sm-4">Wilayah SHST</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->wilayah_shst ?? '-'}}</dd>
<dt class="col-sm-4">Nama Kegiatan</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->kegiatan_name ?? '-'}}</dd>
<dt class="col-sm-4">Nilai SHST</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->nilai_shst ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Integrasi</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->indeks_terintegrasi ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Bg Terbangun</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->indeks_bg_terbangun ?? '-'}}</dd>
</dl>
</div>
<div class="col-md-6">
<dl class="row mb-0">
<dt class="col-sm-4">Nilai Retribusi Bangunan</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->nilai_retribusi_bangunan ?? '-'}}</dd>
<dt class="col-sm-4">Nilai Prasarana</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->nilai_prasarana ?? '-'}}</dd>
<dt class="col-sm-4">PBG Dokumen</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->pbg_document ?? '-'}}</dd>
<dt class="col-sm-4">Underpayment</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->underpayment ?? '-'}}</dd>
<dt class="col-sm-4">SKRD Amount</dt>
<dd class="col-sm-8">{{$data->pbg_task_retributions->skrd_amount ?? '-'}}</dd>
</dl>
</div>
</div>
@else
<div class="alert alert-secondary" role="alert">
Data Not Available
</div>
@endif
</div>
<div class="tab-pane" id="pbgTaskIntegration">
@if ($data->pbg_task_index_integrations)
<dl class="row">
<dt class="col-sm-4">Indeks Fungsi Bangunan</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->indeks_fungsi_bangunan ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Parameter Kompleksitas</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->indeks_parameter_kompleksitas ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Parameter Permanensi</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->indeks_parameter_permanensi ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Parameter Ketinggian</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->indeks_parameter_ketinggian ?? '-'}}</dd>
<dt class="col-sm-4">Faktor Kepemilikan</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->faktor_kepemilikan ?? '-'}}</dd>
<dt class="col-sm-4">Indeks Terintegrasi</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->indeks_terintegrasi ?? '-'}}</dd>
<dt class="col-sm-4">Total</dt>
<dd class="col-sm-8">{{$data->pbg_task_index_integrations->total ?? '-'}}</dd>
</dl>
@else
<div class="alert alert-secondary" role="alert">
Data Not Available
</div>
@endif
</div>
<div class="tab-pane" id="pbgTaskPrasarana">
<div class="row d-flex flex-warp gap-3 justify-content-center">
@if ($data->pbg_task_retributions && $data->pbg_task_retributions->pbg_task_prasarana)
@foreach ($data->pbg_task_retributions->pbg_task_prasarana as $prasarana)
<div class="border p-3 rounded shadow-sm col-md-4">
<dl class="row">
<dt class="col-sm-4">Prasarana Type</dt>
<dd class="col-sm-8">{{$prasarana->prasarana_type ?? '-'}}</dd>
<dt class="col-sm-4">Building Type</dt>
<dd class="col-sm-8">{{$prasarana->building_type ?? '-'}}</dd>
<dt class="col-sm-4">Total</dt>
<dd class="col-sm-8">{{$prasarana->total ?? '-'}}</dd>
<dt class="col-sm-4">Quantity</dt>
<dd class="col-sm-8">{{$prasarana->quantity ?? '-'}}</dd>
<dt class="col-sm-4">Unit</dt>
<dd class="col-sm-8">{{$prasarana->unit ?? '-'}}</dd>
<dt class="col-sm-4">Index Prasarana</dt>
<dd class="col-sm-8">{{$prasarana->index_prasarana ?? '-'}}</dd>
<dt class="col-sm-4">Created At</dt>
<dd class="col-sm-8">{{$prasarana->created_at ? \Carbon\Carbon::parse($prasarana->created_at)->format('d M Y') : '-'}}</dd>
</dl>
</div>
@endforeach
@else
<div class="alert alert-secondary" role="alert">
Data Not Available
</div>
@endif
</div>
</div>
<div class="tab-pane" id="pbgTaskAssignments">
<input type="hidden" id="url_task_assignments" value="{{ route('api.quick-search-task-assignments', ['uuid' => $data->uuid]) }}" />
<div id="table-pbg-task-assignments"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/quick-search/detail.js'])
@endsection

View File

@@ -0,0 +1,35 @@
@extends('layouts.base', ['subtitle' => 'Quick Search'])
@section('css')
@vite(['resources/scss/pages/quick-search/index.scss'])
@endsection
@section('body-attribuet')
class="gsp-body"
@endsection
@section('content')
<div class="position-absolute top-0 end-0 p-3">
<a href="{{ route('login') }}" class="btn btn-md btn-secondary">
Login
</a>
</div>
<div class="container min-vh-100 d-flex justify-content-center align-items-center gsp-body">
<div class="w-100" style="max-width: 700px;">
<div class="text-center mb-4">
<img src="{{ asset('images/simbg-dputr.png') }}" alt="PBG Icon" class="img-fluid gsp-icon mb-3">
<h1 class="gsp-title">SIBEDAS PBG</h1>
</div>
<div class="d-flex flex-column flex-sm-row align-items-stretch gap-2">
<div class="flex-fill">
<input type="text" class="gsp-input" id="searchInput" placeholder="Cari..." autocomplete="off" />
</div>
<button class="gsp-btn" id="searchBtn">Cari</button>
</div>
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/quick-search/index.js'])
@endsection

View File

@@ -0,0 +1,44 @@
@extends('layouts.base', ['subtitle' => 'Quick Search'])
@section('css')
@vite(['resources/scss/pages/quick-search/result.scss'])
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
<input type="hidden" value="{{ route('quick-search-datatable', ['search' => $keyword]) }}" id="base_url_datatable" />
<div class="container qs-wrapper">
<div class="qs-toolbar d-flex justify-content-between align-items-center pt-4 pb-4">
<!-- Back Button -->
<a href="{{ route('search') }}" class="btn btn-light border me-3">
Kembali
</a>
<!-- Search Area (no form action) -->
<div class="qs-search-form d-flex align-items-center">
<input
type="text"
id="search_input"
class="gsp-input me-2"
value="{{ $keyword }}"
placeholder="Cari data..."
required
/>
<button type="button" id="search_button" class="gsp-btn">Cari</button>
</div>
</div>
<div class="qs-header mb-3">
<h2>Hasil Pencarian: <em>{{ $keyword }}</em></h2>
<p>Berikut adalah data hasil pencarian berdasarkan kata kunci yang Anda masukkan.</p>
</div>
<div class="qs-table-wrapper">
<div class="p-3" id="datatable-quick-search-result"></div>
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/quick-search/result.js'])
@endsection

View File

@@ -15,6 +15,7 @@ use App\Http\Controllers\Master\UsersController;
use App\Http\Controllers\MenusController;
use App\Http\Controllers\PaymentRecapsController;
use App\Http\Controllers\PbgTaskAttachmentsController;
use App\Http\Controllers\QuickSearchController;
use App\Http\Controllers\ReportPaymentRecapsController;
use App\Http\Controllers\ReportPbgPTSPController;
use App\Http\Controllers\RequestAssignment\PbgTaskController;
@@ -34,6 +35,12 @@ use Illuminate\Support\Facades\Route;
require __DIR__ . '/auth.php';
Route::get('/search', [QuickSearchController::class, 'index'])->name('search');
Route::get('/search-result', [QuickSearchController::class, 'search_result'])->name('search-result');
Route::get('/quick-search-datatable', [QuickSearchController::class, 'quick_search_datatable'])->name('quick-search-datatable');
Route::get('/quick-search/{id}', [QuickSearchController::class, 'show'])->name('quick-search.detail');
Route::get('/quick-search/{uuid}/task-assignments', [QuickSearchController::class, 'task_assignments'])->name('api.quick-search-task-assignments');
// auth
Route::group(['middleware' => 'auth'], function(){

View File

@@ -20,6 +20,9 @@ export default defineConfig({
"resources/scss/components/_custom_circle.scss",
"resources/scss/dashboards/potentials/_inside_system.scss",
"resources/scss/dashboards/potentials/_outside_system.scss",
"resources/scss/pages/quick-search/detail.scss",
"resources/scss/pages/quick-search/index.scss",
"resources/scss/pages/quick-search/result.scss",
"node_modules/quill/dist/quill.snow.css",
"node_modules/quill/dist/quill.bubble.css",
@@ -109,6 +112,10 @@ export default defineConfig({
"resources/js/pbg-task/create.js",
// google-sheets
"resources/js/data/google-sheet/index.js",
// quick-search
"resources/js/quick-search/index.js",
"resources/js/quick-search/result.js",
"resources/js/quick-search/detail.js",
// dummy
"resources/js/approval/index.js",
"resources/js/invitations/index.js",