Compare commits
23 Commits
feature/da
...
fix/sync-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cc698a623 | ||
|
|
544ad1db46 | ||
|
|
30ca819aa1 | ||
|
|
b0bab784d1 | ||
|
|
01fda22c89 | ||
|
|
de300c2c32 | ||
|
|
7f8a2e4936 | ||
|
|
aa9943ba45 | ||
|
|
9e55ea0dbb | ||
|
|
93f0ac5ef7 | ||
|
|
bb4ab5c769 | ||
|
|
e743b82087 | ||
|
|
c3c7b8e3ec | ||
|
|
d49035ce8d | ||
|
|
ffd9d3514c | ||
|
|
675477c734 | ||
|
|
4350c466e3 | ||
|
|
5ab407d672 | ||
|
|
2aaa487746 | ||
|
|
080582f7ab | ||
|
|
91475aeead | ||
|
|
5d50d6d6cc | ||
|
|
39717d184c |
85
README.md
85
README.md
@@ -1,66 +1,37 @@
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
# Usage icon
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
search or pick icon in <a href="https://icon-sets.iconify.design/mingcute/?keyword=mingcute">here</a>
|
||||
|
||||
## About Laravel
|
||||
# Set up queue for running automatically
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
- Install Supervisor
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
```
|
||||
sudo apt update && sudo apt install supervisor -y
|
||||
```
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
- Create Supervisor Config
|
||||
|
||||
## Learning Laravel
|
||||
```
|
||||
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
[program:laravel-worker]
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
command=php /path-to-your-project/artisan queue:work --tries=3 --timeout=600
|
||||
autostart=true
|
||||
autorestart=true
|
||||
numprocs=1
|
||||
user=www-data
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/supervisor/laravel-worker.log
|
||||
```
|
||||
|
||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||
- Reload Supervisor
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[WebReinvent](https://webreinvent.com/)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Jump24](https://jump24.co.uk)**
|
||||
- **[Redberry](https://redberry.international/laravel/)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
- **[byte5](https://byte5.de)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
```
|
||||
sudo supervisorctl reread
|
||||
sudo supervisorctl update
|
||||
sudo supervisorctl start laravel-worker
|
||||
sudo supervisorctl restart laravel-worker
|
||||
sudo supervisorctl status
|
||||
```
|
||||
|
||||
259
app/Http/Controllers/Api/BigDataResumeController.php
Normal file
259
app/Http/Controllers/Api/BigDataResumeController.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\BigdataResumeResource;
|
||||
use App\Models\BigdataResume;
|
||||
use App\Models\DataSetting;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class BigDataResumeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
try{
|
||||
$filterDate = $request->get("filterByDate");
|
||||
|
||||
// If filterByDate is "latest" or empty, get the most recent record
|
||||
if (!$filterDate || $filterDate === "latest") {
|
||||
$big_data_resume = BigdataResume::latest()->first();
|
||||
} else {
|
||||
// Filter by specific date
|
||||
$big_data_resume = BigdataResume::whereDate('created_at', $filterDate)
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
if (!$big_data_resume) {
|
||||
return $this->response_empty_resume();
|
||||
}
|
||||
}
|
||||
|
||||
$data_settings = DataSetting::all();
|
||||
if($data_settings->isEmpty()){
|
||||
return response()->json(['message' => 'No data setting found']);
|
||||
}
|
||||
|
||||
$target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value);
|
||||
$realisasi_terbit_pbg_sum = floatval(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_SUM')->first())->value);
|
||||
$realisasi_terbit_pbg_count = floatval(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_COUNT')->first())->value);
|
||||
$menuggu_klik_dpmptsp_sum = floatval(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_SUM')->first())->value);
|
||||
$menuggu_klik_dpmptsp_count = floatval(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_COUNT')->first())->value);
|
||||
$proses_dinas_teknis_sum = floatval(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_SUM')->first())->value);
|
||||
$proses_dinas_teknis_count = floatval(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_COUNT')->first())->value);
|
||||
|
||||
$tata_ruang = $big_data_resume->spatial_sum;
|
||||
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
|
||||
|
||||
// percentage kekurangan potensi
|
||||
$kekurangan_potensi_percentage = $target_pad > 0 && $target_pad > 0
|
||||
? round(($kekurangan_potensi / $target_pad) * 100, 2) : 0;
|
||||
|
||||
// percentage total potensi
|
||||
$total_potensi_percentage = $big_data_resume->potention_sum > 0 && $target_pad > 0
|
||||
? round(($big_data_resume->potention_sum / $target_pad) * 100, 2) : 0;
|
||||
|
||||
// percentage verified document
|
||||
$verified_percentage = $big_data_resume->verified_sum > 0 && $big_data_resume->potention_sum > 0
|
||||
? round(($big_data_resume->verified_sum / $big_data_resume->potention_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage non-verified document
|
||||
$non_verified_percentage = $big_data_resume->non_verified_sum > 0 && $big_data_resume->potention_sum > 0
|
||||
? round(($big_data_resume->non_verified_sum / $big_data_resume->potention_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage business document
|
||||
$business_percentage = $big_data_resume->business_sum > 0 && $big_data_resume->non_verified_sum > 0
|
||||
? round(($big_data_resume->business_sum / $big_data_resume->non_verified_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage non-business document
|
||||
$non_business_percentage = $big_data_resume->non_business_sum > 0 && $big_data_resume->potention_sum > 0
|
||||
? round(($big_data_resume->non_business_sum / $big_data_resume->potention_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage tata ruang
|
||||
$tata_ruang_percentage = $tata_ruang > 0 && $big_data_resume->potention_sum > 0
|
||||
? round(($tata_ruang / $big_data_resume->potention_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage realisasi terbit pbg
|
||||
$realisasi_terbit_percentage = $big_data_resume->verified_sum > 0 && $realisasi_terbit_pbg_sum > 0
|
||||
? round(($realisasi_terbit_pbg_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage menunggu klik dpmptsp
|
||||
$menunggu_klik_dpmptsp_percentage = $big_data_resume->verified_sum > 0 && $menuggu_klik_dpmptsp_sum > 0
|
||||
? round(($menuggu_klik_dpmptsp_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
||||
|
||||
// percentage proses_dinas_teknis
|
||||
$proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum > 0
|
||||
? round(($proses_dinas_teknis_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
||||
|
||||
$result = [
|
||||
'target_pad' => [
|
||||
'sum' => $target_pad,
|
||||
'percentage' => 100,
|
||||
],
|
||||
'tata_ruang' => [
|
||||
'sum' => $big_data_resume->spatial_sum,
|
||||
'count' => $big_data_resume->spatial_count,
|
||||
'percentage' => $tata_ruang_percentage,
|
||||
],
|
||||
'kekurangan_potensi' => [
|
||||
'sum' => $kekurangan_potensi,
|
||||
'percentage' => $kekurangan_potensi_percentage
|
||||
],
|
||||
'total_potensi' => [
|
||||
'sum' => (float) $big_data_resume->potention_sum,
|
||||
'count' => $big_data_resume->potention_count,
|
||||
'percentage' => $total_potensi_percentage
|
||||
],
|
||||
'verified_document' => [
|
||||
'sum' => (float) $big_data_resume->verified_sum,
|
||||
'count' => $big_data_resume->verified_count,
|
||||
'percentage' => $verified_percentage
|
||||
],
|
||||
'non_verified_document' => [
|
||||
'sum' => (float) $big_data_resume->non_verified_sum,
|
||||
'count' => $big_data_resume->non_verified_count,
|
||||
'percentage' => $non_verified_percentage
|
||||
],
|
||||
'business_document' => [
|
||||
'sum' => (float) $big_data_resume->business_sum,
|
||||
'count' => $big_data_resume->business_count,
|
||||
'percentage' => $business_percentage
|
||||
],
|
||||
'non_business_document' => [
|
||||
'sum' => (float) $big_data_resume->non_business_sum,
|
||||
'count' => $big_data_resume->non_business_count,
|
||||
'percentage' => $non_business_percentage
|
||||
],
|
||||
'realisasi_terbit' => [
|
||||
'sum' => $realisasi_terbit_pbg_sum,
|
||||
'count' => $realisasi_terbit_pbg_count,
|
||||
'percentage' => $realisasi_terbit_percentage
|
||||
],
|
||||
'menunggu_klik_dpmptsp' => [
|
||||
'sum' => $menuggu_klik_dpmptsp_sum,
|
||||
'count' => $menuggu_klik_dpmptsp_count,
|
||||
'percentage' => $menunggu_klik_dpmptsp_percentage
|
||||
],
|
||||
'proses_dinas_teknis' => [
|
||||
'sum' => $proses_dinas_teknis_sum,
|
||||
'count' => $proses_dinas_teknis_count,
|
||||
'percentage' => $proses_dinas_teknis_percentage
|
||||
]
|
||||
];
|
||||
return response()->json($result);
|
||||
}catch(\Exception $e){
|
||||
return response()->json(['message' => 'Error when fetching data'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function bigdata_report(Request $request){
|
||||
try{
|
||||
$query = BigdataResume::query()->orderBy('id', 'desc');
|
||||
|
||||
if($request->filled('search')){
|
||||
$query->where('name', 'LIKE', '%'.$request->input('search').'%');
|
||||
}
|
||||
|
||||
$query = $query->paginate(15);
|
||||
return BigdataResumeResource::collection($query)->response()->getData(true);
|
||||
}catch(\Exception $e){
|
||||
Log::error($e->getMessage());
|
||||
return response()->json(['message' => 'Error when fetching data'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
private function response_empty_resume(){
|
||||
$result = [
|
||||
'target_pad' => [
|
||||
'sum' => 0,
|
||||
'percentage' => 100,
|
||||
],
|
||||
'tata_ruang' => [
|
||||
'sum' => 0,
|
||||
'percentage' => 0,
|
||||
],
|
||||
'kekurangan_potensi' => [
|
||||
'sum' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'total_potensi' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'verified_document' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'non_verified_document' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'business_document' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'non_business_document' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'realisasi_terbit' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'menunggu_klik_dpmptsp' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
],
|
||||
'proses_dinas_teknis' => [
|
||||
'sum' => 0,
|
||||
'count' => 0,
|
||||
'percentage' => 0
|
||||
]
|
||||
];
|
||||
|
||||
return response()->json($result);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use App\Models\BusinessOrIndustry;
|
||||
use Illuminate\Http\Request;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use \Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\ExcelUploadRequest;
|
||||
class BusinessOrIndustriesController extends Controller
|
||||
{
|
||||
/**
|
||||
@@ -79,29 +80,15 @@ class BusinessOrIndustriesController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function upload(Request $request){
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
$file = $request->file('file');
|
||||
}
|
||||
|
||||
// Validasi file
|
||||
$validator = Validator::make($request->all(), [
|
||||
'file' => 'required|mimes:xlsx,xls|max:102400', // Max 100MB
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'File validation failed.',
|
||||
'errors' => $validator->errors()
|
||||
], 400);
|
||||
}
|
||||
|
||||
public function upload(ExcelUploadRequest $request){
|
||||
try {
|
||||
// Ambil file dari request
|
||||
$file = $request->file('file');
|
||||
if(!$request->hasFile('file')){
|
||||
return response()->json([
|
||||
'error' => 'No file provided'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Menggunakan Laravel Excel untuk mengimpor file
|
||||
$file = $request->file('file');
|
||||
Excel::import(new BusinessIndustriesImport, $file);
|
||||
|
||||
// Jika sukses, kembalikan respons sukses
|
||||
|
||||
@@ -22,7 +22,7 @@ class CustomersController extends Controller
|
||||
if ($request->has('search') &&!empty($request->get('search'))) {
|
||||
$query = $query->where('nomor_pelanggan', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('nama', 'LIKE', '%'.$request->get('search').'%')
|
||||
->orWhere('kota_palayanan', 'LIKE', '%'.$request->get('search').'%');
|
||||
->orWhere('kota_pelayanan', 'LIKE', '%'.$request->get('search').'%');
|
||||
}
|
||||
return CustomersResource::collection($query->paginate());
|
||||
}
|
||||
|
||||
31
app/Http/Controllers/Api/LackOfPotentialController.php
Normal file
31
app/Http/Controllers/Api/LackOfPotentialController.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Advertisement;
|
||||
use App\Models\Customer;
|
||||
use App\Models\SpatialPlanning;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LackOfPotentialController extends Controller
|
||||
{
|
||||
|
||||
public function count_lack_of_potential(){
|
||||
try{
|
||||
$total_reklame = Advertisement::count();
|
||||
$total_pdam = Customer::count();
|
||||
$total_tata_ruang = SpatialPlanning::count();
|
||||
|
||||
return response()->json([
|
||||
'total_reklame' => $total_reklame,
|
||||
'total_pdam' => $total_pdam,
|
||||
'total_tata_ruang' => $total_tata_ruang
|
||||
], 200);
|
||||
}catch(\Exception $e){
|
||||
return response()->json([
|
||||
'message' => 'Error: '.$e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\MenuRequest;
|
||||
use App\Http\Resources\MenuResource;
|
||||
use App\Models\Menu;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class MenusController extends Controller
|
||||
{
|
||||
@@ -13,7 +16,7 @@ class MenusController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Menu::query();
|
||||
$query = Menu::query()->orderBy('id', 'desc');
|
||||
|
||||
if($request->has("search") && !empty($request->get("search"))){
|
||||
$query = $query->where("name", "like", "%".$request->get("search")."%");
|
||||
@@ -25,9 +28,15 @@ class MenusController extends Controller
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(MenuRequest $request)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$menu = Menu::create($request->validated());
|
||||
return response()->json(['message' => 'Menu created successfully', 'data' => new MenuResource($menu)]);
|
||||
}catch(\Exception $e){
|
||||
Log::error($e);
|
||||
return response()->json(['message' => 'Error when creating menu'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,15 +44,37 @@ class MenusController extends Controller
|
||||
*/
|
||||
public function show(string $id)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$menu = Menu::find($id);
|
||||
if($menu){
|
||||
return response()->json(['message' => 'Menu found', 'data' => new MenuResource($menu)]);
|
||||
} else {
|
||||
return response()->json(['message' => 'Menu not found'], 404);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
Log::error($e);
|
||||
Log::error($e->getMessage());
|
||||
return response()->json(['message' => 'Error when finding menu'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, string $id)
|
||||
public function update(MenuRequest $request, string $id)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$menu = Menu::findOrFail($id);
|
||||
if($menu){
|
||||
$menu->update($request->validated());
|
||||
return response()->json(['message' => 'Menu updated successfully', 'data' => new MenuResource($menu)]);
|
||||
} else {
|
||||
return response()->json(['message' => 'Menu not found'], 404);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
Log::error($e);
|
||||
return response()->json(['message' => 'Error when updating menu'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +82,28 @@ class MenusController extends Controller
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$menu = Menu::findOrFail($id);
|
||||
if($menu){
|
||||
$this->deleteChildren($menu);
|
||||
$menu->roles()->detach();
|
||||
$menu->delete();
|
||||
return response()->json(['message' => 'Menu deleted successfully']);
|
||||
} else {
|
||||
return response()->json(['message' => 'Menu not found'], 404);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
Log::error($e);
|
||||
return response()->json(['message' => 'Error when deleting menu'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteChildren($menu)
|
||||
{
|
||||
foreach ($menu->children as $child) {
|
||||
$this->deleteChildren($child); // Recursively delete its children
|
||||
$child->roles()->detach(); // Detach roles before deleting
|
||||
$child->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\RoleRequest;
|
||||
use App\Models\Role;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -13,7 +14,7 @@ class RolesController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Role::query();
|
||||
$query = Role::query()->orderBy('id', 'desc');
|
||||
|
||||
if($request->has('search') && !empty($request->get('search'))){
|
||||
$query = $query->where('name', 'like', '%'. $request->get('search') . '%');
|
||||
@@ -25,9 +26,14 @@ class RolesController extends Controller
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(RoleRequest $request)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$role = Role::create($request->validated());
|
||||
return response()->json(['message' => 'Successfully created', 'data' => $role]);
|
||||
}catch(\Exception $e){
|
||||
return response()->json(['message' => 'Error when creating role', 'error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,15 +41,34 @@ class RolesController extends Controller
|
||||
*/
|
||||
public function show(string $id)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$role = Role::find($id);
|
||||
if($role){
|
||||
return response()->json(['data' => $role]);
|
||||
} else {
|
||||
return response()->json(['message' => 'Role not found'], 404);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
return response()->json(['message' => 'Error when getting role', 'error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, string $id)
|
||||
public function update(RoleRequest $request, string $id)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$role = Role::find($id);
|
||||
if($role){
|
||||
$role->update($request->validated());
|
||||
return response()->json(['message' => 'Successfully updated', 'data' => $role]);
|
||||
} else {
|
||||
return response()->json(['message' => 'Role not found'], 404);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
return response()->json(['message' => 'Error when updating role', 'error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +76,16 @@ class RolesController extends Controller
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
//
|
||||
try{
|
||||
$role = Role::find($id);
|
||||
if($role){
|
||||
$role->delete();
|
||||
return response()->json(['message' => 'Successfully deleted']);
|
||||
} else {
|
||||
return response()->json(['message' => 'Role not found'], 404);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
return response()->json(['message' => 'Error when deleting role', 'error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
app/Http/Controllers/BigdataResumesController.php
Normal file
48
app/Http/Controllers/BigdataResumesController.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BigdataResumesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('bigdata-resumes.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
12
app/Http/Controllers/GoogleApisController.php
Normal file
12
app/Http/Controllers/GoogleApisController.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class GoogleApisController extends Controller
|
||||
{
|
||||
public function index(){
|
||||
return view('maps.google-api');
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class DataSettingRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$id = $this->route('data_setting');
|
||||
$id = $this->route('data_setting_id');
|
||||
return [
|
||||
"key" => "required|unique:data_settings,key," . $id,
|
||||
"value" => "required",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class MenuRequest extends FormRequest
|
||||
{
|
||||
@@ -21,12 +22,14 @@ class MenuRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$menuId = $this->route('menu_id'); // Get the menu ID if updating
|
||||
|
||||
return [
|
||||
'name' => ['required','string','max:255'],
|
||||
'url' => ['nullable','string','max:255'],
|
||||
'icon' => ['nullable','string','max:255'],
|
||||
'parent_id' => ['nullable','exists:menus,id'],
|
||||
'sort_order' => ['required','integer'],
|
||||
'name' => ['required', 'string', 'max:255', Rule::unique('menus', 'name')->ignore($menuId)],
|
||||
'url' => ['nullable', 'string', 'max:255'],
|
||||
'icon' => ['nullable', 'string', 'max:255'],
|
||||
'parent_id' => ['nullable', 'exists:menus,id'],
|
||||
'sort_order' => ['required', 'integer'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class RoleRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$roleId = $this->route('role');
|
||||
$roleId = $this->route('role_id');
|
||||
return [
|
||||
'name' => 'required|string|max:255|unique:roles,name,' . ($roleId ?? 'NULL') . ',id',
|
||||
'description' => 'nullable|string',
|
||||
|
||||
42
app/Http/Resources/BigdataResumeResource.php
Normal file
42
app/Http/Resources/BigdataResumeResource.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BigdataResumeResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'import_datasource_id' => $this->import_datasource_id,
|
||||
'potention_count' => (int) $this->potention_count,
|
||||
'potention_sum' => number_format((float) $this->potention_sum, 2, ',', '.'),
|
||||
|
||||
'non_verified_count' => (int) $this->non_verified_count,
|
||||
'non_verified_sum' => number_format((float) $this->non_verified_sum, 2, ',', '.'),
|
||||
|
||||
'verified_count' => (int) $this->verified_count,
|
||||
'verified_sum' => number_format((float) $this->verified_sum, 2, ',', '.'),
|
||||
|
||||
'business_count' => (int) $this->business_count,
|
||||
'business_sum' => number_format((float) $this->business_sum, 2, ',', '.'),
|
||||
|
||||
'non_business_count' => (int) $this->non_business_count,
|
||||
'non_business_sum' => number_format((float) $this->non_business_sum, 2, ',', '.'),
|
||||
|
||||
'spatial_count' => (int) $this->spatial_count,
|
||||
'spatial_sum' => number_format((float) $this->spatial_sum, 2, ',', '.'),
|
||||
|
||||
'year' => $this->year,
|
||||
'created_at' => $this->created_at->toDateTimeString(),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Http/Resources/MenuResource.php
Normal file
19
app/Http/Resources/MenuResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class MenuResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
||||
@@ -3,37 +3,105 @@
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Models\BusinessOrIndustry;
|
||||
use Maatwebsite\Excel\Concerns\ToModel;
|
||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithChunkReading;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class BusinessIndustriesImport implements ToCollection
|
||||
class BusinessIndustriesImport implements ToCollection, WithMultipleSheets, WithChunkReading, WithBatchInserts, ShouldQueue, WithHeadingRow
|
||||
{
|
||||
/**
|
||||
* @param array $row
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
* @param Collection $collection
|
||||
*/
|
||||
public function collection(Collection $rows)
|
||||
public function collection(Collection $collection)
|
||||
{
|
||||
foreach ($rows->skip(1) as $row){
|
||||
$clean_nop = preg_replace('/[^A-Za-z0-9]/', '', $row[2]);
|
||||
if (!BusinessOrIndustry::where('nop', $clean_nop)->exists()) {
|
||||
BusinessOrIndustry::create([
|
||||
'nama_kecamatan' => $row[0],
|
||||
'nama_kelurahan' => $row[1],
|
||||
'nop' => $clean_nop, // Store cleaned 'nop'
|
||||
'nama_wajib_pajak' => $row[3],
|
||||
'alamat_wajib_pajak' => $row[4],
|
||||
'alamat_objek_pajak' => $row[5],
|
||||
'luas_bumi' => $row[6],
|
||||
'luas_bangunan' => $row[7],
|
||||
'njop_bumi' => $row[8],
|
||||
'njop_bangunan' => $row[9],
|
||||
'ketetapan' => $row[10],
|
||||
'tahun_pajak' => $row[11],
|
||||
try{
|
||||
$batchData = [];
|
||||
$batchSize = 1000;
|
||||
|
||||
foreach ($collection as $row){
|
||||
if(!isset($row['nop']) || empty($row['nop'])){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$clean_nop = preg_replace('/[^A-Za-z0-9]/', '', $row['nop']);
|
||||
|
||||
$batchData[] = [
|
||||
'nama_kecamatan' => $row['nama_kecamatan'],
|
||||
'nama_kelurahan' => $row['nama_kelurahan'],
|
||||
'nop' => $clean_nop,
|
||||
'nama_wajib_pajak' => $row['nama_wajib_pajak'],
|
||||
'alamat_wajib_pajak' => $row['alamat_wajib_pajak'],
|
||||
'alamat_objek_pajak' => $row['alamat_objek_pajak'],
|
||||
'luas_bumi' => $row['luas_bumi'],
|
||||
'luas_bangunan' => $row['luas_bangunan'],
|
||||
'njop_bumi' => $row['njop_bumi'],
|
||||
'njop_bangunan' => $row['njop_bangunan'],
|
||||
'ketetapan' => $row['ketetapan'],
|
||||
'tahun_pajak' => $row['tahun_pajak'],
|
||||
];
|
||||
|
||||
if(count($batchData) >= $batchSize){
|
||||
BusinessOrIndustry::upsert($batchData, ['nop'], [
|
||||
'nama_kecamatan',
|
||||
'nama_kelurahan',
|
||||
'nama_wajib_pajak',
|
||||
'alamat_wajib_pajak',
|
||||
'alamat_objek_pajak',
|
||||
'luas_bumi',
|
||||
'luas_bangunan',
|
||||
'njop_bumi',
|
||||
'njop_bangunan',
|
||||
'ketetapan',
|
||||
'tahun_pajak',
|
||||
]);
|
||||
$batchData = [];
|
||||
}
|
||||
}
|
||||
if(!empty($batchData)){
|
||||
BusinessOrIndustry::upsert($batchData, ['nop'], [
|
||||
'nama_kecamatan',
|
||||
'nama_kelurahan',
|
||||
'nama_wajib_pajak',
|
||||
'alamat_wajib_pajak',
|
||||
'alamat_objek_pajak',
|
||||
'luas_bumi',
|
||||
'luas_bangunan',
|
||||
'njop_bumi',
|
||||
'njop_bangunan',
|
||||
'ketetapan',
|
||||
'tahun_pajak',
|
||||
]);
|
||||
}
|
||||
}catch(\Exception $exception){
|
||||
Log::error('Error while importing Business Industries data:', ['error' => $exception->getMessage()]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function sheets(): array {
|
||||
return [
|
||||
0 => $this
|
||||
];
|
||||
}
|
||||
|
||||
public function headingRow(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function chunkSize(): int
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
|
||||
public function batchSize(): int
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CustomersImport implements ToCollection, WithMultipleSheets
|
||||
class CustomersImport implements ToCollection, WithMultipleSheets, WithChunkReading, WithBatchInserts, ShouldQueue, WithHeadingRow
|
||||
{
|
||||
/**
|
||||
* @param Collection $collection
|
||||
@@ -20,23 +21,51 @@ class CustomersImport implements ToCollection, WithMultipleSheets
|
||||
public function collection(Collection $collection)
|
||||
{
|
||||
$batchData = [];
|
||||
$batchSize = 1000;
|
||||
|
||||
foreach ($collection->skip(1) as $row) {
|
||||
if (!isset($row[0]) || empty($row[0])) {
|
||||
foreach ($collection as $row) {
|
||||
if (!isset($row['nomor_pelanggan']) || empty($row['nomor_pelanggan'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$latitude = filter_var($row[4], FILTER_VALIDATE_FLOAT) ? bcadd($row[4], '0', 18) : null;
|
||||
$longitude = filter_var($row[5], FILTER_VALIDATE_FLOAT) ? bcadd($row[5], '0', 18) : null;
|
||||
$latitude = '0';
|
||||
$longitude = '0';
|
||||
|
||||
if (isset($row['latkor']) && !empty(trim($row['latkor']))) {
|
||||
$latitude = str_replace(',', '.', trim($row['latkor']));
|
||||
if (is_numeric($latitude)) {
|
||||
$latitude = bcadd($latitude, '0', 18);
|
||||
} else {
|
||||
$latitude = '0';
|
||||
}
|
||||
} else {
|
||||
$latitude = '0';
|
||||
}
|
||||
|
||||
if (isset($row['lonkor']) && !empty(trim($row['lonkor']))) {
|
||||
$longitude = str_replace(',', '.', trim($row['lonkor']));
|
||||
if (is_numeric($longitude)) {
|
||||
$longitude = bcadd($longitude, '0', 18);
|
||||
} else {
|
||||
$longitude = '0';
|
||||
}
|
||||
} else {
|
||||
$longitude = '0';
|
||||
}
|
||||
|
||||
$batchData[] = [
|
||||
'nomor_pelanggan' => $row[0],
|
||||
'kota_pelayanan' => $row[1],
|
||||
'nama' => $row[2],
|
||||
'alamat' => $row[3],
|
||||
'nomor_pelanggan' => $row['nomor_pelanggan'] ?? '',
|
||||
'kota_pelayanan' => $row['kota_pelayanan'] ?? '',
|
||||
'nama' => $row['nama'] ?? '',
|
||||
'alamat' => $row['alamat'] ?? '',
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
];
|
||||
|
||||
if (count($batchData) >= $batchSize) {
|
||||
Customer::upsert($batchData, ['nomor_pelanggan'], ['kota_pelayanan', 'nama', 'alamat', 'latitude', 'longitude']);
|
||||
$batchData = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($batchData)) {
|
||||
@@ -44,9 +73,20 @@ class CustomersImport implements ToCollection, WithMultipleSheets
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function sheets(): array {
|
||||
return [
|
||||
0 => $this
|
||||
];
|
||||
}
|
||||
|
||||
public function chunkSize(): int
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
|
||||
public function batchSize(): int
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ class BigdataResume extends Model
|
||||
'business_sum',
|
||||
'non_business_count',
|
||||
'non_business_sum',
|
||||
'spatial_count',
|
||||
'spatial_sum',
|
||||
'year'
|
||||
];
|
||||
|
||||
public function importDatasource()
|
||||
@@ -28,88 +31,78 @@ class BigdataResume extends Model
|
||||
return $this->belongsTo(ImportDatasource::class, 'import_datasource_id');
|
||||
}
|
||||
|
||||
public static function generateResumeData($import_datasource_id){
|
||||
$query_verified = once( function () {
|
||||
return DB::table('pbg_task AS pt')
|
||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid')
|
||||
->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) = ?', [strtolower(trim('Selesai Verifikasi'))])
|
||||
->selectRaw('COUNT(pt.id) AS total_data,
|
||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution')
|
||||
->first();
|
||||
});
|
||||
|
||||
$verified_count = $query_verified->total_data ?? 0;
|
||||
$verified_total = $query_verified->total_retribution ?? 0;
|
||||
|
||||
$query_business = once(function () {
|
||||
return DB::table('pbg_task AS pt')
|
||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid')
|
||||
->where(function ($query) {
|
||||
$query->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) != ?', [strtolower(trim('Selesai Verifikasi'))])
|
||||
->orWhereNull('ptgs.status_verifikasi');
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereRaw('LOWER(TRIM(pt.function_type)) = ?', [strtolower(trim('Sebagai Tempat Usaha'))]);
|
||||
})
|
||||
->selectRaw('COUNT(pt.id) AS total_data,
|
||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution')
|
||||
->first();
|
||||
});
|
||||
|
||||
$business_count = $query_business->total_data ?? 0;
|
||||
$business_total = $query_business->total_retribution ?? 0;
|
||||
|
||||
$query_non_business = once( function () {
|
||||
return DB::table('pbg_task AS pt')
|
||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid') // Join ke pbg_task_retributions
|
||||
->where(function ($query) {
|
||||
$query->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) != ?', [strtolower(trim('Selesai Verifikasi'))])
|
||||
->orWhereNull('ptgs.status_verifikasi'); // Include NULL values
|
||||
public static function generateResumeData($import_datasource_id, $year){
|
||||
$stats = PbgTask::with(['googleSheet', 'pbg_task_retributions'])
|
||||
->leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid')
|
||||
->leftJoin('pbg_task_google_sheet as ptgs', 'pbg_task.registration_number', '=', 'ptgs.no_registrasi')
|
||||
->when($year !== 'all', function ($query) use ($year) {
|
||||
$query->whereYear('pbg_task.task_created_at', (int) $year);
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereRaw('LOWER(TRIM(pt.function_type)) != ?', [strtolower(trim('Sebagai Tempat Usaha'))])
|
||||
->orWhereNull('pt.function_type'); // Include NULL values
|
||||
})
|
||||
->selectRaw('COUNT(pt.id) AS total_data,
|
||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution') // Menambahkan SUM dari pbg_task_retributions
|
||||
->first();
|
||||
});
|
||||
$non_business_count = $query_non_business->total_data ?? 0;
|
||||
$non_business_total = $query_non_business->total_retribution ?? 0;
|
||||
->selectRaw("
|
||||
COUNT(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) = 'selesai verifikasi' THEN 1 END) AS verified_count,
|
||||
SUM(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) = 'selesai verifikasi' THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS verified_total,
|
||||
|
||||
$query_non_verified = once(function () {
|
||||
return DB::table('pbg_task AS pt')
|
||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid') // Join tabel pbg_task_retributions
|
||||
->where(function ($query) {
|
||||
$query->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) != ?', [strtolower(trim('Selesai Verifikasi'))])
|
||||
->orWhereNull('ptgs.status_verifikasi'); // Include NULL values
|
||||
})
|
||||
->selectRaw('COUNT(pt.id) AS total_data,
|
||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution') // Menambahkan SUM dari pbg_task_retributions
|
||||
COUNT(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL THEN 1 END) AS non_verified_count,
|
||||
SUM(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS non_verified_total,
|
||||
|
||||
COUNT(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||
AND LOWER(TRIM(pbg_task.function_type)) = 'sebagai tempat usaha' THEN 1 END) AS business_count,
|
||||
SUM(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||
AND LOWER(TRIM(pbg_task.function_type)) = 'sebagai tempat usaha' THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS business_total,
|
||||
|
||||
COUNT(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||
AND (LOWER(TRIM(pbg_task.function_type)) != 'sebagai tempat usaha' OR pbg_task.function_type IS NULL) THEN 1 END) AS non_business_count,
|
||||
SUM(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||
AND (LOWER(TRIM(pbg_task.function_type)) != 'sebagai tempat usaha' OR pbg_task.function_type IS NULL) THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS non_business_total
|
||||
")
|
||||
->first();
|
||||
|
||||
// Assign Results
|
||||
$verified_count = $stats->verified_count ?? 0;
|
||||
$verified_total = $stats->verified_total ?? 0;
|
||||
$non_verified_count = $stats->non_verified_count ?? 0;
|
||||
$non_verified_total = $stats->non_verified_total ?? 0;
|
||||
$business_count = $stats->business_count ?? 0;
|
||||
$business_total = $stats->business_total ?? 0;
|
||||
$non_business_count = $stats->non_business_count ?? 0;
|
||||
$non_business_total = $stats->non_business_total ?? 0;
|
||||
|
||||
$query_potention = once(function () use ($year) {
|
||||
$query = PbgTask::leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid')
|
||||
->selectRaw('COUNT(DISTINCT pbg_task.id) as task_count, SUM(ptr.nilai_retribusi_bangunan) as total_retribution');
|
||||
|
||||
if ($year !== 'all') {
|
||||
$query->whereYear('pbg_task.task_created_at', (int) $year);
|
||||
}
|
||||
|
||||
return $query->first();
|
||||
});
|
||||
|
||||
$non_verified_count = $query_non_verified->total_data ?? 0;
|
||||
$non_verified_total = $query_non_verified->total_retribution ?? 0;
|
||||
|
||||
$query_potention = once( function () {
|
||||
return DB::table('pbg_task as pt')
|
||||
->leftJoin('pbg_task_retributions as ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid')
|
||||
->select(
|
||||
DB::raw('COUNT(DISTINCT pt.id) as task_count'),
|
||||
DB::raw('SUM(ptr.nilai_retribusi_bangunan) as total_retribution')
|
||||
)
|
||||
->first();
|
||||
});
|
||||
$potention_count = $query_potention->task_count ?? 0;
|
||||
$potention_total = $query_potention->total_retribution ?? 0;
|
||||
|
||||
$query_spatial_plannings = once(function () use ($year) {
|
||||
$query = PbgTask::join('spatial_plannings as sp', 'pbg_task.document_number', '=', 'sp.number')
|
||||
->join('pbg_task_retributions as ptr', 'ptr.pbg_task_uid', '=', 'pbg_task.uuid')
|
||||
->selectRaw('COUNT(DISTINCT pbg_task.id) as task_count, SUM(ptr.nilai_retribusi_bangunan) as total_retribution');
|
||||
|
||||
if ($year !== 'all') {
|
||||
$query->whereYear('pbg_task.task_created_at', (int) $year);
|
||||
}
|
||||
|
||||
return $query->first();
|
||||
});
|
||||
|
||||
$spatial_planning_count = $query_spatial_plannings->task_count ?? 0;
|
||||
$spatial_planning_total = $query_spatial_plannings->total_retribution ?? 0;
|
||||
|
||||
$potention_count -= $spatial_planning_count;
|
||||
$potention_total -= $spatial_planning_total;
|
||||
|
||||
return self::create([
|
||||
'import_datasource_id' => $import_datasource_id,
|
||||
'spatial_count' => $spatial_planning_count,
|
||||
'spatial_sum' => $spatial_planning_total ?? 0.00,
|
||||
'potention_count' => $potention_count ?? 0,
|
||||
'potention_sum' => $potention_total ?? 0.00,
|
||||
'non_verified_count' => $non_verified_count ?? 0,
|
||||
@@ -120,6 +113,7 @@ class BigdataResume extends Model
|
||||
'business_sum' => $business_total ?? 0.00,
|
||||
'non_business_count' => $non_business_count ?? 0,
|
||||
'non_business_sum' => $non_business_total ?? 0.00,
|
||||
'year' => $year
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +37,8 @@ class PbgTask extends Model
|
||||
public function pbg_task_index_integrations(){
|
||||
return $this->hasOne(PbgTaskIndexIntegrations::class, 'pbg_task_uid', 'uuid');
|
||||
}
|
||||
|
||||
public function googleSheet(){
|
||||
return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,32 +61,37 @@ class ServiceSIMBG
|
||||
}
|
||||
}
|
||||
|
||||
public function syncIndexIntegration($uuid, $token)
|
||||
public function syncIndexIntegration($uuids, $token)
|
||||
{
|
||||
try{
|
||||
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/";
|
||||
|
||||
$headers = [
|
||||
'Authorization' => "Bearer " . $token,
|
||||
];
|
||||
|
||||
$res = $this->service_client->get($url, $headers);
|
||||
|
||||
if (empty($res->original['success']) || !$res->original['success']) {
|
||||
// Log error
|
||||
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
|
||||
if(empty($uuids)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $res->original['data']['data'] ?? null;
|
||||
if (!$data) {
|
||||
Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]);
|
||||
return false;
|
||||
}
|
||||
$integrations = [];
|
||||
foreach($uuids as $uuid){
|
||||
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/";
|
||||
|
||||
$resultData = PbgTaskIndexIntegrations::updateOrCreate(
|
||||
['pbg_task_uid' => $uuid],
|
||||
[
|
||||
$headers = [
|
||||
'Authorization' => "Bearer " . $token,
|
||||
];
|
||||
|
||||
$res = $this->service_client->get($url, $headers);
|
||||
|
||||
if (empty($res->original['success']) || !$res->original['success']) {
|
||||
// Log error
|
||||
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $res->original['data']['data'] ?? null;
|
||||
if (!$data) {
|
||||
Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$integrations[] = [
|
||||
'pbg_task_uid' => $uuid,
|
||||
'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null,
|
||||
'indeks_parameter_kompleksitas' => $data['indeks_parameter_kompleksitas'] ?? null,
|
||||
'indeks_parameter_permanensi' => $data['indeks_parameter_permanensi'] ?? null,
|
||||
@@ -94,8 +99,11 @@ class ServiceSIMBG
|
||||
'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null,
|
||||
'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null,
|
||||
'total' => $data['total'] ?? null,
|
||||
]
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
PbgTaskIndexIntegrations::upsert($integrations, ['pbg_task_uid'], ['indeks_fungsi_bangunan',
|
||||
'indeks_parameter_kompleksitas', 'indeks_parameter_permanensi', 'indeks_parameter_ketinggian', 'faktor_kepemilikan', 'indeks_terintegrasi', 'total']);
|
||||
|
||||
return true;
|
||||
}catch (Exception $e){
|
||||
@@ -106,130 +114,136 @@ class ServiceSIMBG
|
||||
|
||||
public function syncTaskList()
|
||||
{
|
||||
$initResToken = $this->getToken();
|
||||
|
||||
$importDatasource = ImportDatasource::create([
|
||||
'status' => ImportDatasourceStatus::Processing->value,
|
||||
]);
|
||||
|
||||
if (empty($initResToken->original['data']['token']['access'])) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Failed to retrieve token'
|
||||
try {
|
||||
$importDatasource = ImportDatasource::create([
|
||||
'status' => ImportDatasourceStatus::Processing->value,
|
||||
]);
|
||||
return $this->resError("Failed to retrieve token");
|
||||
}
|
||||
|
||||
$apiToken = $initResToken->original['data']['token']['access'];
|
||||
$headers = ['Authorization' => "Bearer " . $apiToken];
|
||||
|
||||
$url = "/api/pbg/v1/list/?page=1&size={$this->fetch_per_page}&sort=ASC";
|
||||
$initialResponse = $this->service_client->get($url, $headers);
|
||||
|
||||
$totalPage = $initialResponse->original['data']['total_page'] ?? 0;
|
||||
if ($totalPage == 0) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Invalid response: no total_page'
|
||||
]);
|
||||
return $this->resError("Invalid response from API");
|
||||
}
|
||||
|
||||
$savedCount = $failedCount = 0;
|
||||
|
||||
for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) {
|
||||
$pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
|
||||
$getToken = $this->getToken();
|
||||
Log::info("response index integration", ['currentPage' => $currentPage]);
|
||||
if (empty($getToken->original['data']['token']['access'])) {
|
||||
if (empty($initResToken->original['data']['token']['access'])) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Failed to retrieve token'
|
||||
]);
|
||||
break;
|
||||
return $this->resError("Failed to retrieve token");
|
||||
}
|
||||
$token = $getToken->original['data']['token']['access'];
|
||||
$headers = ['Authorization' => "Bearer " . $token];
|
||||
$response = $this->service_client->get($pageUrl, $headers);
|
||||
$tasks = $response->original['data']['data'] ?? [];
|
||||
|
||||
if (empty($tasks)) {
|
||||
$initResToken = $this->getToken();
|
||||
$apiToken = $initResToken->original['data']['token']['access'];
|
||||
$headers = ['Authorization' => "Bearer " . $apiToken];
|
||||
|
||||
$url = "/api/pbg/v1/list/?page=1&size={$this->fetch_per_page}&sort=ASC";
|
||||
$initialResponse = $this->service_client->get($url, $headers);
|
||||
|
||||
$totalPage = $initialResponse->original['data']['total_page'] ?? 0;
|
||||
if ($totalPage == 0) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'No data found on page'
|
||||
'message' => 'Invalid response: no total_page'
|
||||
]);
|
||||
Log::warning("No data found on page", ['page' => $currentPage]);
|
||||
break;
|
||||
return $this->resError("Invalid response from API");
|
||||
}
|
||||
|
||||
Log::info("executed page", ['page' => $currentPage, 'total' => $totalPage]);
|
||||
$savedCount = $failedCount = 0;
|
||||
|
||||
$tasksCollective = [];
|
||||
foreach ($tasks as $item) {
|
||||
for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) {
|
||||
try {
|
||||
$tasksCollective[] = [
|
||||
'uuid' => $item['uid'],
|
||||
'name' => $item['name'],
|
||||
'owner_name' => $item['owner_name'],
|
||||
'application_type' => $item['application_type'],
|
||||
'application_type_name' => $item['application_type_name'],
|
||||
'condition' => $item['condition'],
|
||||
'registration_number' => $item['registration_number'],
|
||||
'document_number' => $item['document_number'],
|
||||
'address' => $item['address'],
|
||||
'status' => $item['status'],
|
||||
'status_name' => $item['status_name'],
|
||||
'slf_status' => $item['slf_status'] ?? null,
|
||||
'slf_status_name' => $item['slf_status_name'] ?? null,
|
||||
'function_type' => $item['function_type'],
|
||||
'consultation_type' => $item['consultation_type'],
|
||||
'due_date' => $item['due_date'],
|
||||
'land_certificate_phase' => $item['land_certificate_phase'],
|
||||
'task_created_at' => isset($item['created_at']) ? Carbon::parse($item['created_at'])->format('Y-m-d H:i:s') : null,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
];
|
||||
$pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
|
||||
|
||||
$this->syncIndexIntegration($item['uid'], $token);
|
||||
Log::info("Fetching tasks", ['currentPage' => $currentPage]);
|
||||
|
||||
$this->syncTaskDetailSubmit($item['uid'], $token);
|
||||
$response = $this->service_client->get($pageUrl, $headers);
|
||||
$tasks = $response->original['data']['data'] ?? [];
|
||||
|
||||
$savedCount++;
|
||||
if (empty($tasks)) {
|
||||
Log::warning("No data found on page", ['page' => $currentPage]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$tasksCollective = [];
|
||||
foreach ($tasks as $item) {
|
||||
try {
|
||||
$tasksCollective[] = [
|
||||
'uuid' => $item['uid'],
|
||||
'name' => $item['name'],
|
||||
'owner_name' => $item['owner_name'],
|
||||
'application_type' => $item['application_type'],
|
||||
'application_type_name' => $item['application_type_name'],
|
||||
'condition' => $item['condition'],
|
||||
'registration_number' => $item['registration_number'],
|
||||
'document_number' => $item['document_number'],
|
||||
'address' => $item['address'],
|
||||
'status' => $item['status'],
|
||||
'status_name' => $item['status_name'],
|
||||
'slf_status' => $item['slf_status'] ?? null,
|
||||
'slf_status_name' => $item['slf_status_name'] ?? null,
|
||||
'function_type' => $item['function_type'],
|
||||
'consultation_type' => $item['consultation_type'],
|
||||
'due_date' => $item['due_date'],
|
||||
'land_certificate_phase' => $item['land_certificate_phase'],
|
||||
'task_created_at' => isset($item['created_at']) ? Carbon::parse($item['created_at'])->format('Y-m-d H:i:s') : null,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
];
|
||||
|
||||
$this->syncTaskDetailSubmit($item['uid'], $apiToken);
|
||||
$savedCount++;
|
||||
} catch (Exception $e) {
|
||||
$failedCount++;
|
||||
Log::error("Failed to process task", [
|
||||
'error' => $e->getMessage(),
|
||||
'task' => $item,
|
||||
]);
|
||||
continue; // Skip failed task, continue processing the rest
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($tasksCollective)) {
|
||||
PbgTask::upsert($tasksCollective, ['uuid'], [
|
||||
'name', 'owner_name', 'application_type', 'application_type_name', 'condition',
|
||||
'registration_number', 'document_number', 'address', 'status', 'status_name',
|
||||
'slf_status', 'slf_status_name', 'function_type', 'consultation_type', 'due_date',
|
||||
'land_certificate_phase', 'task_created_at', 'updated_at'
|
||||
]);
|
||||
|
||||
$uuids = array_column($tasksCollective, 'uuid');
|
||||
$this->syncIndexIntegration($uuids, $apiToken);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$failedCount++;
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => "Successfully processed: $savedCount, Failed: $failedCount"
|
||||
]);
|
||||
Log::error("Failed to process task", [
|
||||
Log::error("Failed to process page", [
|
||||
'error' => $e->getMessage(),
|
||||
'task' => $item,
|
||||
'page' => $currentPage,
|
||||
]);
|
||||
break;
|
||||
continue; // Skip the failed page and move to the next
|
||||
}
|
||||
}
|
||||
|
||||
PbgTask::upsert($tasksCollective, ['uuid'], [
|
||||
'name', 'owner_name', 'application_type', 'application_type_name', 'condition',
|
||||
'registration_number', 'document_number', 'address', 'status', 'status_name',
|
||||
'slf_status', 'slf_status_name', 'function_type', 'consultation_type', 'due_date',
|
||||
'land_certificate_phase', 'task_created_at', 'updated_at'
|
||||
BigdataResume::generateResumeData($importDatasource->id, "all");
|
||||
BigdataResume::generateResumeData($importDatasource->id, now()->year);
|
||||
|
||||
// Final update after processing all pages
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Success->value,
|
||||
'message' => "Successfully processed: $savedCount, Failed: $failedCount"
|
||||
]);
|
||||
|
||||
Log::info("syncTaskList completed", ['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
||||
|
||||
return $this->resSuccess(['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Log::error("syncTaskList failed", ['error' => $e->getMessage()]);
|
||||
if (isset($importDatasource)) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Critical failure: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
return $this->resError("Critical failure occurred: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Success->value,
|
||||
'message' => "Successfully processed: $savedCount, Failed: $failedCount"
|
||||
]);
|
||||
|
||||
BigdataResume::generateResumeData($importDatasource->id);
|
||||
|
||||
Log::info("syncTaskList completed", ['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
||||
|
||||
return $this->resSuccess(['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function syncTaskDetailSubmit($uuid, $token)
|
||||
{
|
||||
try{
|
||||
@@ -312,4 +326,3 @@ class ServiceSIMBG
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -109,4 +109,10 @@ return [
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
// set timeout queue
|
||||
|
||||
'worker' => [
|
||||
'timeout' => 300
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('bigdata_resumes', function (Blueprint $table) {
|
||||
$table->integer('spatial_count')->default(0);
|
||||
$table->decimal('spatial_sum', 20,2)->default(0);
|
||||
$table->string('year');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bigdata_resumes', function (Blueprint $table) {
|
||||
$table->dropColumn('spatial_count');
|
||||
$table->dropColumn('spatial_sum');
|
||||
$table->dropColumn('year');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -71,7 +71,7 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
[
|
||||
"name" => "Laporan",
|
||||
"url" => "/laporan",
|
||||
"icon" => "mingcute:task-line",
|
||||
"icon" => "mingcute:report-line",
|
||||
"parent_id" => null,
|
||||
"sort_order" => 6,
|
||||
]
|
||||
@@ -116,6 +116,13 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
"parent_id" => $dashboard->id,
|
||||
"sort_order" => 3,
|
||||
],
|
||||
[
|
||||
"name" => "PETA",
|
||||
"url" => "dashboard.maps",
|
||||
"icon" => null,
|
||||
"parent_id" => $dashboard->id,
|
||||
"sort_order" => 4,
|
||||
],
|
||||
[
|
||||
"name" => "Users",
|
||||
"url" => "users.index",
|
||||
@@ -207,6 +214,13 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
"parent_id" => $laporan->id,
|
||||
"sort_order" => 1,
|
||||
],
|
||||
[
|
||||
"name" => "Lap Pimpinan",
|
||||
"url" => "bigdata-resumes",
|
||||
"icon" => null,
|
||||
"parent_id" => $laporan->id,
|
||||
"sort_order" => 2,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($children_menus as $child_menu) {
|
||||
@@ -229,6 +243,8 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
$lack_of_potentials = Menu::where('name', 'Dashboard Potensi')->first();
|
||||
$spatial_plannings = Menu::where('name', 'Tata Ruang')->first();
|
||||
$pdam = Menu::where('name', 'PDAM')->first();
|
||||
$peta = Menu::where('name', 'PETA')->first();
|
||||
$bigdata_resume = Menu::where('name', 'Lap Pimpinan')->first();
|
||||
|
||||
// Superadmin gets all menus
|
||||
$superadmin->menus()->sync([
|
||||
@@ -256,6 +272,8 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
$lack_of_potentials->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$spatial_plannings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$pdam->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$bigdata_resume->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
]);
|
||||
|
||||
// Admin gets limited menus
|
||||
@@ -270,6 +288,7 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
$dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$data->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
]);
|
||||
|
||||
// Attach User to role super admin
|
||||
User::findOrFail(1)->roles()->sync([$superadmin->id]);
|
||||
}
|
||||
|
||||
5851
package-lock.json
generated
5851
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
@@ -1,36 +1,37 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.4",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^1.0",
|
||||
"postcss": "^8.4.47",
|
||||
"sass": "^1.81.1",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"vite": "^5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"apexcharts": "^3.44.2",
|
||||
"big.js": "^6.2.2",
|
||||
"bootstrap": "^5.3.3",
|
||||
"countup.js": "^2.3.2",
|
||||
"dropzone": "^5.9.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"gmaps": "^0.4.25",
|
||||
"gridjs": "^5.1.0",
|
||||
"iconify-icon": "^2.1.0",
|
||||
"jsvectormap": "^1.5.1",
|
||||
"moment": "^2.29.4",
|
||||
"node-waves": "^0.7.6",
|
||||
"quill": "^1.3.7",
|
||||
"simplebar": "^5.3.9",
|
||||
"sweetalert2": "^11.16.0",
|
||||
"wnumb": "^1.2.0"
|
||||
}
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.4",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^1.0",
|
||||
"postcss": "^8.4.47",
|
||||
"sass": "^1.81.1",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"vite": "^5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"apexcharts": "^3.44.2",
|
||||
"big.js": "^6.2.2",
|
||||
"bootstrap": "^5.3.3",
|
||||
"countup.js": "^2.3.2",
|
||||
"dropzone": "^5.9.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"gmaps": "^0.4.25",
|
||||
"gridjs": "^5.1.0",
|
||||
"iconify-icon": "^2.1.0",
|
||||
"jsvectormap": "^1.5.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"moment": "^2.29.4",
|
||||
"node-waves": "^0.7.6",
|
||||
"quill": "^1.3.7",
|
||||
"simplebar": "^5.3.9",
|
||||
"sweetalert2": "^11.16.0",
|
||||
"wnumb": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,30 @@ import bootstrap from "bootstrap/dist/js/bootstrap";
|
||||
window.bootstrap = bootstrap;
|
||||
import "iconify-icon";
|
||||
import "simplebar/dist/simplebar";
|
||||
// import flatpickr from "flatpickr";
|
||||
// import "flatpickr/dist/flatpickr.min.css";
|
||||
|
||||
// class InitDatePicker {
|
||||
// constructor(selector = ".datepicker") {
|
||||
// this.selector = selector;
|
||||
// }
|
||||
// init() {
|
||||
// const elements = document.querySelectorAll(this.selector);
|
||||
// if (elements.length === 0) return; // Skip if no elements found
|
||||
|
||||
// const today = new Date();
|
||||
// const minYear = today.getFullYear() - 5;
|
||||
|
||||
// elements.forEach((element) => {
|
||||
// flatpickr(element, {
|
||||
// enableTime: false,
|
||||
// dateFormat: "Y-m-d",
|
||||
// minDate: `${minYear}-01-01`,
|
||||
// maxDate: today,
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
class Components {
|
||||
initBootstrapComponents() {
|
||||
@@ -107,6 +131,7 @@ class FormValidation {
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new Components().init(), new FormValidation().init();
|
||||
// new InitDatePicker().init();
|
||||
});
|
||||
class ThemeLayout {
|
||||
constructor() {
|
||||
|
||||
154
resources/js/bigdata-resumes/index.js
Normal file
154
resources/js/bigdata-resumes/index.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||
import gridjs from "gridjs/dist/gridjs.umd.js";
|
||||
import "gridjs/dist/gridjs.umd.js";
|
||||
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
|
||||
import Swal from "sweetalert2";
|
||||
import moment from "moment";
|
||||
|
||||
class BigdataResume {
|
||||
constructor() {
|
||||
this.toastMessage = document.getElementById("toast-message");
|
||||
this.toastElement = document.getElementById("toastNotification");
|
||||
this.toast = new bootstrap.Toast(this.toastElement);
|
||||
this.table = null;
|
||||
|
||||
// Initialize functions
|
||||
this.initTableDataSettings();
|
||||
// this.initEvents();
|
||||
}
|
||||
initEvents() {
|
||||
document.body.addEventListener("click", async (event) => {
|
||||
const deleteButton = event.target.closest(
|
||||
".btn-delete-data-settings"
|
||||
);
|
||||
if (deleteButton) {
|
||||
event.preventDefault();
|
||||
await this.handleDelete(deleteButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initTableDataSettings() {
|
||||
let tableContainer = document.getElementById("table-bigdata-resumes");
|
||||
// Create a new Grid.js instance only if it doesn't exist
|
||||
this.table = new Grid({
|
||||
columns: [
|
||||
{ name: "ID" },
|
||||
{ name: "Potention Count" },
|
||||
{ name: "Potention Sum" },
|
||||
{ name: "Non Verified Count" },
|
||||
{ name: "Non Verified Sum" },
|
||||
{ name: "Verified Count" },
|
||||
{ name: "Verified Sum" },
|
||||
{ name: "Business Count" },
|
||||
{ name: "Business Sum" },
|
||||
{ name: "Non Business Count" },
|
||||
{ name: "Non Business Sum" },
|
||||
{ name: "Spatial Sum" },
|
||||
{ name: "Spatial Count" },
|
||||
{
|
||||
name: "Created",
|
||||
attributes: { style: "width: 200px; white-space: nowrap;" }, // Set width dynamically
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
limit: 15,
|
||||
server: {
|
||||
url: (prev, page) =>
|
||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||
page + 1
|
||||
}`,
|
||||
},
|
||||
},
|
||||
sort: true,
|
||||
search: {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/bigdata-report`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.potention_count,
|
||||
addThousandSeparators(item.potention_sum),
|
||||
item.non_verified_count,
|
||||
addThousandSeparators(item.non_verified_sum),
|
||||
item.verified_count,
|
||||
addThousandSeparators(item.verified_sum),
|
||||
item.business_count,
|
||||
addThousandSeparators(item.business_sum),
|
||||
item.non_business_count,
|
||||
addThousandSeparators(item.non_business_sum),
|
||||
item.spatial_count,
|
||||
addThousandSeparators(item.spatial_sum),
|
||||
moment(item.created_at).format("YYYY-MM-DD H:mm:ss"),
|
||||
]),
|
||||
total: (data) => data.total,
|
||||
},
|
||||
}).render(tableContainer);
|
||||
}
|
||||
async handleDelete(deleteButton) {
|
||||
const id = deleteButton.getAttribute("data-id");
|
||||
|
||||
const result = await Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You won't be able to revert this!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
});
|
||||
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
let response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/data-settings/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
this.toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
this.toast.show();
|
||||
|
||||
// Refresh Grid.js table
|
||||
if (typeof this.table !== "undefined") {
|
||||
this.table.updateConfig({}).forceRender();
|
||||
}
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
this.toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
this.toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
this.toastMessage.innerText = "An error occurred!";
|
||||
this.toast.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new BigdataResume();
|
||||
});
|
||||
@@ -5,9 +5,9 @@ Dropzone.autoDiscover = false;
|
||||
var previewTemplate,
|
||||
dropzone,
|
||||
dropzonePreviewNode = document.querySelector("#dropzone-preview-list");
|
||||
console.log(previewTemplate);
|
||||
console.log(dropzone);
|
||||
console.log(dropzonePreviewNode);
|
||||
|
||||
const uploadButton = document.getElementById("btnUploadBusinessIndustry");
|
||||
const spinner = document.getElementById("spinner");
|
||||
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
@@ -29,97 +29,50 @@ const toast = new bootstrap.Toast(toastNotification);
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
init: function () {
|
||||
// Listen for the success event
|
||||
this.on("success", function (file, response) {
|
||||
console.log("File successfully uploaded:", file);
|
||||
console.log("API Response:", response);
|
||||
|
||||
// Show success toast
|
||||
document.getElementById("toast-message").innerText =
|
||||
response.message;
|
||||
toast.show();
|
||||
document.getElementById("submit-upload").innerHTML =
|
||||
"Upload Files";
|
||||
// Tunggu sebentar lalu reload halaman
|
||||
setTimeout(() => {
|
||||
window.location.href = "/data/business-industries";
|
||||
}, 2000);
|
||||
});
|
||||
// Listen for the error event
|
||||
this.on("error", function (file, errorMessage) {
|
||||
console.error("Error uploading file:", file);
|
||||
console.error("Error message:", errorMessage);
|
||||
// Handle the error response
|
||||
|
||||
// Show error toast
|
||||
document.getElementById("toast-message").innerText =
|
||||
errorMessage.message;
|
||||
toast.show();
|
||||
document.getElementById("submit-upload").innerHTML =
|
||||
"Upload Files";
|
||||
uploadButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
});
|
||||
},
|
||||
})));
|
||||
|
||||
// Add event listener to control the submission manually
|
||||
document.querySelector("#submit-upload").addEventListener("click", function () {
|
||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||
const formData = new FormData();
|
||||
console.log("Dropzonefiles", dropzone.files);
|
||||
document
|
||||
.querySelector("#btnUploadBusinessIndustry")
|
||||
.addEventListener("click", function () {
|
||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||
const formData = new FormData();
|
||||
|
||||
this.innerHTML =
|
||||
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
||||
if (dropzone.files.length > 0) {
|
||||
formData.append("file", dropzone.files[0]);
|
||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||
uploadButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
} else {
|
||||
document.getElementById("toast-message").innerText =
|
||||
"Please add a file first.";
|
||||
toast.show();
|
||||
uploadButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
});
|
||||
|
||||
// Pastikan ada file dalam queue sebelum memprosesnya
|
||||
if (dropzone.files.length > 0) {
|
||||
formData.append("file", dropzone.files[0]);
|
||||
console.log("ini adalah form data on submit", ...formData);
|
||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||
} else {
|
||||
// Show error toast when no file is selected
|
||||
document.getElementById("toast-message").innerText =
|
||||
"Please add a file first.";
|
||||
toast.show();
|
||||
|
||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
||||
}
|
||||
});
|
||||
|
||||
// Optional: Listen for the 'addedfile' event to log or control file add behavior
|
||||
dropzone.on("addedfile", function (file) {
|
||||
console.log("File ditambahkan:", file);
|
||||
console.log("Nama File:", file.name);
|
||||
console.log("Tipe File:", file.type);
|
||||
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
|
||||
});
|
||||
dropzone.on("addedfile", function (file) {});
|
||||
|
||||
dropzone.on("complete", function (file) {
|
||||
dropzone.removeFile(file);
|
||||
});
|
||||
|
||||
// Function to show toast
|
||||
// function showToast(iconClass, iconColor, message) {
|
||||
// const toastElement = document.getElementById("toastUploadAdvertisement");
|
||||
// const toastBody = toastElement.querySelector(".toast-body");
|
||||
// const toastHeader = toastElement.querySelector(".toast-header");
|
||||
|
||||
// // Remove existing icon (if any) before adding the new one
|
||||
// const existingIcon = toastHeader.querySelector(".bx");
|
||||
// if (existingIcon) {
|
||||
// toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
|
||||
// }
|
||||
|
||||
// // Add the new icon to the toast header
|
||||
// const icon = document.createElement("i");
|
||||
// icon.classList.add("bx", iconClass);
|
||||
// icon.style.fontSize = "25px";
|
||||
// icon.style.color = iconColor;
|
||||
// toastHeader.querySelector(".auth-logo").appendChild(icon);
|
||||
|
||||
// // Set the toast message
|
||||
// toastBody.textContent = message;
|
||||
|
||||
// // Show the toast
|
||||
// const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
||||
// toast.show();
|
||||
// }
|
||||
|
||||
@@ -2,58 +2,36 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||
import gridjs from "gridjs/dist/gridjs.umd.js";
|
||||
import "gridjs/dist/gridjs.umd.js";
|
||||
import GlobalConfig from "../global-config.js";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
class BusinessIndustries {
|
||||
constructor() {
|
||||
this.table = null; // Store Grid.js instance
|
||||
this.toastMessage = document.getElementById("toast-message");
|
||||
this.toastElement = document.getElementById("toastNotification");
|
||||
this.toast = new bootstrap.Toast(this.toastElement);
|
||||
this.table = null;
|
||||
|
||||
// Initialize functions
|
||||
this.initTableBusinessIndustries();
|
||||
this.initEvents();
|
||||
}
|
||||
init() {
|
||||
this.getFetchApiData();
|
||||
initEvents() {
|
||||
document.body.addEventListener("click", async (event) => {
|
||||
const deleteButton = event.target.closest(
|
||||
".btn-delete-business-industry"
|
||||
);
|
||||
if (deleteButton) {
|
||||
event.preventDefault();
|
||||
await this.handleDelete(deleteButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getFetchApiData() {
|
||||
initTableBusinessIndustries() {
|
||||
let tableContainer = document.getElementById(
|
||||
"table-business-industries"
|
||||
);
|
||||
|
||||
if (this.table) {
|
||||
// If table exists, update its data instead of recreating
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-business-industries`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.nama_kecamatan,
|
||||
item.nama_kelurahan,
|
||||
item.nop,
|
||||
item.nama_wajib_pajak,
|
||||
item.alamat_wajib_pajak,
|
||||
item.alamat_objek_pajak,
|
||||
item.luas_bumi,
|
||||
item.luas_bangunan,
|
||||
item.njop_bumi,
|
||||
item.njop_bangunan,
|
||||
item.ketetapan,
|
||||
item.tahun_pajak,
|
||||
item.created_at,
|
||||
item.id,
|
||||
]),
|
||||
total: (data) => data.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new Grid.js instance only if it doesn't exist
|
||||
this.table = new Grid({
|
||||
columns: [
|
||||
{ name: "ID", width: "80px", hidden: false },
|
||||
@@ -71,27 +49,20 @@ class BusinessIndustries {
|
||||
{ name: "Tahun Pajak", width: "120px" },
|
||||
{ name: "Created", width: "180px" },
|
||||
{
|
||||
name: "Actions",
|
||||
width: "120px",
|
||||
formatter: function (cell) {
|
||||
return gridjs.html(`
|
||||
<div class="d-flex justify-content-center gap-2">
|
||||
<a href="/data/business-industries/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
|
||||
<i class='bx bx-edit'></i>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-red d-inline-flex align-items-center justify-content-center btn-delete-business-industries" data-id="${cell}">
|
||||
<i class='bx bxs-trash'></i>
|
||||
</button>
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
name: "Action",
|
||||
formatter: (cell) =>
|
||||
gridjs.html(`
|
||||
<div class="d-flex justify-content-center gap-2">
|
||||
<a href="/data/business-industries/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
|
||||
<i class='bx bx-edit'></i>
|
||||
</a>
|
||||
<button data-id="${cell}" class="btn btn-sm btn-red btn-delete-business-industry d-inline-flex align-items-center justify-content-center">
|
||||
<i class='bx bxs-trash' ></i>
|
||||
</button>
|
||||
</div>
|
||||
`),
|
||||
},
|
||||
],
|
||||
search: {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
limit: 15,
|
||||
server: {
|
||||
@@ -102,6 +73,11 @@ class BusinessIndustries {
|
||||
},
|
||||
},
|
||||
sort: true,
|
||||
search: {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-business-industries`,
|
||||
headers: {
|
||||
@@ -131,98 +107,26 @@ class BusinessIndustries {
|
||||
total: (data) => data.total,
|
||||
},
|
||||
}).render(tableContainer);
|
||||
|
||||
document.addEventListener("click", this.handleDelete.bind(this));
|
||||
}
|
||||
handleDelete(event) {
|
||||
if (event.target.classList.contains("btn-delete-business-industries")) {
|
||||
event.preventDefault();
|
||||
const id = event.target.getAttribute("data-id");
|
||||
let modalElement = document.getElementById("modalConfirmation");
|
||||
let toastMessage = document.getElementById("toast-message");
|
||||
async handleDelete(deleteButton) {
|
||||
const id = deleteButton.getAttribute("data-id");
|
||||
|
||||
if (!modalElement) {
|
||||
console.error("Modal element not found!");
|
||||
return;
|
||||
}
|
||||
const result = await Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You won't be able to revert this!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
});
|
||||
|
||||
let modal = new bootstrap.Modal(modalElement);
|
||||
let btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
let toastElement = document.getElementById("toastNotification");
|
||||
let toast = new bootstrap.Toast(toastElement);
|
||||
|
||||
// Remove previous event listeners to avoid multiple bindings
|
||||
btnSaveConfirmation.replaceWith(
|
||||
btnSaveConfirmation.cloneNode(true)
|
||||
);
|
||||
btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
|
||||
// Set the role ID on the confirm button inside the modal
|
||||
btnSaveConfirmation.setAttribute("data-business-industries-id", id);
|
||||
|
||||
// Show the modal
|
||||
modal.show();
|
||||
|
||||
btnSaveConfirmation.addEventListener("click", async () => {
|
||||
let deletedId = btnSaveConfirmation.getAttribute(
|
||||
"data-business-industries-id"
|
||||
);
|
||||
|
||||
try {
|
||||
let response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/api-business-industries/${deletedId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
toast.show();
|
||||
|
||||
// Hide modal
|
||||
modal.hide();
|
||||
|
||||
// Refresh Grid.js table
|
||||
this.refreshDataSettings();
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
toastMessage.innerText = "An error occurred!";
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
refreshDataSettings() {
|
||||
if (this.table) {
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-business-industries`,
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
let response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/api-business-industries/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
@@ -230,31 +134,34 @@ class BusinessIndustries {
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.nama_kecamatan,
|
||||
item.nama_kelurahan,
|
||||
item.nop,
|
||||
item.nama_wajib_pajak,
|
||||
item.alamat_wajib_pajak,
|
||||
item.alamat_objek_pajak,
|
||||
item.luas_bumi,
|
||||
item.luas_bangunan,
|
||||
item.njop_bumi,
|
||||
item.njop_bangunan,
|
||||
item.ketetapan,
|
||||
item.tahun_pajak,
|
||||
item.created_at,
|
||||
item.id, // ID for Actions column
|
||||
]),
|
||||
total: (data) => data.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
this.toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
this.toast.show();
|
||||
|
||||
// Refresh Grid.js table
|
||||
if (typeof this.table !== "undefined") {
|
||||
this.table.updateConfig({}).forceRender();
|
||||
}
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
this.toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
this.toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
this.toastMessage.innerText = "An error occurred!";
|
||||
this.toast.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new BusinessIndustries().init();
|
||||
new BusinessIndustries();
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class UpdateCustomer {
|
||||
constructor() {
|
||||
this.initUpdateSpatial();
|
||||
this.initUpdateCustomer();
|
||||
}
|
||||
|
||||
initUpdateSpatial() {
|
||||
initUpdateCustomer() {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
|
||||
@@ -12,7 +12,7 @@ class Customers {
|
||||
this.table = null;
|
||||
|
||||
// Initialize functions
|
||||
this.initTableSpatialPlannings();
|
||||
this.initTableCustomers();
|
||||
this.initEvents();
|
||||
}
|
||||
initEvents() {
|
||||
@@ -25,7 +25,7 @@ class Customers {
|
||||
});
|
||||
}
|
||||
|
||||
initTableSpatialPlannings() {
|
||||
initTableCustomers() {
|
||||
let tableContainer = document.getElementById("table-customers");
|
||||
// Create a new Grid.js instance only if it doesn't exist
|
||||
this.table = new Grid({
|
||||
|
||||
@@ -1,167 +1,31 @@
|
||||
import Big from "big.js";
|
||||
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
|
||||
import flatpickr from "flatpickr";
|
||||
import "flatpickr/dist/flatpickr.min.css";
|
||||
import InitDatePicker from "../utils/InitDatePicker.js";
|
||||
|
||||
class BigData {
|
||||
async init() {
|
||||
try {
|
||||
this.filterYear = new Date().getFullYear(); // Set initial year
|
||||
|
||||
let yearSelect = document.querySelector("#yearPicker");
|
||||
let filterButton = document.querySelector("#btnFilterYear");
|
||||
|
||||
if (!yearSelect || !filterButton) {
|
||||
console.error(
|
||||
"Element #yearPicker or #btnFilterYear not found."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set default value for input
|
||||
yearSelect.value = this.filterYear;
|
||||
|
||||
yearSelect.addEventListener("change", () => {
|
||||
this.updateYear(yearSelect.value);
|
||||
});
|
||||
|
||||
// Handle button click
|
||||
filterButton.addEventListener("click", () => {
|
||||
this.updateYear(yearSelect.value);
|
||||
});
|
||||
|
||||
console.log("init filter this year", this.filterYear);
|
||||
new InitDatePicker(
|
||||
"#datepicker-dashboard-bigdata",
|
||||
this.handleChangeDate.bind(this)
|
||||
).init();
|
||||
|
||||
// Load initial data
|
||||
await this.updateData(this.filterYear);
|
||||
this.updateData("latest");
|
||||
} catch (error) {
|
||||
console.error("Error initializing data:", error);
|
||||
}
|
||||
}
|
||||
updateYear(value) {
|
||||
let inputYear = parseInt(value, 10);
|
||||
if (!isNaN(inputYear)) {
|
||||
this.filterYear = inputYear;
|
||||
this.updateData(this.filterYear);
|
||||
} else {
|
||||
console.error("Invalid year input");
|
||||
}
|
||||
|
||||
handleChangeDate(filterDate) {
|
||||
if (!filterDate) return;
|
||||
this.updateData(filterDate);
|
||||
}
|
||||
async updateData(year) {
|
||||
async updateData(filterDate) {
|
||||
try {
|
||||
this.totalTargetPAD = await this.getDataSettings("TARGET_PAD");
|
||||
this.resultDataTotal = await this.getDataTotalPotensi(year);
|
||||
this.dataVerification = await this.getDataVerfication(year);
|
||||
this.dataNonVerification = await this.getDataNonVerfication(year);
|
||||
this.dataBusiness = await this.getDataBusiness(year);
|
||||
this.dataNonBusiness = await this.getDataNonBusiness(year);
|
||||
this.dataTataRuang = await this.getDataSettings("TATA_RUANG");
|
||||
this.dataSumRealisasiTerbit = await this.getDataSettings(
|
||||
"REALISASI_TERBIT_PBG_SUM"
|
||||
);
|
||||
this.dataCountRealisasiTerbit = await this.getDataSettings(
|
||||
"REALISASI_TERBIT_PBG_COUNT"
|
||||
);
|
||||
this.dataSumMenungguKlikDPMPTSP = await this.getDataSettings(
|
||||
"MENUNGGU_KLIK_DPMPTSP_SUM"
|
||||
);
|
||||
this.dataCountMenungguKlikDPMPTSP = await this.getDataSettings(
|
||||
"MENUNGGU_KLIK_DPMPTSP_COUNT"
|
||||
);
|
||||
this.dataSumProsesDinasTeknis = await this.getDataSettings(
|
||||
"PROSES_DINAS_TEKNIS_SUM"
|
||||
);
|
||||
this.dataCountProsesDinasTeknis = await this.getDataSettings(
|
||||
"PROSES_DINAS_TEKNIS_COUNT"
|
||||
);
|
||||
this.resumeBigData = await this.getBigDataResume(filterDate);
|
||||
|
||||
// total potensi
|
||||
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
|
||||
this.bigTotalPotensi = new Big(this.resultDataTotal.totalData ?? 0);
|
||||
|
||||
this.resultPercentage = 0;
|
||||
if (this.bigTotalPotensi > 0 && this.bigTargetPAD > 0) {
|
||||
this.resultPercentage = this.bigTotalPotensi
|
||||
.div(this.bigTargetPAD)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
if (this.resultPercentage > 100) {
|
||||
this.resultPercentage = 100;
|
||||
}
|
||||
}
|
||||
|
||||
// tata ruang
|
||||
this.bigTotalTataRuang = new Big(this.dataTataRuang);
|
||||
this.percentageResultTataRuang =
|
||||
this.bigTotalTataRuang <= 0 || this.bigTotalPotensi <= 0
|
||||
? 0
|
||||
: this.bigTotalTataRuang
|
||||
.div(this.bigTotalPotensi)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
|
||||
// kekurangan potensi
|
||||
this.totalKekuranganPotensi = new Big(
|
||||
this.bigTargetPAD - this.bigTotalPotensi
|
||||
);
|
||||
|
||||
this.percentageKekuranganPotensi =
|
||||
this.totalKekuranganPotensi <= 0 || this.bigTargetPAD <= 0
|
||||
? 0
|
||||
: this.totalKekuranganPotensi
|
||||
.div(this.bigTargetPAD)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
|
||||
// non-verification documents
|
||||
this.bigTotalNonVerification = new Big(
|
||||
this.dataNonVerification.total
|
||||
);
|
||||
this.percentageResultNonVerification =
|
||||
this.bigTotalNonVerification <= 0 || this.bigTotalPotensi <= 0
|
||||
? 0
|
||||
: this.bigTotalNonVerification
|
||||
.div(this.bigTotalPotensi)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
|
||||
// verification documents
|
||||
this.bigTotalVerification = new Big(this.dataVerification.total);
|
||||
this.percetageResultVerification =
|
||||
this.bigTotalVerification <= 0 || this.bigTotalPotensi <= 0
|
||||
? 0
|
||||
: this.bigTotalVerification
|
||||
.div(this.bigTargetPAD)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
|
||||
// business documents
|
||||
this.bigTotalBusiness = new Big(this.dataBusiness.total);
|
||||
this.percentageResultBusiness =
|
||||
this.bigTotalNonVerification <= 0 || this.bigTotalBusiness <= 0
|
||||
? 0
|
||||
: this.bigTotalBusiness
|
||||
.div(this.bigTotalNonVerification)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
|
||||
// non-business documents
|
||||
this.bigTotalNonBusiness = new Big(this.dataNonBusiness.total);
|
||||
this.percentageResultNonBusiness =
|
||||
this.bigTotalNonBusiness <= 0 ||
|
||||
this.bigTotalNonVerification <= 0
|
||||
? 0
|
||||
: this.bigTotalNonBusiness
|
||||
.div(this.bigTotalNonVerification)
|
||||
.times(100)
|
||||
.toFixed(2);
|
||||
|
||||
if (!this.bigTargetPAD) {
|
||||
console.error("Failed to load chart data");
|
||||
return;
|
||||
}
|
||||
|
||||
this.initChartTargetPAD();
|
||||
this.initChartTargetPAD(filterDate);
|
||||
this.initChartUsaha();
|
||||
this.initChartNonUsaha();
|
||||
this.initChartTotalPotensi();
|
||||
@@ -177,10 +41,10 @@ class BigData {
|
||||
}
|
||||
}
|
||||
|
||||
async getDataTotalPotensi(year) {
|
||||
async getBigDataResume(filterByDate) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/all-task-documents?year=${year}`,
|
||||
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterByDate}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -198,169 +62,23 @@ class BigData {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
countData: data.data.count,
|
||||
totalData: data.data.total,
|
||||
};
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async getDataVerfication(year) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/verification-documents?year=${year}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
count: data.data.count,
|
||||
total: data.data.total,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getDataNonVerfication(year) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/non-verification-documents?year=${year}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
count: data.data.count,
|
||||
total: data.data.total,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getDataBusiness(year) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/business-documents?year=${year}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
count: data.data.count,
|
||||
total: data.data.total,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getDataNonBusiness(year) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/non-business-documents?year=${year}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
count: data.data.count,
|
||||
total: data.data.total,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getDataSettings(string_key) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/api-data-settings?search=${string_key}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.data[0].value;
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
initChartTargetPAD() {
|
||||
initChartTargetPAD(filterDate) {
|
||||
const year =
|
||||
filterDate === "latest"
|
||||
? new Date().getFullYear()
|
||||
: new Date(filterDate).getFullYear();
|
||||
document
|
||||
.querySelectorAll(".document-title.chart-target-pad")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Target PAD ${year}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-target-pad")
|
||||
.forEach((element) => {
|
||||
@@ -370,53 +88,59 @@ class BigData {
|
||||
.querySelectorAll(".document-total.chart-target-pad")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTargetPAD.toString()
|
||||
this.resumeBigData.target_pad.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-target-pad")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${100}%`;
|
||||
element.innerText = `${this.resumeBigData.target_pad.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartTotalPotensi() {
|
||||
const countAll = this.resultDataTotal.countData ?? 0;
|
||||
// const countAll = this.resultDataTotal.countData ?? 0;
|
||||
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-total-potensi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${countAll}`;
|
||||
// element.innerText = `${countAll}`;
|
||||
element.innerText = `${this.resumeBigData.total_potensi.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-total-potensi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTotalPotensi.toString()
|
||||
// this.bigTotalPotensi.toString()
|
||||
this.resumeBigData.total_potensi.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-total-potensi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.resultPercentage}%`;
|
||||
// element.innerText = `${this.resultPercentage}%`;
|
||||
element.innerText = `${this.resumeBigData.total_potensi.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartVerificationDocuments() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-berkas-terverifikasi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataVerification.count}`;
|
||||
// element.innerText = `${this.dataVerification.count}`;
|
||||
element.innerText = `${this.resumeBigData.verified_document.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-berkas-terverifikasi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTotalVerification.toString()
|
||||
// this.bigTotalVerification.toString()
|
||||
this.resumeBigData.verified_document.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-berkas-terverifikasi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.percetageResultVerification}%`;
|
||||
// element.innerText = `${this.percetageResultVerification}%`;
|
||||
element.innerText = `${this.resumeBigData.verified_document.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartNonVerificationDocuments() {
|
||||
@@ -425,7 +149,8 @@ class BigData {
|
||||
".document-count.chart-berkas-belum-terverifikasi"
|
||||
)
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataNonVerification.count}`;
|
||||
// element.innerText = `${this.dataNonVerification.count}`;
|
||||
element.innerText = `${this.resumeBigData.non_verified_document.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(
|
||||
@@ -433,7 +158,8 @@ class BigData {
|
||||
)
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTotalNonVerification.toString()
|
||||
// this.bigTotalNonVerification.toString()
|
||||
this.resumeBigData.non_verified_document.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
@@ -441,45 +167,52 @@ class BigData {
|
||||
".small-percentage.chart-berkas-belum-terverifikasi"
|
||||
)
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.percentageResultNonVerification}%`;
|
||||
// element.innerText = `${this.percentageResultNonVerification}%`;
|
||||
element.innerText = `${this.resumeBigData.non_verified_document.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartUsaha() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-business")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataBusiness.count}`;
|
||||
// element.innerText = `${this.dataBusiness.count}`;
|
||||
element.innerText = `${this.resumeBigData.business_document.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-business")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTotalBusiness.toString()
|
||||
// this.bigTotalBusiness.toString()
|
||||
this.resumeBigData.business_document.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-business")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.percentageResultBusiness}%`;
|
||||
// element.innerText = `${this.percentageResultBusiness}%`;
|
||||
element.innerText = `${this.resumeBigData.business_document.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartNonUsaha() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-non-business")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataNonBusiness.count}`;
|
||||
// element.innerText = `${this.dataNonBusiness.count}`;
|
||||
element.innerText = `${this.resumeBigData.non_business_document.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-non-business")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTotalNonBusiness.toString()
|
||||
// this.bigTotalNonBusiness.toString()
|
||||
this.resumeBigData.non_business_document.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-non-business")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.percentageResultNonBusiness}%`;
|
||||
// element.innerText = `${this.percentageResultNonBusiness}%`;
|
||||
element.innerText = `${this.resumeBigData.non_business_document.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartKekuranganPotensi() {
|
||||
@@ -492,89 +225,97 @@ class BigData {
|
||||
.querySelectorAll(".document-total.chart-kekurangan-potensi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.totalKekuranganPotensi.toString()
|
||||
// this.totalKekuranganPotensi.toString()
|
||||
this.resumeBigData.kekurangan_potensi.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-kekurangan-potensi")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.percentageKekuranganPotensi}%`;
|
||||
// element.innerText = `${this.percentageKekuranganPotensi}%`;
|
||||
element.innerText = `${this.resumeBigData.kekurangan_potensi.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartRealisasiTerbitPBG() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-realisasi-tebit-pbg")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataCountRealisasiTerbit}`;
|
||||
// element.innerText = `${this.dataCountRealisasiTerbit}`;
|
||||
element.innerText = `${this.resumeBigData.realisasi_terbit.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-realisasi-tebit-pbg")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.dataSumRealisasiTerbit
|
||||
// this.dataSumRealisasiTerbit
|
||||
this.resumeBigData.realisasi_terbit.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-realisasi-tebit-pbg")
|
||||
.forEach((element) => {
|
||||
element.innerText = `0.00%`;
|
||||
element.innerText = `${this.resumeBigData.realisasi_terbit.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartMenungguKlikDPMPTSP() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-menunggu-klik-dpmptsp")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`;
|
||||
// element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`;
|
||||
element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-menunggu-klik-dpmptsp")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.dataSumMenungguKlikDPMPTSP
|
||||
// this.dataSumMenungguKlikDPMPTSP
|
||||
this.resumeBigData.menunggu_klik_dpmptsp.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-menunggu-klik-dpmptsp")
|
||||
.forEach((element) => {
|
||||
element.innerText = `0.00%`;
|
||||
element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartProsesDinasTeknis() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-proses-dinas-teknis")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.dataCountProsesDinasTeknis}`;
|
||||
// element.innerText = `${this.dataCountProsesDinasTeknis}`;
|
||||
element.innerText = `${this.resumeBigData.proses_dinas_teknis.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-proses-dinas-teknis")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.dataSumProsesDinasTeknis
|
||||
// this.dataSumProsesDinasTeknis
|
||||
this.resumeBigData.proses_dinas_teknis.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-proses-dinas-teknis")
|
||||
.forEach((element) => {
|
||||
element.innerText = `0.00%`;
|
||||
element.innerText = `${this.resumeBigData.proses_dinas_teknis.percentage}%`;
|
||||
});
|
||||
}
|
||||
initChartPotensiTataRuang() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-potensi-tata-ruang")
|
||||
.forEach((element) => {
|
||||
element.innerText = "";
|
||||
element.innerText = `${this.resumeBigData.tata_ruang.count}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".document-total.chart-potensi-tata-ruang")
|
||||
.forEach((element) => {
|
||||
element.innerText = `Rp.${addThousandSeparators(
|
||||
this.bigTotalTataRuang.toString()
|
||||
this.resumeBigData.tata_ruang.sum.toString()
|
||||
)}`;
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".small-percentage.chart-potensi-tata-ruang")
|
||||
.forEach((element) => {
|
||||
element.innerText = `${this.percentageResultTataRuang}%`;
|
||||
element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
import Big from "big.js";
|
||||
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
|
||||
import InitDatePicker from "../utils/InitDatePicker.js";
|
||||
|
||||
class LackOfPotential {
|
||||
async init() {
|
||||
new InitDatePicker(
|
||||
"#datepicker-lack-of-potential",
|
||||
this.handleChangedDate.bind(this)
|
||||
).init();
|
||||
this.bigTotalLackPotential = 0;
|
||||
this.totalPotensi = await this.getDataTotalPotensi(2025);
|
||||
this.totalPotensi = await this.getDataTotalPotensi("latest");
|
||||
this.totalTargetPAD = await this.getDataSettings("TARGET_PAD");
|
||||
this.allCountData = await this.getValueDashboard();
|
||||
this.reklameCount = this.allCountData.total_reklame ?? 0;
|
||||
this.pdamCount = this.allCountData.total_pdam ?? 0;
|
||||
this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0;
|
||||
|
||||
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
|
||||
this.bigTotalPotensi = new Big(this.totalPotensi.totalData ?? 0);
|
||||
this.bigTotalLackPotential = this.bigTargetPAD - this.bigTotalPotensi;
|
||||
this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0);
|
||||
this.bigTotalLackPotential = this.bigTargetPAD.minus(
|
||||
this.bigTotalPotensi
|
||||
);
|
||||
this.initChartKekuranganPotensi();
|
||||
this.initDataValueDashboard();
|
||||
}
|
||||
async handleChangedDate(filterDate) {
|
||||
const totalPotensi = await this.getDataTotalPotensi(filterDate);
|
||||
this.bigTotalPotensi = new Big(totalPotensi.total ?? 0);
|
||||
this.bigTotalLackPotential = this.bigTargetPAD.minus(
|
||||
this.bigTotalPotensi
|
||||
);
|
||||
|
||||
this.initChartKekuranganPotensi();
|
||||
}
|
||||
async getDataTotalPotensi(year) {
|
||||
async getDataTotalPotensi(filterDate) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/all-task-documents?year=${year}`,
|
||||
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterDate}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -34,8 +55,7 @@ class LackOfPotential {
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
countData: data.data.count,
|
||||
totalData: data.data.total,
|
||||
total: data.total_potensi.sum,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
@@ -45,7 +65,7 @@ class LackOfPotential {
|
||||
async getDataSettings(string_key) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/api-data-settings?search=${string_key}`,
|
||||
`${GlobalConfig.apiHost}/api/data-settings?search=${string_key}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -69,6 +89,33 @@ class LackOfPotential {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
async getValueDashboard() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/dashboard-potential-count`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
initChartKekuranganPotensi() {
|
||||
document
|
||||
.querySelectorAll(".document-count.chart-lack-of-potential")
|
||||
@@ -88,6 +135,12 @@ class LackOfPotential {
|
||||
element.innerText = ``;
|
||||
});
|
||||
}
|
||||
initDataValueDashboard() {
|
||||
document.getElementById("reklame-count").innerText = this.reklameCount;
|
||||
document.getElementById("pdam-count").innerText = this.pdamCount;
|
||||
document.getElementById("pbb-bangunan-count").innerText =
|
||||
this.tataRuangCount;
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", async function (e) {
|
||||
await new LackOfPotential().init();
|
||||
|
||||
@@ -17,35 +17,64 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
targetPadElement.textContent = formatCurrency(targetPadValue);
|
||||
|
||||
// Total Potensi Berkas
|
||||
const totalPotensiBerkas = document.getElementById("total-potensi-berkas");
|
||||
const totalPotensiBerkas = document.getElementById(
|
||||
"total-potensi-berkas"
|
||||
);
|
||||
if (!totalPotensiBerkas) return;
|
||||
const totalPotensiBerkasValue = await getDataTotalPotensi(selectedYear);
|
||||
totalPotensiBerkas.textContent = formatCurrency(totalPotensiBerkasValue.totalData);
|
||||
totalPotensiBerkas.textContent = formatCurrency(
|
||||
totalPotensiBerkasValue.totalData
|
||||
);
|
||||
|
||||
// Total Berkas Terverifikasi
|
||||
const totalBerkasTerverifikasi = document.getElementById("total-berkas-terverifikasi");
|
||||
const totalBerkasTerverifikasi = document.getElementById(
|
||||
"total-berkas-terverifikasi"
|
||||
);
|
||||
if (!totalBerkasTerverifikasi) return;
|
||||
const totalBerkasTerverifikasiValue = await getDataVerification(selectedYear);
|
||||
totalBerkasTerverifikasi.textContent = formatCurrency(totalBerkasTerverifikasiValue.totalData);
|
||||
const totalBerkasTerverifikasiValue = await getDataVerification(
|
||||
selectedYear
|
||||
);
|
||||
totalBerkasTerverifikasi.textContent = formatCurrency(
|
||||
totalBerkasTerverifikasiValue.totalData
|
||||
);
|
||||
|
||||
// Total Kekurangan potensi
|
||||
const totalKekuranganPotensi = document.getElementById("total-kekurangan-potensi");
|
||||
const totalKekuranganPotensi = document.getElementById(
|
||||
"total-kekurangan-potensi"
|
||||
);
|
||||
if (!totalKekuranganPotensi) return;
|
||||
const totalKekuranganPotensiValue = new Big(targetPadValue) - new Big(totalPotensiBerkasValue.totalData);
|
||||
totalKekuranganPotensi.textContent = formatCurrency(totalKekuranganPotensiValue)
|
||||
const totalKekuranganPotensiValue =
|
||||
new Big(targetPadValue) -
|
||||
new Big(totalPotensiBerkasValue.totalData);
|
||||
totalKekuranganPotensi.textContent = formatCurrency(
|
||||
totalKekuranganPotensiValue
|
||||
);
|
||||
|
||||
// Total Potensi PBG dari tata ruang
|
||||
const totalPotensiPBGTataRuang = document.getElementById("total-potensi-pbd-tata-ruang");
|
||||
const totalPotensiPBGTataRuang = document.getElementById(
|
||||
"total-potensi-pbd-tata-ruang"
|
||||
);
|
||||
if (!totalPotensiPBGTataRuang) return;
|
||||
const totalPotensiPBGTataRuangValue = await getDataSettings("TATA_RUANG");
|
||||
totalPotensiPBGTataRuang.textContent = formatCurrency(totalPotensiPBGTataRuangValue);
|
||||
const totalPotensiPBGTataRuangValue = await getDataSettings(
|
||||
"TATA_RUANG"
|
||||
);
|
||||
totalPotensiPBGTataRuang.textContent = formatCurrency(
|
||||
totalPotensiPBGTataRuangValue
|
||||
);
|
||||
|
||||
// Total Berkas Belum terverifikasi
|
||||
const totalBerkasBelumTerverifikasi = document.getElementById("total-berkas-belum-terverifikasi");
|
||||
const totalBerkasBelumTerverifikasi = document.getElementById(
|
||||
"total-berkas-belum-terverifikasi"
|
||||
);
|
||||
if (!totalBerkasBelumTerverifikasi) return;
|
||||
const totalBerkasBelumTerverifikasiValue = await getDataNonVerification(selectedYear);
|
||||
const totalBerkasBelumTerverifikasiCount = totalBerkasBelumTerverifikasiValue.countData;
|
||||
totalBerkasBelumTerverifikasi.textContent = formatCurrency(totalBerkasBelumTerverifikasiValue.totalData);
|
||||
const totalBerkasBelumTerverifikasiValue = await getDataNonVerification(
|
||||
selectedYear
|
||||
);
|
||||
const totalBerkasBelumTerverifikasiCount =
|
||||
totalBerkasBelumTerverifikasiValue.countData;
|
||||
totalBerkasBelumTerverifikasi.textContent = formatCurrency(
|
||||
totalBerkasBelumTerverifikasiValue.totalData
|
||||
);
|
||||
|
||||
// Total Berkas Usaha
|
||||
const totalBerkasUsahaValue = await getDataBusiness(selectedYear);
|
||||
@@ -58,21 +87,40 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
const totalBerkasNonUsahaTotalData = totalBerkasNonUsahaValue.totalData;
|
||||
|
||||
// Pie Chart Section
|
||||
let persenUsaha = totalBerkasBelumTerverifikasiCount > 0
|
||||
? ((totalBerkasUsahaCount / totalBerkasBelumTerverifikasiCount) * 100).toFixed(2)
|
||||
: "0";
|
||||
let persenUsaha =
|
||||
totalBerkasBelumTerverifikasiCount > 0
|
||||
? (
|
||||
(totalBerkasUsahaCount /
|
||||
totalBerkasBelumTerverifikasiCount) *
|
||||
100
|
||||
).toFixed(2)
|
||||
: "0";
|
||||
|
||||
let persenNonUsaha = totalBerkasBelumTerverifikasiCount > 0
|
||||
? ((totalBerkasNonUsahaCount / totalBerkasBelumTerverifikasiCount) * 100).toFixed(2)
|
||||
: "0";
|
||||
let persenNonUsaha =
|
||||
totalBerkasBelumTerverifikasiCount > 0
|
||||
? (
|
||||
(totalBerkasNonUsahaCount /
|
||||
totalBerkasBelumTerverifikasiCount) *
|
||||
100
|
||||
).toFixed(2)
|
||||
: "0";
|
||||
|
||||
const dataSeriesPieChart = [Number(persenUsaha), Number(persenNonUsaha)]
|
||||
const dataSeriesPieChart = [
|
||||
Number(persenUsaha),
|
||||
Number(persenNonUsaha),
|
||||
];
|
||||
const labelsPieChart = ["Berkas Usaha", "Berkas Non Usaha"];
|
||||
document.querySelector("td[data-category='non-usaha']").textContent = formatCurrency(totalBerkasNonUsahaTotalData).toLocaleString();
|
||||
document.querySelector("td[data-category='non-usaha-percentage']").textContent = persenNonUsaha + "%";
|
||||
document.querySelector("td[data-category='non-usaha']").textContent =
|
||||
formatCurrency(totalBerkasNonUsahaTotalData).toLocaleString();
|
||||
document.querySelector(
|
||||
"td[data-category='non-usaha-percentage']"
|
||||
).textContent = persenNonUsaha + "%";
|
||||
|
||||
document.querySelector("td[data-category='usaha']").textContent = formatCurrency(totalBerkasUsahaTotalData).toLocaleString();
|
||||
document.querySelector("td[data-category='usaha-percentage']").textContent = persenUsaha + "%";
|
||||
document.querySelector("td[data-category='usaha']").textContent =
|
||||
formatCurrency(totalBerkasUsahaTotalData).toLocaleString();
|
||||
document.querySelector(
|
||||
"td[data-category='usaha-percentage']"
|
||||
).textContent = persenUsaha + "%";
|
||||
|
||||
updatePieChart(dataSeriesPieChart, labelsPieChart);
|
||||
|
||||
@@ -81,27 +129,30 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
console.log(allLocation);
|
||||
|
||||
// Filter hanya data yang memiliki angka valid
|
||||
let validLocations = allLocation.dataLocation.filter(loc => {
|
||||
return !isNaN(parseFloat(loc.longitude)) && !isNaN(parseFloat(loc.latitude));
|
||||
let validLocations = allLocation.dataLocation.filter((loc) => {
|
||||
return (
|
||||
!isNaN(parseFloat(loc.longitude)) &&
|
||||
!isNaN(parseFloat(loc.latitude))
|
||||
);
|
||||
});
|
||||
|
||||
// Ubah string ke float
|
||||
validLocations = validLocations.map(loc => ({
|
||||
validLocations = validLocations.map((loc) => ({
|
||||
name: loc.project_name,
|
||||
longitude: parseFloat(loc.longitude),
|
||||
latitude: parseFloat(loc.latitude)
|
||||
latitude: parseFloat(loc.latitude),
|
||||
}));
|
||||
|
||||
console.log(validLocations.name)
|
||||
console.log(validLocations.name);
|
||||
|
||||
// Inisialisasi peta
|
||||
var map = L.map('map').setView([-7.0230, 107.5275], 10);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
var map = L.map("map").setView([-7.023, 107.5275], 10);
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
}).addTo(map);
|
||||
|
||||
// Tambahkan marker ke peta
|
||||
validLocations.forEach(function(loc) {
|
||||
validLocations.forEach(function (loc) {
|
||||
L.marker([loc.latitude, loc.longitude])
|
||||
.addTo(map)
|
||||
.bindPopup(`<b>${loc.name}</b>`) // Popup saat diklik
|
||||
@@ -109,22 +160,40 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
});
|
||||
|
||||
// Realisasi terbit PBG
|
||||
const totalRealisasiTerbitPBG = document.getElementById("realisasi-terbit-pbg");
|
||||
const totalRealisasiTerbitPBG = document.getElementById(
|
||||
"realisasi-terbit-pbg"
|
||||
);
|
||||
if (!totalRealisasiTerbitPBG) return;
|
||||
const totalRealisasiTerbitPBGValue = await getDataSettings("REALISASI_TERBIT_PBG_SUM");
|
||||
totalRealisasiTerbitPBG.textContent = formatCurrency(totalRealisasiTerbitPBGValue);
|
||||
const totalRealisasiTerbitPBGValue = await getDataSettings(
|
||||
"REALISASI_TERBIT_PBG_SUM"
|
||||
);
|
||||
totalRealisasiTerbitPBG.textContent = formatCurrency(
|
||||
totalRealisasiTerbitPBGValue
|
||||
);
|
||||
|
||||
// Menunggu Klik DPMPTSP
|
||||
const totalMenungguKlikDpmptsp = document.getElementById("waiting-click-dpmptsp");
|
||||
const totalMenungguKlikDpmptsp = document.getElementById(
|
||||
"waiting-click-dpmptsp"
|
||||
);
|
||||
if (!totalMenungguKlikDpmptsp) return;
|
||||
const totalMenungguKlikDpmptspValue = await getDataSettings("MENUNGGU_KLIK_DPMPTSP_SUM");
|
||||
totalMenungguKlikDpmptsp.textContent = formatCurrency(totalMenungguKlikDpmptspValue);
|
||||
const totalMenungguKlikDpmptspValue = await getDataSettings(
|
||||
"MENUNGGU_KLIK_DPMPTSP_SUM"
|
||||
);
|
||||
totalMenungguKlikDpmptsp.textContent = formatCurrency(
|
||||
totalMenungguKlikDpmptspValue
|
||||
);
|
||||
|
||||
// Proses Dinas Teknis
|
||||
const totalProsesDinasTeknis = document.getElementById("processing-technical-services");
|
||||
const totalProsesDinasTeknis = document.getElementById(
|
||||
"processing-technical-services"
|
||||
);
|
||||
if (!totalProsesDinasTeknis) return;
|
||||
const totalProsesDinasTeknisValue = await getDataSettings("PROSES_DINAS_TEKNIS_SUM");
|
||||
totalProsesDinasTeknis.textContent = formatCurrency(totalProsesDinasTeknisValue);
|
||||
const totalProsesDinasTeknisValue = await getDataSettings(
|
||||
"PROSES_DINAS_TEKNIS_SUM"
|
||||
);
|
||||
totalProsesDinasTeknis.textContent = formatCurrency(
|
||||
totalProsesDinasTeknisValue
|
||||
);
|
||||
|
||||
// Load Tabel Baru di Update
|
||||
const tableLastUpdated = new GeneralTable(
|
||||
@@ -172,25 +241,32 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
|
||||
tableSKPBGTerbit.init();
|
||||
|
||||
document.querySelector("#pbg-filter-by-updated-at .gridjs-search").hidden = true;
|
||||
document.querySelector("#pbg-filter-by-updated-at .gridjs-footer").hidden = true;
|
||||
document.querySelector("#pbg-filter-by-status .gridjs-search").hidden = true;
|
||||
document.querySelector("#pbg-filter-by-status .gridjs-footer").hidden = true;
|
||||
document.querySelector(
|
||||
"#pbg-filter-by-updated-at .gridjs-search"
|
||||
).hidden = true;
|
||||
document.querySelector(
|
||||
"#pbg-filter-by-updated-at .gridjs-footer"
|
||||
).hidden = true;
|
||||
document.querySelector(
|
||||
"#pbg-filter-by-status .gridjs-search"
|
||||
).hidden = true;
|
||||
document.querySelector(
|
||||
"#pbg-filter-by-status .gridjs-footer"
|
||||
).hidden = true;
|
||||
}
|
||||
|
||||
await updateDataByYear(yearPicker.value);
|
||||
|
||||
yearPicker.addEventListener("change", async function () {
|
||||
console.log("event change dropdown")
|
||||
console.log("event change dropdown");
|
||||
await updateDataByYear(yearPicker.value);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
async function getDataSettings(string_key) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/api-data-settings?search=${string_key}`,
|
||||
`${GlobalConfig.apiHost}/api/data-settings?search=${string_key}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -224,7 +300,7 @@ async function getDataTotalPotensi(year) {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']").content
|
||||
}`,
|
||||
"Content-Type": "application/json"
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -251,8 +327,7 @@ async function getDataVerification(year) {
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
document.querySelector("meta[name='api-token']").content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -263,26 +338,25 @@ async function getDataVerification(year) {
|
||||
console.error("Network response was not ok", response);
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const data = await response.json();
|
||||
return {
|
||||
totalData: data.data.total,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async function getDataNonVerification(year) {
|
||||
try {
|
||||
const response = await fetch (
|
||||
const response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/non-verification-documents?year=${year}`,
|
||||
{
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
document.querySelector("meta[name='api-token']").content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -297,7 +371,7 @@ async function getDataNonVerification(year) {
|
||||
return {
|
||||
countData: data.data.count,
|
||||
totalData: data.data.total,
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
@@ -312,8 +386,7 @@ async function getDataBusiness(year) {
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
document.querySelector("meta[name='api-token']").content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -343,8 +416,7 @@ async function getDataNonBusiness(year) {
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
document.querySelector("meta[name='api-token']").content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -357,8 +429,8 @@ async function getDataNonBusiness(year) {
|
||||
const data = await response.json();
|
||||
return {
|
||||
countData: data.data.count,
|
||||
totalData: data.data.total
|
||||
}
|
||||
totalData: data.data.total,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
@@ -373,8 +445,7 @@ async function getAllLocation() {
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${
|
||||
document.querySelector("meta[name='api-token']")
|
||||
.content
|
||||
document.querySelector("meta[name='api-token']").content
|
||||
}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -386,7 +457,7 @@ async function getAllLocation() {
|
||||
const data = await response.json();
|
||||
return {
|
||||
dataLocation: data.data,
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching chart data:", error);
|
||||
return 0;
|
||||
@@ -397,38 +468,40 @@ async function initChart() {
|
||||
var options = {
|
||||
chart: {
|
||||
height: 180,
|
||||
type: 'donut',
|
||||
type: "donut",
|
||||
},
|
||||
series: [0, 0], // Inisialisasi dengan nilai awal
|
||||
labels: ["Berkas Usaha", "Berkas Non Usaha"],
|
||||
legend: {
|
||||
show: false
|
||||
show: false,
|
||||
},
|
||||
stroke: {
|
||||
width: 0
|
||||
width: 0,
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: '60%',
|
||||
}
|
||||
}
|
||||
size: "60%",
|
||||
},
|
||||
},
|
||||
},
|
||||
colors: ["#7e67fe", "#17c553"],
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
enabled: false,
|
||||
},
|
||||
responsive: [{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: 200
|
||||
}
|
||||
}
|
||||
}],
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
fill: {
|
||||
type: 'gradient'
|
||||
}
|
||||
type: "gradient",
|
||||
},
|
||||
};
|
||||
|
||||
chart = new ApexCharts(document.querySelector("#conversions"), options);
|
||||
@@ -447,7 +520,7 @@ async function updatePieChart(dataSeries, labels) {
|
||||
// Perbarui label jika diperlukan
|
||||
if (Array.isArray(labels) && labels.length === dataSeries.length) {
|
||||
chart.updateOptions({
|
||||
labels: labels
|
||||
labels: labels,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -457,7 +530,7 @@ function formatCurrency(number) {
|
||||
return new Intl.NumberFormat("id-ID", {
|
||||
style: "currency",
|
||||
currency: "IDR",
|
||||
minimumFractionDigits: 0
|
||||
minimumFractionDigits: 0,
|
||||
}).format(number);
|
||||
}
|
||||
|
||||
@@ -467,4 +540,4 @@ const pbgTaskColumns = [
|
||||
"Nomor Registrasi",
|
||||
"Nomor Dokumen",
|
||||
"Alamat",
|
||||
]
|
||||
];
|
||||
|
||||
@@ -24,10 +24,9 @@ document.addEventListener("DOMContentLoaded", function (e) {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
@@ -35,7 +34,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
result.data.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/data-settings";
|
||||
@@ -46,6 +45,8 @@ document.addEventListener("DOMContentLoaded", function (e) {
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
submitButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
|
||||
@@ -2,46 +2,34 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||
import gridjs from "gridjs/dist/gridjs.umd.js";
|
||||
import "gridjs/dist/gridjs.umd.js";
|
||||
import GlobalConfig from "../global-config.js";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
class DataSettings {
|
||||
constructor() {
|
||||
this.table = null; // Store Grid.js instance
|
||||
this.toastMessage = document.getElementById("toast-message");
|
||||
this.toastElement = document.getElementById("toastNotification");
|
||||
this.toast = new bootstrap.Toast(this.toastElement);
|
||||
this.table = null;
|
||||
|
||||
// Initialize functions
|
||||
this.initTableDataSettings();
|
||||
this.initEvents();
|
||||
}
|
||||
init() {
|
||||
this.getFetchApiData();
|
||||
initEvents() {
|
||||
document.body.addEventListener("click", async (event) => {
|
||||
const deleteButton = event.target.closest(
|
||||
".btn-delete-data-settings"
|
||||
);
|
||||
if (deleteButton) {
|
||||
event.preventDefault();
|
||||
await this.handleDelete(deleteButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getFetchApiData() {
|
||||
initTableDataSettings() {
|
||||
let tableContainer = document.getElementById("table-data-settings");
|
||||
|
||||
if (this.table) {
|
||||
// If table exists, update its data instead of recreating
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-data-settings`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.key,
|
||||
item.value,
|
||||
item.created_at,
|
||||
item.id,
|
||||
]),
|
||||
total: (data) => data.meta.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new Grid.js instance only if it doesn't exist
|
||||
this.table = new Grid({
|
||||
columns: [
|
||||
"ID",
|
||||
@@ -65,11 +53,6 @@ class DataSettings {
|
||||
},
|
||||
},
|
||||
],
|
||||
search: {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
limit: 15,
|
||||
server: {
|
||||
@@ -80,8 +63,13 @@ class DataSettings {
|
||||
},
|
||||
},
|
||||
sort: true,
|
||||
search: {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-data-settings`,
|
||||
url: `${GlobalConfig.apiHost}/api/data-settings`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
@@ -99,94 +87,26 @@ class DataSettings {
|
||||
total: (data) => data.meta.total,
|
||||
},
|
||||
}).render(tableContainer);
|
||||
|
||||
document.addEventListener("click", this.handleDelete.bind(this));
|
||||
}
|
||||
handleDelete(event) {
|
||||
if (event.target.classList.contains("btn-delete-data-settings")) {
|
||||
event.preventDefault();
|
||||
const id = event.target.getAttribute("data-id");
|
||||
let modalElement = document.getElementById("modalConfirmation");
|
||||
let toastMessage = document.getElementById("toast-message");
|
||||
async handleDelete(deleteButton) {
|
||||
const id = deleteButton.getAttribute("data-id");
|
||||
|
||||
if (!modalElement) {
|
||||
console.error("Modal element not found!");
|
||||
return;
|
||||
}
|
||||
const result = await Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You won't be able to revert this!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
});
|
||||
|
||||
let modal = new bootstrap.Modal(modalElement);
|
||||
let btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
let toastElement = document.getElementById("toastNotification");
|
||||
let toast = new bootstrap.Toast(toastElement);
|
||||
|
||||
// Remove previous event listeners to avoid multiple bindings
|
||||
btnSaveConfirmation.replaceWith(
|
||||
btnSaveConfirmation.cloneNode(true)
|
||||
);
|
||||
btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
|
||||
// Set the role ID on the confirm button inside the modal
|
||||
btnSaveConfirmation.setAttribute("data-settings-id", id);
|
||||
|
||||
// Show the modal
|
||||
modal.show();
|
||||
|
||||
btnSaveConfirmation.addEventListener("click", async () => {
|
||||
let dataSettingId =
|
||||
btnSaveConfirmation.getAttribute("data-settings-id");
|
||||
|
||||
try {
|
||||
let response = await fetch(
|
||||
`/data-settings/${dataSettingId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
toast.show();
|
||||
|
||||
// Hide modal
|
||||
modal.hide();
|
||||
|
||||
// Refresh Grid.js table
|
||||
this.refreshDataSettings();
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
toastMessage.innerText = "An error occurred!";
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
refreshDataSettings() {
|
||||
if (this.table) {
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-data-settings`,
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
let response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/data-settings/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
@@ -194,21 +114,34 @@ class DataSettings {
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.key,
|
||||
item.value,
|
||||
item.created_at,
|
||||
item.id,
|
||||
]),
|
||||
total: (data) => data.meta.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
this.toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
this.toast.show();
|
||||
|
||||
// Refresh Grid.js table
|
||||
if (typeof this.table !== "undefined") {
|
||||
this.table.updateConfig({}).forceRender();
|
||||
}
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
this.toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
this.toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
this.toastMessage.innerText = "An error occurred!";
|
||||
this.toast.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new DataSettings().init();
|
||||
new DataSettings();
|
||||
});
|
||||
|
||||
@@ -24,16 +24,16 @@ document.addEventListener("DOMContentLoaded", function (e) {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText = result.message;
|
||||
toastMessage.innerText = result.data.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/data-settings";
|
||||
@@ -43,6 +43,8 @@ document.addEventListener("DOMContentLoaded", function (e) {
|
||||
toastMessage.innerText = error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
submitButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
|
||||
79
resources/js/maps/maps-kml.js
Normal file
79
resources/js/maps/maps-kml.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import L from "leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var map = L.map("map").setView([-6.9175, 107.6191], 10); // Bandung
|
||||
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
}).addTo(map);
|
||||
|
||||
// Dapatkan elemen loading
|
||||
const loadingDiv = document.getElementById("loading");
|
||||
loadingDiv.style.display = "flex"; // Tampilkan loading
|
||||
|
||||
fetch("/storage/maps/rencana-polaruang.geojson")
|
||||
.then((res) => res.json())
|
||||
.then((geojson) => {
|
||||
let colorMapping = {
|
||||
BJ: "rgb(235, 30, 30)",
|
||||
BA: "rgb(151, 219, 242)",
|
||||
CA: "rgb(70, 70, 165)",
|
||||
"P-2": "rgb(230, 255, 75)",
|
||||
HL: "rgb(50, 95, 40)",
|
||||
HPT: "rgb(75, 155, 55)",
|
||||
HP: "rgb(125, 180, 55)",
|
||||
W: "rgb(255, 165, 255)",
|
||||
PTL: "rgb(0, 255, 205)",
|
||||
"IK-2": "rgb(130, 185, 210)",
|
||||
"P-3": "rgb(175, 175, 55)",
|
||||
PS: "rgb(5, 215, 215)",
|
||||
PD: "rgb(235, 155, 60)",
|
||||
PK: "rgb(245, 155, 30)",
|
||||
HK: "rgb(155, 0, 255)",
|
||||
KPI: "rgb(105, 0, 0)",
|
||||
MBT: "rgb(95, 115, 145)",
|
||||
"P-4": "rgb(185, 235, 185)",
|
||||
TB: "rgb(70, 150, 255)",
|
||||
"P-1": "rgb(200, 245, 70)",
|
||||
TR: "rgb(215, 55, 0)",
|
||||
THR: "rgb(185, 165, 255)",
|
||||
TWA: "rgb(210, 190, 255)",
|
||||
};
|
||||
var geoLayer = L.geoJSON(geojson, {
|
||||
style: function (feature) {
|
||||
let htmlString = feature.properties.description.toString();
|
||||
|
||||
let match = htmlString.match(
|
||||
/<td>Kode Kawasan<\/td>\s*<td>(.*?)<\/td>/
|
||||
);
|
||||
|
||||
console.log("Kode Kawasan ", match[1]);
|
||||
|
||||
let color_code = match[1];
|
||||
|
||||
return {
|
||||
color: colorMapping[color_code],
|
||||
fillColor: colorMapping[color_code] || "#cccccc",
|
||||
fillOpacity: 0.6,
|
||||
weight: 1.5,
|
||||
};
|
||||
},
|
||||
onEachFeature: function (feature, layer) {
|
||||
if (feature.properties && feature.properties.name) {
|
||||
layer.bindPopup(feature.properties.name);
|
||||
}
|
||||
},
|
||||
}).addTo(map);
|
||||
|
||||
map.fitBounds(geoLayer.getBounds());
|
||||
|
||||
// Sembunyikan loading setelah selesai render
|
||||
loadingDiv.style.display = "none";
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error loading GeoJSON:", error);
|
||||
loadingDiv.innerHTML =
|
||||
"<div class='loading-text' style='background: red;'>Failed to load data!</div>";
|
||||
});
|
||||
});
|
||||
@@ -1,55 +1,67 @@
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
.getElementById("btnCreateMenus")
|
||||
.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
let spinner = document.getElementById("spinner");
|
||||
let form = document.getElementById("formCreateMenus");
|
||||
class CreateMenu {
|
||||
constructor() {
|
||||
this.initCreateMenu();
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
initCreateMenu() {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
.getElementById("btnCreateMenus")
|
||||
.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
let spinner = document.getElementById("spinner");
|
||||
let form = document.getElementById("formCreateMenus");
|
||||
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/menus";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/menus";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
submitButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new CreateMenu();
|
||||
});
|
||||
|
||||
@@ -2,13 +2,27 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||
import gridjs from "gridjs/dist/gridjs.umd.js";
|
||||
import "gridjs/dist/gridjs.umd.js";
|
||||
import GlobalConfig from "../global-config";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
class Menus {
|
||||
constructor() {
|
||||
this.toastMessage = document.getElementById("toast-message");
|
||||
this.toastElement = document.getElementById("toastNotification");
|
||||
this.toast = new bootstrap.Toast(this.toastElement);
|
||||
this.table = null;
|
||||
}
|
||||
init() {
|
||||
|
||||
// Initialize functions
|
||||
this.initTableMenus();
|
||||
this.initEvents();
|
||||
}
|
||||
initEvents() {
|
||||
document.body.addEventListener("click", async (event) => {
|
||||
const deleteButton = event.target.closest(".btn-delete-menu");
|
||||
if (deleteButton) {
|
||||
event.preventDefault();
|
||||
await this.handleDelete(deleteButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initTableMenus() {
|
||||
@@ -19,7 +33,7 @@ class Menus {
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-menus`,
|
||||
url: `${GlobalConfig.apiHost}/api/menus`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
@@ -60,7 +74,8 @@ class Menus {
|
||||
<a href="/menus/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
|
||||
<i class='bx bx-edit'></i>
|
||||
</a>
|
||||
<button data-id="${cell}" class="btn btn-red btn-sm btn-delete-menu d-inline-flex align-items-center justify-content-center">
|
||||
<button data-id="${cell}"
|
||||
class="btn btn-red btn-sm btn-delete-menu d-inline-flex align-items-center justify-content-center">
|
||||
<i class='bx bxs-trash' ></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -83,7 +98,7 @@ class Menus {
|
||||
},
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-menus`,
|
||||
url: `${GlobalConfig.apiHost}/api/menus`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
@@ -104,118 +119,63 @@ class Menus {
|
||||
total: (data) => data.total,
|
||||
},
|
||||
}).render(tableContainer);
|
||||
|
||||
document.addEventListener("click", this.handleDelete.bind(this));
|
||||
}
|
||||
|
||||
handleDelete(event) {
|
||||
if (event.target.classList.contains("btn-delete-menu")) {
|
||||
event.preventDefault();
|
||||
const id = event.target.getAttribute("data-id");
|
||||
let modalElement = document.getElementById("modalConfirmation");
|
||||
let toastMessage = document.getElementById("toast-message");
|
||||
async handleDelete(button) {
|
||||
const id = button.getAttribute("data-id");
|
||||
|
||||
if (!modalElement) {
|
||||
console.error("Modal element not found!");
|
||||
return;
|
||||
}
|
||||
const result = await Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You won't be able to revert this!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
});
|
||||
|
||||
let modal = new bootstrap.Modal(modalElement);
|
||||
let btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
let toastElement = document.getElementById("toastNotification");
|
||||
let toast = new bootstrap.Toast(toastElement);
|
||||
|
||||
// Remove previous event listeners to avoid multiple bindings
|
||||
btnSaveConfirmation.replaceWith(
|
||||
btnSaveConfirmation.cloneNode(true)
|
||||
);
|
||||
btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
|
||||
// Set the role ID on the confirm button inside the modal
|
||||
btnSaveConfirmation.setAttribute("data-menu-id", id);
|
||||
|
||||
// Show the modal
|
||||
modal.show();
|
||||
|
||||
btnSaveConfirmation.addEventListener("click", async () => {
|
||||
let menuId = btnSaveConfirmation.getAttribute("data-menu-id");
|
||||
|
||||
try {
|
||||
let response = await fetch(`/menus/${menuId}`, {
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
let response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/menus/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
toast.show();
|
||||
|
||||
// Hide modal
|
||||
modal.hide();
|
||||
|
||||
// Refresh Grid.js table
|
||||
this.refreshTableMenus();
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
toastMessage.innerText = "An error occurred!";
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
refreshTableMenus() {
|
||||
if (this.table) {
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-menus`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.name,
|
||||
item.url,
|
||||
item.icon,
|
||||
item.parent_id,
|
||||
item.sort_order,
|
||||
item.id,
|
||||
]),
|
||||
total: (data) => data.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
} else {
|
||||
this.initTableMenus(); // If no table exists, reinitialize it
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
this.toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
this.toast.show();
|
||||
|
||||
// Refresh Grid.js table
|
||||
if (typeof this.table !== "undefined") {
|
||||
this.table.updateConfig({}).forceRender();
|
||||
}
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
this.toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
this.toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
this.toastMessage.innerText = "An error occurred!";
|
||||
this.toast.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new Menus().init();
|
||||
new Menus();
|
||||
});
|
||||
|
||||
@@ -1,53 +1,67 @@
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
let form = document.getElementById("formUpdateMenus");
|
||||
let submitButton = document.getElementById("btnUpdateMenus");
|
||||
let spinner = document.getElementById("spinner");
|
||||
let toastMessage = document.getElementById("toast-message");
|
||||
let toast = new bootstrap.Toast(
|
||||
document.getElementById("toastNotification")
|
||||
);
|
||||
submitButton.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
class UpdateMenu {
|
||||
constructor() {
|
||||
this.initUpdateMenu();
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
initUpdateMenu() {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
.getElementById("btnUpdateMenus")
|
||||
.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
let spinner = document.getElementById("spinner");
|
||||
let form = document.getElementById("formUpdateMenus");
|
||||
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
},
|
||||
body: formData,
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/menus";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
submitButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText = result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/menus";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
toastMessage.innerText = error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
toastMessage.innerText = error.message;
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new UpdateMenu();
|
||||
});
|
||||
|
||||
@@ -1,55 +1,67 @@
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
.getElementById("btnCreateRole")
|
||||
.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
let spinner = document.getElementById("spinner");
|
||||
let form = document.getElementById("formCreateRole");
|
||||
class CreateRoles {
|
||||
constructor() {
|
||||
this.initCreateRole();
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
initCreateRole() {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
.getElementById("btnCreateRole")
|
||||
.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
let spinner = document.getElementById("spinner");
|
||||
let form = document.getElementById("formCreateRole");
|
||||
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/roles";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/roles";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
submitButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new CreateRoles();
|
||||
});
|
||||
|
||||
@@ -2,46 +2,31 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||
import gridjs from "gridjs/dist/gridjs.umd.js";
|
||||
import "gridjs/dist/gridjs.umd.js";
|
||||
import GlobalConfig from "../global-config";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
class Roles {
|
||||
constructor() {
|
||||
this.table = null; // Store Grid.js instance
|
||||
}
|
||||
this.toastMessage = document.getElementById("toast-message");
|
||||
this.toastElement = document.getElementById("toastNotification");
|
||||
this.toast = new bootstrap.Toast(this.toastElement);
|
||||
this.table = null;
|
||||
|
||||
init() {
|
||||
// Initialize functions
|
||||
this.initTableRoles();
|
||||
this.initEvents();
|
||||
}
|
||||
initEvents() {
|
||||
document.body.addEventListener("click", async (event) => {
|
||||
const deleteButton = event.target.closest(".btn-delete-role");
|
||||
if (deleteButton) {
|
||||
event.preventDefault();
|
||||
await this.handleDelete(deleteButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initTableRoles() {
|
||||
let tableContainer = document.getElementById("table-roles");
|
||||
|
||||
// If table instance already exists, update it instead of re-creating
|
||||
if (this.table) {
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-roles`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.name,
|
||||
item.description,
|
||||
item.id,
|
||||
]),
|
||||
total: (data) => data.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new Grid.js instance only if it doesn't exist
|
||||
this.table = new gridjs.Grid({
|
||||
columns: [
|
||||
@@ -82,7 +67,7 @@ class Roles {
|
||||
},
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-roles`,
|
||||
url: `${GlobalConfig.apiHost}/api/roles`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
@@ -100,116 +85,63 @@ class Roles {
|
||||
total: (data) => data.total,
|
||||
},
|
||||
}).render(tableContainer);
|
||||
|
||||
document.addEventListener("click", this.handleDelete.bind(this));
|
||||
}
|
||||
|
||||
handleDelete(event) {
|
||||
if (event.target.classList.contains("btn-delete-role")) {
|
||||
event.preventDefault();
|
||||
async handleDelete(deleteButton) {
|
||||
const id = deleteButton.getAttribute("data-id");
|
||||
|
||||
const id = event.target.getAttribute("data-id");
|
||||
let modalElement = document.getElementById("modalConfirmation");
|
||||
let toastMessage = document.getElementById("toast-message");
|
||||
const result = await Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You won't be able to revert this!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
});
|
||||
|
||||
if (!modalElement) {
|
||||
console.error("Modal element not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
let modal = new bootstrap.Modal(modalElement);
|
||||
let btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
let toastElement = document.getElementById("toastNotification");
|
||||
let toast = new bootstrap.Toast(toastElement);
|
||||
|
||||
// Remove previous event listeners to avoid multiple bindings
|
||||
btnSaveConfirmation.replaceWith(
|
||||
btnSaveConfirmation.cloneNode(true)
|
||||
);
|
||||
btnSaveConfirmation = document.getElementById(
|
||||
"btnSaveConfirmation"
|
||||
);
|
||||
|
||||
// Set the role ID on the confirm button inside the modal
|
||||
btnSaveConfirmation.setAttribute("data-role-id", id);
|
||||
|
||||
// Show the modal
|
||||
modal.show();
|
||||
|
||||
btnSaveConfirmation.addEventListener("click", async () => {
|
||||
let roleId = btnSaveConfirmation.getAttribute("data-role-id");
|
||||
|
||||
try {
|
||||
let response = await fetch(`/roles/${roleId}`, {
|
||||
if (result.isConfirmed) {
|
||||
try {
|
||||
let response = await fetch(
|
||||
`${GlobalConfig.apiHost}/api/roles/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
toast.show();
|
||||
|
||||
// Hide modal
|
||||
modal.hide();
|
||||
|
||||
// Refresh Grid.js table
|
||||
this.refreshRolesTable();
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
toastMessage.innerText = "An error occurred!";
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
refreshRolesTable() {
|
||||
if (this.table) {
|
||||
this.table
|
||||
.updateConfig({
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-roles`,
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
then: (data) =>
|
||||
data.data.map((item) => [
|
||||
item.id,
|
||||
item.name,
|
||||
item.description,
|
||||
item.id,
|
||||
]),
|
||||
total: (data) => data.total,
|
||||
},
|
||||
})
|
||||
.forceRender();
|
||||
} else {
|
||||
this.initTableRoles(); // If the table is null, reinitialize
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
this.toastMessage.innerText =
|
||||
result.message || "Deleted successfully!";
|
||||
this.toast.show();
|
||||
|
||||
// Refresh Grid.js table
|
||||
if (typeof this.table !== "undefined") {
|
||||
this.table.updateConfig({}).forceRender();
|
||||
}
|
||||
} else {
|
||||
let error = await response.json();
|
||||
console.error("Delete failed:", error);
|
||||
this.toastMessage.innerText =
|
||||
error.message || "Delete failed!";
|
||||
this.toast.show();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting item:", error);
|
||||
this.toastMessage.innerText = "An error occurred!";
|
||||
this.toast.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new Roles().init();
|
||||
new Roles();
|
||||
});
|
||||
|
||||
@@ -1,53 +1,67 @@
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
let form = document.getElementById("formUpdateRole");
|
||||
let submitButton = document.getElementById("btnUpdateRole");
|
||||
let spinner = document.getElementById("spinner");
|
||||
let toastMessage = document.getElementById("toast-message");
|
||||
let toast = new bootstrap.Toast(
|
||||
document.getElementById("toastNotification")
|
||||
);
|
||||
submitButton.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
class UpdateRoles {
|
||||
constructor() {
|
||||
this.initUpdateRole();
|
||||
}
|
||||
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
initUpdateRole() {
|
||||
const toastNotification = document.getElementById("toastNotification");
|
||||
const toast = new bootstrap.Toast(toastNotification);
|
||||
document
|
||||
.getElementById("btnUpdateRole")
|
||||
.addEventListener("click", async function () {
|
||||
let submitButton = this;
|
||||
let spinner = document.getElementById("spinner");
|
||||
let form = document.getElementById("formUpdateRole");
|
||||
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
if (!form) {
|
||||
console.error("Form element not found!");
|
||||
return;
|
||||
}
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": document
|
||||
.querySelector('meta[name="csrf-token"]')
|
||||
.getAttribute("content"),
|
||||
},
|
||||
body: formData,
|
||||
// Disable button and show spinner
|
||||
submitButton.disabled = true;
|
||||
spinner.classList.remove("d-none");
|
||||
|
||||
try {
|
||||
let response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
.getAttribute("content")}`,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/roles";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
submitButton.disabled = false;
|
||||
spinner.classList.add("d-none");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
document.getElementById("toast-message").innerText =
|
||||
error.message;
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
let result = await response.json();
|
||||
toastMessage.innerText = result.message;
|
||||
toast.show();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/roles";
|
||||
}, 2000);
|
||||
} else {
|
||||
let error = await response.json();
|
||||
toastMessage.innerText = error.message;
|
||||
toast.show();
|
||||
console.error("Error:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Request failed:", error);
|
||||
toastMessage.innerText = error.message;
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
new UpdateRoles();
|
||||
});
|
||||
|
||||
34
resources/js/utils/InitDatePicker.js
Normal file
34
resources/js/utils/InitDatePicker.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import flatpickr from "flatpickr";
|
||||
import "flatpickr/dist/flatpickr.min.css";
|
||||
|
||||
class InitDatePicker {
|
||||
constructor(selector = ".datepicker", onChangeCallback = null) {
|
||||
this.selector = selector;
|
||||
this.onChangeCallback = onChangeCallback;
|
||||
}
|
||||
|
||||
init() {
|
||||
const today = new Date();
|
||||
|
||||
document.querySelectorAll(this.selector).forEach((element) => {
|
||||
flatpickr(element, {
|
||||
enableTime: false,
|
||||
dateFormat: "Y-m-d",
|
||||
maxDate: today,
|
||||
onChange: (selectedDates, dateStr) => {
|
||||
if (this.onChangeCallback) {
|
||||
this.onChangeCallback(dateStr); // Call callback with selected date
|
||||
}
|
||||
},
|
||||
onReady: (selectedDates, dateStr, instance) => {
|
||||
// Call the callback with the default date when initialized
|
||||
if (this.onChangeCallback && dateStr) {
|
||||
this.onChangeCallback(dateStr);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default InitDatePicker;
|
||||
@@ -21,8 +21,8 @@
|
||||
// overflow: hidden;
|
||||
|
||||
.circle-content {
|
||||
width: 180px; /* Ukuran lingkaran dalam */
|
||||
height: 180px;
|
||||
min-width: 180px; /* Ukuran lingkaran dalam */
|
||||
min-height: 180px;
|
||||
background-color: var(--circle-color); /* Warna lingkaran dalam */
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
@@ -57,17 +57,20 @@
|
||||
padding: 0 7px;
|
||||
border-radius: 10px;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.circle-content .document-count {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.circle-content .document-type {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.small-circle-container {
|
||||
|
||||
27
resources/views/bigdata-resumes/index.blade.php
Normal file
27
resources/views/bigdata-resumes/index.blade.php
Normal file
@@ -0,0 +1,27 @@
|
||||
@extends('layouts.vertical', ['subtitle' => 'Laporan Pimpinan'])
|
||||
|
||||
@section('css')
|
||||
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
@include('layouts.partials/page-title', ['title' => 'Laporan', 'subtitle' => 'Laporan Pimpinan'])
|
||||
|
||||
<x-toast-notification />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card w-100">
|
||||
<div class="card-body">
|
||||
<div id="table-bigdata-resumes"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
@vite(['resources/js/bigdata-resumes/index.js'])
|
||||
@endsection
|
||||
@@ -64,7 +64,10 @@
|
||||
<!-- end dropzon-preview -->
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button id="submit-upload" class="btn btn-primary">Upload Files</button>
|
||||
<button type="button" class="btn btn-primary" id="btnUploadBusinessIndustry">
|
||||
<span id="spinner" class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
</div> <!-- end card body -->
|
||||
</div> <!-- end card -->
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'Business Industries'])
|
||||
|
||||
<x-toast-notification />
|
||||
<x-modal-confirmation buttonText="Delete" confirmationMessage="Are you sure you want to delete this?" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card w-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap justify-content-end align-items-center mb-2">
|
||||
<a href="{{ route('business-industries.create')}}" class="btn btn-success btn-sm d-block d-sm-inline w-auto">Create</a>
|
||||
<a href="{{ route('business-industries.create')}}" class="btn btn-success btn-sm d-block d-sm-inline w-auto">Upload</a>
|
||||
</div>
|
||||
<div id="table-business-industries"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@props(['title' => 'title component', 'visible_data' => false, 'data' => 'data text', 'visible_data_type' => false,
|
||||
'data_type' => '','style' => '', 'size' => '', 'line' => []])
|
||||
@props(['title' => 'title component', 'visible_data' => false, 'data_count' => '', 'visible_data_type' => false,
|
||||
'data_type' => '','style' => '', 'size' => '', 'line' => [], 'data_id' => ''])
|
||||
|
||||
@section('css')
|
||||
@vite(['resources/scss/components/_custom_circle.scss'])
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="custom-circle-content">
|
||||
<p class="custom-circle-text">{{ $title }}</p>
|
||||
@if ($visible_data === "true")
|
||||
<div class="custom-circle-data">{{ $data }}</div>
|
||||
<div class="custom-circle-data" id="{{ $data_id }}">{{ $data_count }}</div>
|
||||
@endif
|
||||
@if ($visible_data_type === "true")
|
||||
<div class="custom-circle-data-type">{{ $data_type }}</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@include('layouts.partials/page-title', ['title' => 'Dashboards', 'subtitle' => 'Dashboard Pimpinan'])
|
||||
|
||||
<div id="dashboard-fixed-wrapper" class="row">
|
||||
<div class="col-12">
|
||||
<!-- <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>
|
||||
@@ -20,13 +20,20 @@
|
||||
|
||||
<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 gap-2">
|
||||
<select class="form-select w-auto" id="yearPicker" name="year" style="min-width: 100px;">
|
||||
@for ($i = date('Y'); $i > date('Y') - 5; $i--)
|
||||
<option value="{{ $i }}" {{ $i == date('Y') ? 'selected' : '' }}>{{ $i }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
<button class="btn btn-sm btn-primary" id="btnFilterYear">Filter</button>
|
||||
<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">
|
||||
ANALISA BIG DATA PROSES PBG <br>
|
||||
MELALUI APLIKASI SIBEDAS PBG
|
||||
</h2>
|
||||
<div class="text-black text-end d-flex flex-column align-items-end me-3">
|
||||
<span class="fs-5">Terakhir di update - {{$latest_created}}</span>
|
||||
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-dashboard-bigdata" placeholder="Filter Date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,7 +49,7 @@
|
||||
@endcomponent
|
||||
|
||||
@component('components.circle', [
|
||||
'document_title' => 'Target PAD 2024',
|
||||
'document_title' => 'Target PAD',
|
||||
'document_color' => '#204f6b',
|
||||
'document_type' => '',
|
||||
'document_id' => 'chart-target-pad',
|
||||
|
||||
@@ -9,106 +9,105 @@
|
||||
|
||||
<div class="lack-of-potential-wrapper">
|
||||
<div class="row" id="lack-of-potential-wrapper">
|
||||
<div class="col-12">
|
||||
<h3 class="mt-3 ms-2 text-danger">
|
||||
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
|
||||
<h2 class="text-danger m-0">
|
||||
ANALISA BIG DATA MELALUI APLIKASI SIBEDAS PBG
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="">
|
||||
<div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
|
||||
<div style="position: absolute; top: 200px; left: 50px;">
|
||||
<x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div>
|
||||
<x-custom-circle title="PBB Bangunan" visible_data="true" data="649.157" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div>
|
||||
<x-custom-circle title="Reklame" visible_data="true" data="2.428" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square dia-top-right-bottom-left" style="top:140px;left:50px;width:150px;height:120px;"></div>
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 300px; left: 200px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-100px;left:30px;width:150px;height:120px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-100px;left:120px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="BAPENDA" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="PDAM" visible_data="true" data="9.022" visible_data_type="true" data_type="Pelanggan" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="KECAMATAN" size="small" style="float:left;background-color: #234f6c;" />
|
||||
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 0px; left: 270px;">
|
||||
<div class="square" style="width:5px;height:600px;background-color:black;left:70px;top:50px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:350px;left:-50px;width:120px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:350px;left:70px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="Rumah Tinggal" size="small" style="background-color: #234f6c;margin:auto;" />
|
||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:150px;" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 650px; left: 110px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:40px;width:200px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div>
|
||||
<x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" />
|
||||
<div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div>
|
||||
<x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 280px; left: 550px;">
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:-150px;width:200px;height:180px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:70px;left:-150px;width:200px;height:130px;"></div>
|
||||
<x-custom-circle title="Tim Wasdal Gabungan" size="large" style="background-color: #da6635;float:left" />
|
||||
<div class="square" style="width:650px;height:5px;background-color:black;left:100px;top:75px;"></div>
|
||||
@component('components.circle', [
|
||||
'document_title' => 'Kekurangan Potensi',
|
||||
'document_color' => '#ff5757',
|
||||
'document_type' => '',
|
||||
'document_id' => 'chart-lack-of-potential',
|
||||
'visible_small_circle' => false,
|
||||
'style' => 'margin-left:180px;top:-20px;'
|
||||
])
|
||||
@endcomponent
|
||||
|
||||
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" />
|
||||
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 310px; left: 1150px;">
|
||||
<div class="square dia-top-left-bottom-right" style="top:90px;left:-100px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div>
|
||||
<x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" />
|
||||
<x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" />
|
||||
|
||||
</div>
|
||||
|
||||
<x-custom-circle title="UUCK" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" />
|
||||
|
||||
<div style="position: absolute; top: 470px; left: 430px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-80px;left:20px;width:150px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-50px;left:100px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-50px;left:180px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-60px;left:240px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="UPT Wasdal" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="Satpol PP" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="KEJARI" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="TNI & POLRI" size="small" style="background-color: #0f4853;float:left;" />
|
||||
</div>
|
||||
|
||||
<x-custom-circle title="UUCK" size="small" style="background-color: #2390af;position:absolute;left:980px;top:500px;" />
|
||||
|
||||
<div style="position: absolute; top: 50px; left: 1100px;">
|
||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" />
|
||||
</div>
|
||||
</h2>
|
||||
<div class="text-black text-end d-flex flex-column align-items-end me-3">
|
||||
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-lack-of-potential" placeholder="Filter Date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
|
||||
<div style="position: absolute; top: 200px; left: 50px;">
|
||||
<x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div>
|
||||
<x-custom-circle title="PBB Bangunan" visible_data="true" data_id="pbb-bangunan-count" data_count="0" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div>
|
||||
<x-custom-circle title="Reklame" visible_data="true" data_id="reklame-count" data_count="0" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square dia-top-right-bottom-left" style="top:140px;left:50px;width:150px;height:120px;"></div>
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 300px; left: 200px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-100px;left:30px;width:150px;height:120px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-100px;left:120px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="BAPENDA" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="PDAM" visible_data="true" data_id="pdam-count" data_count="0" visible_data_type="true" data_type="Pelanggan" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="KECAMATAN" size="small" style="float:left;background-color: #234f6c;" />
|
||||
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 0px; left: 270px;">
|
||||
<div class="square" style="width:5px;height:600px;background-color:black;left:70px;top:50px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:350px;left:-50px;width:120px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:350px;left:70px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="Rumah Tinggal" size="small" style="background-color: #234f6c;margin:auto;" />
|
||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:150px;" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 650px; left: 110px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:40px;width:200px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div>
|
||||
<x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" />
|
||||
<div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div>
|
||||
<x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 280px; left: 550px;">
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:-150px;width:200px;height:180px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:70px;left:-150px;width:200px;height:130px;"></div>
|
||||
<x-custom-circle title="Tim Wasdal Gabungan" size="large" style="background-color: #da6635;float:left" />
|
||||
<div class="square" style="width:650px;height:5px;background-color:black;left:100px;top:75px;"></div>
|
||||
@component('components.circle', [
|
||||
'document_title' => 'Kekurangan Potensi',
|
||||
'document_color' => '#ff5757',
|
||||
'document_type' => '',
|
||||
'document_id' => 'chart-lack-of-potential',
|
||||
'visible_small_circle' => false,
|
||||
'style' => 'margin-left:180px;top:-20px;'
|
||||
])
|
||||
@endcomponent
|
||||
|
||||
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" />
|
||||
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 310px; left: 1150px;">
|
||||
<div class="square dia-top-left-bottom-right" style="top:90px;left:-100px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div>
|
||||
<x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" />
|
||||
<x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" />
|
||||
|
||||
</div>
|
||||
|
||||
<x-custom-circle title="BPN" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" />
|
||||
|
||||
<div style="position: absolute; top: 470px; left: 430px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-80px;left:20px;width:150px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-50px;left:100px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-50px;left:180px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-60px;left:240px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="UPT Wasdal" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="Satpol PP" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="KEJARI" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="TNI & POLRI" size="small" style="background-color: #0f4853;float:left;" />
|
||||
</div>
|
||||
|
||||
<x-custom-circle title="UUCK" size="small" style="background-color: #2390af;position:absolute;left:980px;top:500px;" />
|
||||
|
||||
<div style="position: absolute; top: 50px; left: 1100px;">
|
||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('data-settings.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="formDataSettings" action="{{ route('data-settings.store') }}" method="POST">
|
||||
<form id="formDataSettings" action="{{ route('api.data-settings.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="key" class="form-label">Key</label>
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('data-settings.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="formUpdateDataSettings" action="{{ route('data-settings.update', $data->id) }}" method="POST">
|
||||
<form id="formUpdateDataSettings" action="{{ route('api.data-settings.update', $data->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-3">
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
@include('layouts.partials/page-title', ['title' => 'Data Settings', 'subtitle' => 'Setting Dashboard'])
|
||||
|
||||
<x-toast-notification />
|
||||
<x-modal-confirmation buttonText="Delete" confirmationMessage="Are you sure you want to delete this?" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
|
||||
41
resources/views/maps/google-api.blade.php
Normal file
41
resources/views/maps/google-api.blade.php
Normal file
@@ -0,0 +1,41 @@
|
||||
@extends('layouts.vertical', ['subtitle' => 'Google Maps'])
|
||||
|
||||
@section('css')
|
||||
<style>
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5); /* Latar belakang gelap transparan */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
background: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@include('layouts.partials.page-title', ['title' => 'Maps', 'subtitle' => 'Google Maps'])
|
||||
<!-- Elemen loading -->
|
||||
<div id="loading" class="loading-overlay">
|
||||
<div class="loading-text">Loading data...</div>
|
||||
</div>
|
||||
|
||||
<!-- Peta -->
|
||||
<div id="map" style="width: 100%; height: 90vh;"></div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
@vite(['resources/js/maps/maps-kml.js'])
|
||||
@endsection
|
||||
@@ -12,8 +12,11 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('menus.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="formCreateMenus" action="{{route("menus.store")}}" method="post">
|
||||
<form id="formCreateMenus" action="{{route("api.menus.store")}}" method="post">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('menus.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="formUpdateMenus" action="{{route("menus.update", $menu->id)}}" method="post">
|
||||
<form id="formUpdateMenus" action="{{route("api.menus.update", $menu->id)}}" method="post">
|
||||
@csrf
|
||||
@method("put")
|
||||
<div class="mb-3">
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('roles.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{route("roles.store")}}" method="post" id="formCreateRole" data-redirect="{{route("roles.index")}}">
|
||||
<form action="{{route("api.roles.store")}}" method="post" id="formCreateRole" data-redirect="{{route("roles.index")}}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('roles.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="formUpdateRole" action="{{route("roles.update", $role->id)}}" method="post" >
|
||||
<form id="formUpdateRole" action="{{route("api.roles.update", $role->id)}}" method="post" >
|
||||
@csrf
|
||||
@method("put")
|
||||
<div class="mb-3">
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-end">
|
||||
<a href="{{ route('roles.index') }}" class="btn btn-sm btn-secondary">Back</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>Manage Permissions for Role: {{ $role->name }}</h5>
|
||||
<form action="{{route("role-menu.permission.update", $role->id)}}" method="post">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\BigDataResumeController;
|
||||
use App\Http\Controllers\Api\BusinessOrIndustriesController;
|
||||
use App\Http\Controllers\Api\CustomersController;
|
||||
use App\Http\Controllers\Api\DashboardController;
|
||||
@@ -7,6 +8,7 @@ use App\Http\Controllers\Api\DataSettingController;
|
||||
use App\Http\Controllers\Api\GlobalSettingsController;
|
||||
use App\Http\Controllers\Api\GoogleSheetController;
|
||||
use App\Http\Controllers\Api\ImportDatasourceController;
|
||||
use App\Http\Controllers\Api\LackOfPotentialController;
|
||||
use App\Http\Controllers\Api\MenusController;
|
||||
use App\Http\Controllers\Api\PbgTaskController;
|
||||
use App\Http\Controllers\Api\RequestAssignmentController;
|
||||
@@ -80,7 +82,13 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
||||
Route::get('/download-template-spatialPlannings', [SpatialPlanningController::class, 'downloadExcelSpatialPlanning']);
|
||||
|
||||
// data-settings
|
||||
Route::apiResource('/api-data-settings', DataSettingController::class);
|
||||
// Route::apiResource('/api-data-settings', DataSettingController::class);
|
||||
Route::controller(DataSettingController::class)->group(function (){
|
||||
Route::get('/data-settings', 'index')->name('api.data-settings');
|
||||
Route::post('/data-settings', 'store')->name('api.data-settings.store');
|
||||
Route::put('/data-settings/{data_setting_id}', 'update')->name('api.data-settings.update');
|
||||
Route::delete('/data-settings/{data_setting_id}', 'destroy')->name('api.data-settings.destroy');
|
||||
});
|
||||
|
||||
Route::apiResource('/api-pbg-task', PbgTaskController::class);
|
||||
|
||||
@@ -93,10 +101,20 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
||||
Route::get('/sync-task-submit/{uuid}', [SyncronizeController::class, 'syncTaskDetailSubmit'])->name('api.task.submit');
|
||||
|
||||
// menus api
|
||||
Route::apiResource('api-menus', MenusController::class);
|
||||
Route::controller(MenusController::class)->group(function (){
|
||||
Route::get('/menus', 'index')->name('api.menus');
|
||||
Route::post('/menus', 'store')->name('api.menus.store');
|
||||
Route::put('/menus/{menu_id}', 'update')->name('api.menus.update');
|
||||
Route::delete('/menus/{menu_id}', 'destroy')->name('api.menus.destroy');
|
||||
});
|
||||
|
||||
// roles api
|
||||
Route::apiResource('api-roles', RolesController::class);
|
||||
Route::controller(RolesController::class)->group(function (){
|
||||
Route::get('/roles', 'index')->name('api.roles');
|
||||
Route::post('/roles', 'store')->name('api.roles.store');
|
||||
Route::put('/roles/{role_id}', 'update')->name('api.roles.update');
|
||||
Route::delete('/roles/{role_id}', 'destroy')->name('api.roles.destroy');
|
||||
});
|
||||
|
||||
//business industries api
|
||||
Route::apiResource('api-business-industries', BusinessOrIndustriesController::class);
|
||||
@@ -109,4 +127,13 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
||||
Route::delete('/customers/{id}', 'destroy')->name('api.customers.destroy');
|
||||
Route::post('/customers/upload', 'upload')->name('api.customers.upload');
|
||||
});
|
||||
|
||||
//dashboard potensi
|
||||
Route::get('/dashboard-potential-count', [LackOfPotentialController::class, 'count_lack_of_potential'])->name('api.count-dashboard-potential');
|
||||
|
||||
// big data resume
|
||||
Route::controller(BigDataResumeController::class)->group(function (){
|
||||
Route::get('/bigdata-resume', 'index')->name('api.bigdata-resume');
|
||||
Route::get('/bigdata-report', 'bigdata_report')->name('api.bigdata-report');
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\BigdataResumesController;
|
||||
use App\Http\Controllers\BusinessOrIndustriesController;
|
||||
use App\Http\Controllers\CustomersController;
|
||||
use App\Http\Controllers\Dashboards\LackOfPotentialController;
|
||||
use App\Http\Controllers\DataSettingController;
|
||||
use App\Http\Controllers\Dashboards\BigDataController;
|
||||
use App\Http\Controllers\GoogleApisController;
|
||||
use App\Http\Controllers\Home\HomeController;
|
||||
use App\Http\Controllers\Master\UsersController;
|
||||
use App\Http\Controllers\MenusController;
|
||||
@@ -32,6 +34,7 @@ Route::group(['middleware' => 'auth'], function(){
|
||||
Route::get('/bigdata', [BigDataController::class, 'index'])->name('dashboard.home');
|
||||
Route::get('/dashboard-pbg', [BigDataController::class, 'pbg'])->name('dashboard.pbg');
|
||||
Route::get('/lack-of-potential', [LackOfPotentialController::class, 'lack_of_potential'])->name('dashboard.lack_of_potential');
|
||||
Route::get('/maps', [GoogleApisController::class, 'index'])->name('dashboard.maps');
|
||||
});
|
||||
|
||||
// settings
|
||||
@@ -106,5 +109,9 @@ Route::group(['middleware' => 'auth'], function(){
|
||||
Route::group(['prefix' => '/report'], function(){
|
||||
// Resource route, kecuali create karena dibuat terpisah
|
||||
Route::resource('/tourisms-report', ReportTourismController::class);
|
||||
|
||||
Route::controller(BigdataResumesController::class)->group(function (){
|
||||
Route::get('/bigdata-resumes', 'index')->name('bigdata-resumes');
|
||||
});
|
||||
});
|
||||
});
|
||||
185
sibedas.sql
185
sibedas.sql
File diff suppressed because one or more lines are too long
@@ -3,10 +3,6 @@ import laravel from "laravel-vite-plugin";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: "public/build",
|
||||
manifest: true, // Menghasilkan manifest.json untuk Laravel
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "resources/js"),
|
||||
@@ -98,6 +94,9 @@ export default defineConfig({
|
||||
"resources/js/customers/index.js",
|
||||
"resources/js/customers/create.js",
|
||||
"resources/js/customers/edit.js",
|
||||
"resources/js/dashboards/pbg.js",
|
||||
// maps
|
||||
"resources/js/maps/maps-kml.js",
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user