Compare commits

...

36 Commits

Author SHA1 Message Date
arifal
f9e1aa1604 fix service scraping data 2025-03-18 06:42:42 +07:00
arifal
2e385f80cd fix optimize syncronize 2025-03-14 19:10:28 +07:00
arifal
e2c26e0eff fix reklame route index 2025-03-13 16:23:17 +07:00
arifal
ca5b8ad403 fix deploy code and readme 2025-03-13 15:59:11 +07:00
arifal
e97b7eb70d fix redirect back spatial plannings 2025-03-13 15:48:53 +07:00
arifal
0258ca9f04 fix redirect back umkm 2025-03-13 15:39:32 +07:00
arifal
0116147e06 fix redirect back reklame and tourisms 2025-03-13 15:24:59 +07:00
arifal
7787db02a3 fix redirect back business or industries 2025-03-13 14:13:06 +07:00
arifal
e47ab36d5e fix pdam redirect back and advertisement partial update redirect 2025-03-13 13:57:53 +07:00
arifal
e5db2294b4 fix redirect back data settings 2025-03-12 21:39:52 +07:00
arifal
4db457d7bd fix redirect back users crud 2025-03-12 21:17:49 +07:00
arifal
7a82ad5302 fix redirect back roles 2025-03-12 20:30:16 +07:00
arifal
b0d4d4c23b fix redirect back with params menu id 2025-03-12 17:52:11 +07:00
arifal
68ffc1c090 fix 503 page 2025-03-12 15:57:31 +07:00
arifal
a1f4bd7f81 fix seeder menu role and user assign, create 503 page and fix redirect home 2025-03-12 15:37:22 +07:00
arifal
238aaba96c fix seeder users role menu 2025-03-12 12:01:28 +07:00
arifal
c7152d9dbe fix resources report payment recaps 2025-03-12 11:50:56 +07:00
arifal
2a4b96d0b2 fix vite resources 2025-03-12 11:43:11 +07:00
arifal
dd940ebdb6 fix inside and outside dashboard 2025-03-12 11:38:07 +07:00
arifal
dce5409248 done all view dummy new modul 2025-03-12 00:32:50 +07:00
arifal
b8f7d7f655 fix back button 2025-03-11 01:17:16 +07:00
arifal
65600f1b4f add view list data from google sheet 2025-03-11 00:26:48 +07:00
arifal
b0f15a9221 fix sidebar permission user 2025-03-10 16:33:53 +07:00
arifal
bf55eb228e fix width dashboard outside sy system and loading when load maps pariwisata, backup localdb 2025-03-07 18:00:30 +07:00
arifal
755720bac9 fix failed build images from leaflet 2025-03-07 16:41:52 +07:00
arifal
098b4c605b fix missing scss on vite 2025-03-07 15:39:26 +07:00
arifal
4632e102eb add use package missing 2025-03-07 15:00:26 +07:00
arifal
0431945a42 fix conflict 2025-03-07 14:52:51 +07:00
arifal
c529a5d511 add readme 2025-03-07 14:39:23 +07:00
arifal
ff244039ff add readme and env example 2025-03-07 14:37:26 +07:00
arifal
55902042f4 add readme 2025-03-07 14:27:49 +07:00
arifal
c67aa979c2 change column type expertise and fix syncronize simbg service 2025-03-07 14:04:37 +07:00
arifal
fbaa33ae13 fix height scrollbar table grid js 2025-03-07 01:55:46 +07:00
arifal
9516b6f575 fix task assignment table on pbg task 2025-03-07 01:03:22 +07:00
arifal
ffc08f26cc add dashboard inside and outside system and fix timeout when search filter 2025-03-06 23:33:31 +07:00
arifal
e0c35b8897 fix search filter on page big data resume 2025-03-06 14:39:36 +07:00
169 changed files with 5718 additions and 1775 deletions

View File

@@ -67,4 +67,5 @@ AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
API_KEY_GOOGLE="xxxxx"
SPREAD_SHEET_ID="xxxxx"
SPREAD_SHEET_ID="xxxxx"
OPENAI_API_KEY="xxxxx"

View File

@@ -17,13 +17,14 @@ sudo nano /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path-to-your-project/artisan queue:work --tries=3 --timeout=600
command=php /home/arifal/development/sibedas-pbg-web/artisan queue:work --queue=default --timeout=40000 --tries=1 --sleep=3
autostart=true
autorestart=true
numprocs=1
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-worker.log
stdout_logfile=/home/arifal/development/sibedas-pbg-web/storage/logs/worker.log
stopasgroup=true
killasgroup=true
```
- Reload Supervisor
@@ -35,3 +36,77 @@ sudo supervisorctl start laravel-worker
sudo supervisorctl restart laravel-worker
sudo supervisorctl status
```
# How to running
- Install composer package
```
composer install
```
- Install npm package
```
npm install && npm run build
```
- Create symlinks storage
```
php artisan storage:link
```
- Running migration
```
php artisan migrate
```
- Running seeder
```
php artisan db:seed
```
- Create view table
- excute all sql queries on folder database/view_query
# Add ENV variable
- API_KEY_GOOGLE
```
Get api key from google developer console for and turn on spreadsheet api or feaature for google sheet
```
- SPREAD_SHEET_ID
```
Get spreadsheet id from google sheet link
```
- OPENAI_API_KEY
```
Get OpenAI API key from chatgpt subscription
```
- ENV
```
API_KEY_GOOGLE="xxxxx"
SPREAD_SHEET_ID="xxxxx"
OPENAI_API_KEY="xxxxx"
```
# Technology version
- php 8.3
- Laravel 11
- node v22.13.0
- npm 10.9.2
- mariadb Ver 15.1 Distrib 10.6.18-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
- Ubuntu 24.04

View File

@@ -34,7 +34,7 @@ class ExecuteScraping extends Command
}
public function handle()
{
SyncronizeSIMBG::dispatch();
SyncronizeSIMBG::dispatch()->onQueue('default');
Log::info("running scheduler daily scraping");
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace App\Console\Commands;
use App\Models\ImportDatasource;
use App\Services\ServiceGoogleSheet;
use App\Services\ServicePbgTask;
use App\Services\ServiceTabPbgTask;
use GuzzleHttp\Client; // Import Guzzle Client
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
class ScrapingData extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:scraping-data';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
private $client;
private $service_pbg_task;
private $service_tab_pbg_task;
/**
* Inject dependencies.
*/
public function __construct(Client $client, ServicePbgTask $service_pbg_task, ServiceTabPbgTask $serviceTabPbgTask)
{
parent::__construct();
$this->client = $client;
$this->service_pbg_task = $service_pbg_task;
$this->service_tab_pbg_task = $serviceTabPbgTask;
}
/**
* Execute the console command.
*/
public function handle()
{
try {
// Create a record with "processing" status
$import_datasource = ImportDatasource::create([
'message' => 'Initiating scraping...',
'response_body' => null,
'status' => 'processing'
]);
// Run the service
$service_google_sheet = new ServiceGoogleSheet($import_datasource->id);
$service_google_sheet->run_service();
// Run the ServicePbgTask with injected Guzzle Client
$this->service_pbg_task->run_service();
// run the service pbg task assignments
$this->service_tab_pbg_task->run_service();
// Update the record status to "success" after completion
$import_datasource->update([
'status' => 'success',
'message' => 'Scraping completed successfully.'
]);
} catch (\Exception $e) {
// Log the error for debugging
Log::error('Scraping failed: ' . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
// Handle errors by updating the status to "failed"
if (isset($import_datasource)) {
$import_datasource->update([
'status' => 'failed',
'response_body' => 'Error: ' . $e->getMessage()
]);
}
}
}
}

View File

@@ -162,7 +162,7 @@ class BigDataResumeController extends Controller
$query = BigdataResume::query()->orderBy('id', 'desc');
if($request->filled('search')){
$query->where('name', 'LIKE', '%'.$request->input('search').'%');
$query->where('year', 'LIKE', '%'.$request->input('search').'%');
}
$query = $query->paginate(config('app.paginate_per_page', 50));
@@ -173,6 +173,53 @@ class BigDataResumeController extends Controller
}
}
public function payment_recaps(Request $request)
{
try {
$query = BigdataResume::query()->orderBy('id', 'desc');
if ($request->filled('date')) {
$query->where('year', 'LIKE', '%' . $request->input('search') . '%');
}
$data = $query->paginate(10);
// Restructure response
$transformedData = [];
foreach ($data as $item) {
$createdAt = $item->created_at;
$id = $item->id;
foreach ($item->toArray() as $key => $value) {
// Only include columns with "sum" in their names
if (strpos($key, 'sum') !== false) {
$transformedData[] = [
'id' => $id,
'category' => $key,
'nominal' => $value,
'created_at' => $createdAt,
];
}
}
}
return response()->json([
'data' => $transformedData, // Flat array
'pagination' => [
'total' => $data->total(),
'per_page' => $data->perPage(),
'current_page' => $data->currentPage(),
'last_page' => $data->lastPage(),
]
]);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Error when fetching data'], 500);
}
}
private function response_empty_resume(){
$result = [
'target_pad' => [

View File

@@ -34,7 +34,7 @@ class GlobalSettingsController extends Controller
try {
$data = GlobalSetting::create($request->validated());
return new GlobalSettingResource($data);
} catch (\Exception $e) {
} catch (Exception $e) {
return $this->resError($e->getMessage(), null, $e->getCode());
}
}

View File

@@ -3,33 +3,15 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Services\GoogleSheetService;
use Illuminate\Http\Request;
class GoogleSheetController extends Controller
{
protected $googleSheetService;
public function __construct(GoogleSheetService $googleSheetService){
$this->googleSheetService = $googleSheetService;
}
/**
* Display a listing of the resource.
*/
public function index()
public function index(Request $request)
{
$dataCollection = $this->googleSheetService->getSheetDataCollection();
$result = [
"last_row" => $this->googleSheetService->getLastRowByColumn("C"),
"last_column" => $this->googleSheetService->getLastColumn(),
"header" => $this->googleSheetService->getHeader(),
"data_collection" => $dataCollection
];
return response()->json($result);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\PbgTaskGoogleSheetResource;
use App\Models\PbgTaskGoogleSheet;
use Illuminate\Http\Request;
class PbgTaskGoogleSheetsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$query = PbgTaskGoogleSheet::query()->orderBy('id', 'desc');
if ($request->filled('search')) {
$query->where('no_registrasi', 'like', "%{$request->get('search')}%");
}
return PbgTaskGoogleSheetResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}
/**
* 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)
{
try{
$data = PbgTaskGoogleSheet::find($id);
$data->delete();
return response()->json(['message' => 'Data deleted successfully'], 200);
}catch(\Exception $e){
return response()->json(['message' => 'Failed to delete data'], 500);
}
}
}

View File

@@ -5,7 +5,11 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\RequestAssignmentResouce;
use App\Models\PbgTask;
use App\Models\PbgTaskGoogleSheet;
use DB;
use Exception;
use Illuminate\Http\Request;
use Log;
class RequestAssignmentController extends Controller
{
@@ -23,6 +27,51 @@ class RequestAssignmentController extends Controller
return RequestAssignmentResouce::collection($query->paginate());
}
public function report_payment_recaps(Request $request)
{
try {
// Query dengan group by kecamatan dan sum nilai_retribusi_keseluruhan_simbg
$query = PbgTaskGoogleSheet::select(
'kecamatan',
DB::raw('SUM(nilai_retribusi_keseluruhan_simbg) as total')
)
->groupBy('kecamatan')
->paginate(10);
// Return hasil dalam JSON format
return response()->json([
'success' => true,
'data' => $query
]);
} catch (Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Terjadi kesalahan: ' . $e->getMessage()], 500);
}
}
public function report_pbg_ptsp()
{
try {
// Query dengan group by status dan count total per status
$query = PbgTask::select(
'status',
'status_name',
DB::raw('COUNT(*) as total')
)
->groupBy('status', 'status_name')
->paginate(10);
// Return hasil dalam JSON format
return response()->json([
'success' => true,
'data' => $query
]);
} catch (Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Terjadi kesalahan: ' . $e->getMessage()], 500);
}
}
/**
* Store a newly created resource in storage.
*/

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\TaskAssignmentsResource;
use App\Models\TaskAssignment;
use Illuminate\Http\Request;
class TaskAssignmentsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request, $uuid)
{
try{
$query = TaskAssignment::query()
->where('pbg_task_uid', $uuid)
->orderBy('id', 'desc');
if ($request->filled('search')) {
$query->where('name', 'like', "%{$request->get('search')}%")
->orWhere('email', 'like', "%{$request->get('search')}%");
}
return TaskAssignmentsResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}catch(\Exception $exception){
return response()->json(['message' => $exception->getMessage()], 500);
}
}
/**
* 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)
{
//
}
}

View File

@@ -30,7 +30,8 @@ class UsersController extends Controller
public function index(Request $request){
$query = User::query();
if($request->has('search') && !empty($request->get("search"))){
$query->where('name', 'LIKE', '%'.$request->get('search').'%');
$query->where('name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('email', 'LIKE', '%'.$request->get('search').'%');
}
return UserResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Approval;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ApprovalController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('approval.index');
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(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)
{
//
}
}

View File

@@ -14,74 +14,30 @@ class BusinessOrIndustriesController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('business-industries.index', compact('creator', 'updater', 'destroyer'));
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('business-industries.index', compact('creator', 'updater', 'destroyer','menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(Request $request)
{
return view("business-industries.create");
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
return view("business-industries.create", compact('menuId'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
public function edit(string $id, Request $request)
{
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$data = BusinessOrIndustry::findOrFail($id);
return view('business-industries.edit', compact('data'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
return view('business-industries.edit', compact('data', 'menuId'));
}
}

View File

@@ -2,7 +2,54 @@
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
abstract class Controller
{
//
protected array $permissions = [];
public function __construct()
{
if (!Auth::check()) {
return;
}
$this->setUserPermissions();
}
protected function setUserPermissions()
{
$user = Auth::user();
if (!$user) {
return;
}
$menus = $user->roles()
->with(['menus' => function ($query) {
$query->select('menus.id', 'menus.name')
->withPivot(['allow_show' ,'allow_create', 'allow_update', 'allow_destroy']);
}])
->get()
->pluck('menus')
->flatten()
->unique('id');
// Store permissions in an associative array
foreach ($menus as $menu) {
$this->permissions[$menu->id] = [
'allow_show' => $menu->pivot->allow_show ?? 0,
'allow_create' => $menu->pivot->allow_create ?? 0,
'allow_update' => $menu->pivot->allow_update ?? 0,
'allow_destroy' => $menu->pivot->allow_destroy ?? 0,
];
}
// Share permissions globally in views
view()->share('permissions', $this->permissions);
}
public function getPermissions()
{
return $this->permissions;
}
}

View File

@@ -5,43 +5,33 @@ namespace App\Http\Controllers;
use App\Models\Customer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class CustomersController extends Controller
{
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('customers.index', compact('creator', 'updater', 'destroyer'));
return view('customers.index', compact('creator', 'updater', 'destroyer', 'menuId'));
}
public function create()
public function create(Request $request)
{
return view('customers.create');
$menuId = $request->query('menu_id');
return view('customers.create', compact('menuId'));
}
public function edit(string $id)
public function edit(Request $request, string $id)
{
$data = Customer::findOrFail($id);
return view('customers.edit', compact('data'));
$menuId = $request->query('menu_id');
return view('customers.edit', compact('data', 'menuId'));
}
public function upload(){
return view('customers.upload');
public function upload(Request $request){
$menuId = $request->query('menu_id');
return view('customers.upload', compact('menuId'));
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Http\Controllers\Dashboards;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class PotentialsController extends Controller
{
public function inside_system(){
return view('dashboards.potentials.inside_system');
}
public function outside_system(){
return view('dashboards.potentials.outside_system');
}
}

View File

@@ -15,27 +15,14 @@ class AdvertisementController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('data.advertisements.index', compact('creator', 'updater', 'destroyer'));
return view('data.advertisements.index', compact('creator', 'updater', 'destroyer','menuId'));
}
/**
@@ -50,8 +37,9 @@ class AdvertisementController extends Controller
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(Request $request)
{
$menuId = $request->query('menu_id', 0);
$title = 'Advertisement';
$subtitle = 'Create Data';
@@ -68,14 +56,15 @@ class AdvertisementController extends Controller
// $route = 'advertisements.create';
// info("AdvertisementController@edit diakses dengan ID: $title");
return view('data.advertisements.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
return view('data.advertisements.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
public function edit(Request $request, $id)
{
$menuId = $request->query('menu_id', 0);
info("AdvertisementController@edit diakses dengan ID: $id");
$title = 'Advertisement';
$subtitle = 'Update Data';
@@ -107,7 +96,7 @@ class AdvertisementController extends Controller
// $route = 'advertisements.update'; // Menggunakan route update untuk form edit
// info("AdvertisementController@edit diakses dengan route: $route");
return view('data.advertisements.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
return view('data.advertisements.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions', 'menuId'));
}
private function getFields()

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Http\Controllers\Data;
use App\Http\Controllers\Controller;
use App\Models\PbgTaskGoogleSheet;
use Illuminate\Http\Request;
class GoogleSheetsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$menu_id = $request->query('menu_id');
$user_menu_permission = $this->permissions[$menu_id];
return view('data.google-sheet.index', compact('user_menu_permission'));
}
public function create()
{
return view('data.google-sheet.create');
}
public function show(string $id)
{
$data = PbgTaskGoogleSheet::find($id);
return view('data.google-sheet.show', compact('data'));
}
public function edit(string $id)
{
return view('data.google-sheet.edit');
}
}

View File

@@ -5,8 +5,6 @@ namespace App\Http\Controllers\Data;
use App\Http\Controllers\Controller;
use App\Models\SpatialPlanning;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class SpatialPlanningController extends Controller
{
@@ -15,42 +13,31 @@ class SpatialPlanningController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('data.spatialPlannings.index', compact('creator', 'updater', 'destroyer'));
return view('data.spatialPlannings.index', compact('creator', 'updater', 'destroyer','menuId'));
}
/**
* show the form for creating a new resource.
*/
public function bulkCreate()
public function bulkCreate(Request $request)
{
return view('data.spatialPlannings.form-upload');
$menuId = $request->query('menu_id', 0);
return view('data.spatialPlannings.form-upload', compact('menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(Request $request)
{
$menuId = $request->query('menu_id', 0);
$title = 'Rencana Tata Ruang';
$subtitle = "Create Data";
@@ -61,30 +48,15 @@ class SpatialPlanningController extends Controller
$fieldTypes = $this->getFieldTypes();
$apiUrl = url('/api/spatial-plannings');
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
public function edit(Request $request,string $id)
{
$menuId = $request->query('menu_id', 0);
$title = 'Rencana Tata Ruang';
$subtitle = 'Update Data';
@@ -100,23 +72,7 @@ class SpatialPlanningController extends Controller
$fieldTypes = $this->getFieldTypes();
$apiUrl = url('/api/spatial-plannings');
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
private function getFields()

View File

@@ -15,41 +15,30 @@ class TourismController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('data.tourisms.index', compact('creator', 'updater', 'destroyer'));
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('data.tourisms.index', compact('creator', 'updater', 'destroyer', 'menuId'));
}
/**
* show the form for creating a new rsource.
*/
public function bulkCreate()
public function bulkCreate(Request $request)
{
return view('data.tourisms.form-upload');
$menuId = $request->query('menu_id', 0);
return view('data.tourisms.form-upload', compact('menuId'));
}
/**
* Show th form for creating a new resource
*/
public function create()
public function create(Request $request)
{
$menuId = $request->query('menu_id', 0);
$title = 'Pariwisata';
$subtitle = 'Create Data';
@@ -64,14 +53,15 @@ class TourismController extends Controller
$apiUrl = url('/api/tourisms');
return view('data.tourisms.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
return view('data.tourisms.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions', 'menuId'));
}
/**
* show the form for editing the specified resource.
*/
public function edit($id)
public function edit(Request $request, $id)
{
$menuId = $request->query('menu_id', 0);
$title = 'Pariwisata';
$subtitle = 'Update Data';
@@ -98,7 +88,7 @@ class TourismController extends Controller
$apiUrl = url('/api/tourisms');
return view('data.tourisms.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
return view('data.tourisms.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions', 'menuId'));
}
private function getFields()

View File

@@ -15,41 +15,30 @@ class UmkmController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('data.umkm.index', compact('creator', 'updater', 'destroyer'));
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('data.umkm.index', compact('creator', 'updater', 'destroyer', 'menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function bulkCreate()
public function bulkCreate(Request $request)
{
return view('data.umkm.form-upload');
$menuId = $request->query('menu_id', 0);
return view('data.umkm.form-upload', compact('menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(Request $request)
{
$menuId = $request->query('menu_id', 0);
$title = 'UMKM';
$subtitle = 'Create Data';
@@ -67,14 +56,15 @@ class UmkmController extends Controller
$apiUrl = url('/api/umkm');
return view('data.umkm.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
return view('data.umkm.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
public function edit(Request $request,$id)
{
$menuId = $request->query('menu_id', 0);
$title = 'UMKM';
$subtitle = 'Update Data';
$modelInstance = Umkm::find($id);
@@ -116,7 +106,7 @@ class UmkmController extends Controller
$apiUrl = url('/api/umkm');
// dd($modelInstance->business_form_id, $dropdownOptions['business_form']);
return view('data.umkm.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
return view('data.umkm.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
private function getFields()

View File

@@ -18,34 +18,21 @@ class DataSettingController extends Controller
*/
public function index(IndexRequest $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view("data-settings.index", compact('creator', 'updater', 'destroyer'));
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view("data-settings.index", compact('creator', 'updater', 'destroyer','menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(IndexRequest $request)
{
return view("data-settings.create");
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
return view("data-settings.create", compact('menuId'));
}
/**
@@ -78,14 +65,15 @@ class DataSettingController extends Controller
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
public function edit(IndexRequest $request,string $id)
{
try{
$data = DataSetting::findOrFail($id);
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
if(empty($data)){
return redirect()->route('data-settings.index')->with('error', 'Invalid id');
}
return view("data-settings.edit", compact("data"));
return view("data-settings.edit", compact("data", 'menuId'));
}catch(Exception $ex){
return redirect()->route("data-settings.index")->with("error", "Invalid id");
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class InvitationsController extends Controller
{
public function index(Request $request){
return view('invitations.index');
}
}

View File

@@ -23,32 +23,19 @@ class UsersController extends Controller
return $this->resSuccess($users);
}
public function index(Request $request){
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
$users = User::paginate();
return view('master.users.index', compact('users', 'creator', 'updater', 'destroyer'));
return view('master.users.index', compact('users', 'creator', 'updater', 'destroyer','menuId'));
}
public function create(){
public function create(Request $request){
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$roles = Role::all();
return view('master.users.create', compact('roles'));
return view('master.users.create', compact('roles', 'menuId'));
}
public function store(UsersRequest $request){
$request->validate([
@@ -86,10 +73,11 @@ class UsersController extends Controller
$user = User::find($id);
return view('master.users.show', compact('user'));
}
public function edit($id){
public function edit(Request $request, $id){
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$user = User::find($id);
$roles = Role::all();
return view('master.users.edit', compact('user', 'roles'));
return view('master.users.edit', compact('user', 'roles', 'menuId'));
}
public function update(Request $request, $id){
$user = User::find($id);

View File

@@ -6,7 +6,6 @@ use App\Http\Requests\MenuRequest;
use App\Models\Menu;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class MenusController extends Controller
{
@@ -15,36 +14,33 @@ class MenusController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = (int) $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('menus.index', compact('creator', 'updater', 'destroyer'));
return view('menus.index', compact('creator', 'updater', 'destroyer', 'menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(Request $request)
{
$parent_menus = Menu::whereNull('parent_id')->get();
return view("menus.create", compact('parent_menus'));
$menuId = $request->query('menu_id'); // Get menu_id from request
$menu = Menu::with('children')->find($menuId); // Find the menu
// Get IDs of all child menus to exclude
$excludedIds = $menu ? $this->getChildMenuIds($menu) : [$menuId];
// Fetch only menus that have children and are not in the excluded list
$parent_menus = Menu::whereHas('children')
->whereNotIn('id', $excludedIds)
->get();
return view("menus.create", compact('parent_menus', 'menuId'));
}
/**
@@ -77,11 +73,16 @@ class MenusController extends Controller
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
public function edit(string $id, Request $request)
{
$menu = Menu::findOrFail($id);
$parent_menus = Menu::whereNull('parent_id')->where('id','!=',$id)->get();
return view("menus.edit", compact('menu','parent_menus'));
$menuId = $request->query('menu_id');
$menu = Menu::with('children')->find($id);
$excludedIds = $menu ? $this->getChildMenuIds($menu) : [$id];
$parent_menus = Menu::whereHas('children')
->whereNotIn('id', $excludedIds)
->get();
return view("menus.edit", compact('menu','parent_menus', 'menuId'));
}
/**
@@ -131,4 +132,15 @@ class MenusController extends Controller
$child->delete();
}
}
private function getChildMenuIds($menu)
{
$ids = [$menu->id]; // Start with current menu ID
foreach ($menu->children as $child) {
$ids = array_merge($ids, $this->getChildMenuIds($child)); // Recursively fetch children
}
return $ids;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PaymentRecapsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('payment-recaps.index');
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(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)
{
//
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ReportPaymentRecapsController extends Controller
{
public function index(Request $request){
return view('report-payment-recaps.index');
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ReportPbgPTSPController extends Controller
{
public function index(Request $request){
return view('report-pbg-ptsp.index');
}
}

View File

@@ -59,7 +59,7 @@ class PbgTaskController extends Controller
*/
public function show(string $id)
{
$data = PbgTask::with(['pbg_task_retributions','pbg_task_index_integrations', 'pbg_task_retributions.pbg_task_prasarana', 'taskAssignments'])->findOrFail($id);
$data = PbgTask::with(['pbg_task_retributions','pbg_task_index_integrations', 'pbg_task_retributions.pbg_task_prasarana'])->findOrFail($id);
return view("pbg_task.show", compact("data"));
}

View File

@@ -19,35 +19,22 @@ class RolesController extends Controller
*/
public function index(Request $request)
{
$menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view("roles.index", compact('creator', 'updater', 'destroyer'));
return view("roles.index", compact('creator', 'updater', 'destroyer', 'menuId'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
public function create(Request $request)
{
return view("roles.create");
$menuId = $request->query('menu_id');
return view("roles.create", compact('menuId'));
}
/**
@@ -80,10 +67,11 @@ class RolesController extends Controller
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
public function edit(string $id, Request $request)
{
$menuId = $request->query('menu_id');
$role = Role::findOrFail($id);
return view("roles.edit", compact('role'));
return view("roles.edit", compact('role', 'menuId'));
}
/**
@@ -121,12 +109,13 @@ class RolesController extends Controller
}
}
public function menu_permission(string $role_id){
public function menu_permission(string $role_id, Request $request){
try{
$menuId = $request->query('menu_id');
$role = Role::findOrFail($role_id);
$menus = Menu::all();
$role_menus = RoleMenu::where('role_id', $role_id)->get() ?? collect();
return view('roles.role_menu', compact('role', 'menus', 'role_menus'));
return view('roles.role_menu', compact('role', 'menus', 'role_menus', 'menuId'));
}catch(\Exception $e){
return redirect()->back()->with("error", $e->getMessage());
}
@@ -134,8 +123,9 @@ class RolesController extends Controller
public function update_menu_permission(Request $request, string $role_id){
try{
$menuId = $request->query('menu_id');
$validateData = $request->validate([
"permissions" => "array",
"permissions" => "nullable|array",
"permissions.*.allow_show" => "nullable|boolean",
"permissions.*.allow_create" => "nullable|boolean",
"permissions.*.allow_update" => "nullable|boolean",
@@ -144,6 +134,13 @@ class RolesController extends Controller
$role = Role::find($role_id);
// Jika `permissions` tidak ada atau kosong, hapus semua permissions terkait
if (!isset($validateData['permissions']) || empty($validateData['permissions'])) {
$role->menus()->detach();
return redirect()->route("roles.index", ['menu_id' => $menuId])
->with('success', 'All menu permissions have been removed.');
}
$permissionsArray = [];
foreach ($validateData['permissions'] as $menu_id => $permission) {
$permissionsArray[$menu_id] = [
@@ -158,7 +155,7 @@ class RolesController extends Controller
// Sync will update existing records and insert new ones
$role->menus()->sync($permissionsArray);
return redirect()->route("role-menu.permission", $role_id)->with('success','Menu Permission updated successfully');
return redirect()->route("roles.index", ['menu_id' => $menuId])->with('success','Menu Permission updated successfully');
}catch(\Exception $e){
Log::error("Error updating role_menu:", ["error" => $e->getMessage()]);
return redirect()->route("role-menu.permission", $role_id)->with("error", $e->getMessage());

View File

@@ -6,6 +6,8 @@ use App\Http\Controllers\Controller;
use App\Services\ServiceSIMBG;
use Illuminate\Http\Request;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class SyncronizeController extends Controller
{
protected $service_simbg;

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TpatptsController extends Controller
{
public function index()
{
return view('tpa-tpt.index');
}
public function create()
{
//
}
public function show(string $id)
{
//
}
public function edit(string $id)
{
//
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PbgTaskGoogleSheetResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TaskAssignmentsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -22,7 +22,14 @@ class SyncronizeSIMBG implements ShouldQueue
public function handle(): void
{
$serviceSIMBG = app(ServiceSIMBG::class);
$serviceSIMBG->syncTaskPBG();
try {
$serviceSIMBG = app(ServiceSIMBG::class);
$serviceSIMBG->syncTaskPBG();
} catch (\Exception $e) {
\Log::error("SyncronizeSIMBG Job Failed: " . $e->getMessage(), [
'exception' => $e,
]);
$this->fail($e); // Mark the job as failed
}
}
}

View File

@@ -18,6 +18,13 @@ class RoleMenu extends Model
'allow_destroy',
];
protected $casts = [
'allow_show' => 'boolean',
'allow_create' => 'boolean',
'allow_update' => 'boolean',
'allow_destroy' => 'boolean',
];
public $timestamps = true;
public function role(){

View File

@@ -12,7 +12,7 @@ class TaskAssignment extends Model
protected $fillable = [
'user_id', 'name', 'username', 'email', 'phone_number', 'role',
'role_name', 'is_active', 'file', 'expertise', 'experience',
'is_verif', 'uid', 'status', 'status_name', 'note', 'pbg_task_uid'
'is_verif', 'uid', 'status', 'status_name', 'note', 'pbg_task_uid', 'tas_id'
];
protected $casts = [

View File

@@ -53,4 +53,11 @@ class User extends Authenticatable
public function roles(){
return $this->belongsToMany(Role::class, 'user_role')->withTimestamps();
}
public function menus(){
return Menu::whereHas('roles', function ($query){
$query->whereIn('roles.id', $this->roles->pluck('id'))
->where('role_menu.allow_show', true);
});
}
}

View File

@@ -34,21 +34,24 @@ class AppServiceProvider extends ServiceProvider
{
Blade::component('circle', Circle::class);
View::composer('layouts.partials.sidebar', function ($view){
View::composer('layouts.partials.sidebar', function ($view) {
$user = Auth::user();
if($user){
$menus = Menu::whereHas('roles', function ($query) use ($user){
$query->where('roles.id', $user->roles->pluck('id'));
})
->with(['children' => function ($query) {
$query->whereHas('roles', function ($subQuery) {
$subQuery->where('role_menu.allow_show', 1);
});
}])
->orderBy('sort_order', 'asc')
->get();
}else{
if ($user) {
$menus = Menu::whereHas('roles', function ($query) use ($user) {
$query->whereIn('roles.id', $user->roles->pluck('id'))
->where('role_menu.allow_show', 1);
})
->with(['children' => function ($query) use ($user) {
$query->whereHas('roles', function ($subQuery) use ($user) {
$subQuery->whereIn('roles.id', $user->roles->pluck('id'))
->where('role_menu.allow_show', 1);
});
}])
->whereNull('parent_id') // Ambil hanya menu utama
->orderBy('sort_order', 'asc')
->get();
} else {
$menus = collect();
}

View File

@@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string
*/
public const HOME = '/dashboards/bigdata';
public const HOME = '/home';
/**
* Define your route model bindings, pattern filters, and other route configuration.

View File

@@ -0,0 +1,284 @@
<?php
namespace App\Services;
use App\Models\DataSetting;
use App\Models\ImportDatasource;
use App\Models\PbgTaskGoogleSheet;
use Carbon\Carbon;
use Google_Client;
use Google_Service_Sheets;
use Log;
class ServiceGoogleSheet
{
protected $client;
protected $service;
protected $spreadsheetID;
protected $service_sheets;
protected $import_datasource;
public function __construct(int $import_datasource_id)
{
$this->client = new Google_Client();
$this->client->setApplicationName("Sibedas Google Sheets API");
$this->client->setScopes([Google_Service_Sheets::SPREADSHEETS_READONLY]);
$this->client->setAuthConfig(storage_path("app/teak-banner-450003-s8-ea05661d9db0.json"));
$this->client->setAccessType("offline");
$this->service = new Google_Service_Sheets($this->client);
$this->spreadsheetID = env("SPREAD_SHEET_ID");
$this->service_sheets = new Google_Service_Sheets($this->client);
$this->import_datasource = ImportDatasource::findOrFail($import_datasource_id);
}
public function run_service(){
$run_one = $this->sync_big_data();
if(!$run_one){
$this->import_datasource->update(['status' => 'failed']);
return false;
}
$run_two = $this->sync_google_sheet_data();
if(!$run_two){
$this->import_datasource->update(['status' => 'failed']);
return false;
}
return true;
}
public function sync_google_sheet_data() {
try {
$sheet_data = $this->get_data_by_sheet(0);
if (empty($sheet_data) || count($sheet_data) < 2) {
Log::warning("sync_google_sheet_data: No valid data found.");
return false;
}
$cleanValue = function ($value) {
return (isset($value) && trim($value) !== '') ? trim($value) : null;
};
$mapUpsert = [];
foreach(array_slice($sheet_data, 1) as $row){
if(!is_array($row)){
continue;
}
$no_registrasi = $cleanValue($row[2] ?? null);
// Apply the same logic from your SQL UPDATE
if (strpos($no_registrasi, 'PBG-') === 0) {
$format_registrasi = $no_registrasi;
} else {
$format_registrasi = sprintf(
"PBG-%s-%s-%s",
substr($no_registrasi, 0, 6) ?: '',
substr($no_registrasi, 7, 8) ?: '',
substr($no_registrasi, -2) ?: ''
);
}
$mapUpsert[] = [
'jenis_konsultasi' => $cleanValue($row[1] ?? null),
'no_registrasi' => $no_registrasi,
'formatted_registration_number' => $format_registrasi,
'nama_pemilik' => $cleanValue($row[3] ?? null),
'lokasi_bg' => $cleanValue($row[4] ?? null),
'fungsi_bg' => $cleanValue($row[5] ?? null),
'nama_bangunan' => $cleanValue($row[6] ?? null),
'tgl_permohonan' => $this->convertToDate($cleanValue($row[7] ?? null)),
'status_verifikasi' => $cleanValue($row[8] ?? null),
'status_permohonan' => $cleanValue($row[9] ?? null),
'alamat_pemilik' => $cleanValue($row[10] ?? null),
'no_hp' => $cleanValue($row[11] ?? null),
'email' => $cleanValue($row[12] ?? null),
'tanggal_catatan' => $this->convertToDate($cleanValue($row[13] ?? null)),
'catatan_kekurangan_dokumen' => $cleanValue($row[14] ?? null),
'gambar' => $cleanValue($row[15] ?? null),
'krk_kkpr' => $cleanValue($row[16] ?? null),
'no_krk' => $cleanValue($row[17] ?? null),
'lh' => $cleanValue($row[18] ?? null),
'ska' => $cleanValue($row[19] ?? null),
'keterangan' => $cleanValue($row[20] ?? null),
'helpdesk' => $cleanValue($row[21] ?? null),
'pj' => $cleanValue($row[22] ?? null),
'kepemilikan' => $cleanValue($row[24] ?? null),
'potensi_taru' => $cleanValue($row[25] ?? null),
'validasi_dinas' => $cleanValue($row[26] ?? null),
'kategori_retribusi' => $cleanValue($row[27] ?? null),
'no_urut_ba_tpt' => $cleanValue($row[28] ?? null),
'tanggal_ba_tpt' => $this->convertToDate($cleanValue($row[29] ?? null)),
'no_urut_ba_tpa' => $cleanValue($row[30] ?? null),
'tanggal_ba_tpa' => $this->convertToDate($cleanValue($row[31] ?? null)),
'no_urut_skrd' => $cleanValue($row[32] ?? null),
'tanggal_skrd' => $this->convertToDate($cleanValue($row[33] ?? null)),
'ptsp' => $cleanValue($row[34] ?? null),
'selesai_terbit' => $cleanValue($row[35] ?? null),
'tanggal_pembayaran' => $cleanValue($row[36] ?? null),
'format_sts' => $cleanValue($row[37] ?? null),
'tahun_terbit' => (int) $cleanValue($row[38] ?? null),
'tahun_berjalan' => (int) $cleanValue($row[39] ?? null),
'kelurahan' => $cleanValue($row[40] ?? null),
'kecamatan' => $cleanValue($row[41] ?? null),
'lb' => $this->convertToDecimal($cleanValue($row[42] ?? 0)),
'tb' => $this->convertToDecimal($cleanValue($row[43] ?? 0)),
'jlb' => (int) $cleanValue($row[44] ?? null),
'unit' => (int) $cleanValue($row[45] ?? null),
'usulan_retribusi' => (int) $cleanValue($row[46] ?? null),
'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($cleanValue($row[47] ?? 0)),
'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($cleanValue($row[48] ?? 0)),
'denda' => $this->convertToDecimal($cleanValue($row[49] ?? 0)),
'latitude' => $cleanValue($row[50] ?? null),
'longitude' => $cleanValue($row[51] ?? null),
'nik_nib' => $cleanValue($row[52] ?? null),
'dok_tanah' => $cleanValue($row[53] ?? null),
'temuan' => $cleanValue($row[54] ?? null),
'updated_at' => now()
];
}
// Count occurrences of each no_registrasi
$registrasiCounts = array_count_values(array_column($mapUpsert, 'no_registrasi'));
// Filter duplicates (those appearing more than once)
$duplicates = array_filter($registrasiCounts, function ($count) {
return $count > 1;
});
if (!empty($duplicates)) {
Log::warning("Duplicate no_registrasi found", ['duplicates' => array_keys($duplicates)]);
}
// Remove duplicates before upsert
$mapUpsert = collect($mapUpsert)->unique('no_registrasi')->values()->all();
$batchSize = 1000;
$chunks = array_chunk($mapUpsert, $batchSize);
foreach ($chunks as $chunk) {
PbgTaskGoogleSheet::upsert($chunk, ['no_registrasi']);
}
Log::info("sync google sheet done");
} catch (\Exception $e) {
Log::error("sync_google_sheet_data failed", ['error' => $e->getMessage()]);
return false;
}
}
public function sync_big_data(){
try {
$sheet_big_data = $this->get_data_by_sheet();
$data_setting_result = []; // Initialize result storage
$found_section = null; // Track which section is found
foreach ($sheet_big_data as $row) {
// Check for section headers
if (in_array("•PROSES PENERBITAN:", $row)) {
$found_section = "MENUNGGU_KLIK_DPMPTSP";
} elseif (in_array("•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2024:", $row)) {
$found_section = "REALISASI_TERBIT_PBG";
} elseif (in_array("•TERPROSES DI DPUTR: belum selesai rekomtek'", $row)) {
$found_section = "PROSES_DINAS_TEKNIS";
}
// If a section is found and we reach "Grand Total", save the corresponding values
if ($found_section && isset($row[0]) && trim($row[0]) === "Grand Total") {
if ($found_section === "MENUNGGU_KLIK_DPMPTSP") {
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $this->convertToInteger($row[2]) ?? null;
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $this->convertToDecimal($row[3]) ?? null;
} elseif ($found_section === "REALISASI_TERBIT_PBG") {
$data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $this->convertToInteger($row[2]) ?? null;
$data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $this->convertToDecimal($row[4]) ?? null;
} elseif ($found_section === "PROSES_DINAS_TEKNIS") {
$data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $this->convertToInteger($row[2]) ?? null;
$data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $this->convertToDecimal($row[3]) ?? null;
}
// Reset section tracking after capturing "Grand Total"
$found_section = null;
}
}
foreach ($data_setting_result as $key => $value) {
DataSetting::updateOrInsert(
["key" => $key], // Find by key
["value" => $value] // Update or insert value
);
}
return true;
} catch (\Exception $e) {
// **Log error**
Log::error("Error syncing Google Sheet data", ['error' => $e->getMessage()]);
return false;
}
}
private function get_data_by_sheet($no_sheet = 1){
$spreadsheet = $this->service->spreadsheets->get($this->spreadsheetID);
$sheets = $spreadsheet->getSheets();
$sheetTitle = $sheets[$no_sheet]->getProperties()->getTitle();
$range = "{$sheetTitle}";
$response = $this->service->spreadsheets_values->get($this->spreadsheetID, $range);
$values = $response->getValues();
return!empty($values)? $values : [];
}
private function convertToInteger($value) {
// Check if the value is an empty string, and return null if true
if (trim($value) === "") {
return null;
}
$cleaned = str_replace('.','', $value);
// Otherwise, cast to integer
return (int) $cleaned;
}
private function convertToDecimal(?string $value): ?float
{
if (empty($value)) {
return null; // Return null if the input is empty
}
// Remove all non-numeric characters except comma and dot
$value = preg_replace('/[^0-9,\.]/', '', $value);
// If the number contains both dot (.) and comma (,)
if (strpos($value, '.') !== false && strpos($value, ',') !== false) {
$value = str_replace('.', '', $value); // Remove thousands separator
$value = str_replace(',', '.', $value); // Convert decimal separator to dot
}
// If only a dot is present (assumed as thousands separator)
elseif (strpos($value, '.') !== false) {
$value = str_replace('.', '', $value); // Remove all dots (treat as thousands separators)
}
// If only a comma is present (assumed as decimal separator)
elseif (strpos($value, ',') !== false) {
$value = str_replace(',', '.', $value); // Convert comma to dot (decimal separator)
}
// Ensure the value is numeric before returning
return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null;
}
private function convertToDate($dateString)
{
try {
// Check if the string is empty
if (empty($dateString)) {
return null;
}
// Try to parse the date string
$date = Carbon::parse($dateString);
// Return the Carbon instance
return $date->format('Y-m-d');
} catch (\Exception $e) {
// Return null if an error occurs during parsing
return null;
}
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace App\Services;
use App\Models\GlobalSetting;
use App\Models\PbgTask;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Exception;
use Illuminate\Support\Facades\Log;
class ServicePbgTask
{
private $client;
private $simbg_host;
private $fetch_per_page;
private $pbg_task_url;
private $service_token;
private $user_token;
private $user_refresh_token;
public function __construct(Client $client, ServiceTokenSIMBG $service_token)
{
$settings = GlobalSetting::whereIn('key', ['SIMBG_HOST', 'FETCH_PER_PAGE'])
->pluck('value', 'key');
$this->simbg_host = trim((string) ($settings['SIMBG_HOST'] ?? ""));
$this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? "10"));
$this->client = $client;
$this->service_token = $service_token;
$this->pbg_task_url = "{$this->simbg_host}/api/pbg/v1/list/?page=1&size={$this->fetch_per_page}&sort=ASC";
$auth_data = $this->service_token->get_token();
$this->user_token = $auth_data['access'];
$this->user_refresh_token = $auth_data['refresh'];
}
public function run_service()
{
try{
$this->fetch_pbg_task();
}catch(Exception $e){
throw $e;
}
}
private function fetch_pbg_task()
{
try {
$currentPage = 1;
$totalPage = 1;
$options = [
'headers' => [
'Authorization' => "Bearer {$this->user_token}",
'Content-Type' => 'application/json'
]
];
$maxRetries = 3; // Maximum number of retries
$initialDelay = 1; // Initial delay in seconds
$fetchData = function ($url) use (&$options, $maxRetries, $initialDelay) {
$retryCount = 0;
while ($retryCount < $maxRetries) {
try {
return $this->client->get($url, $options);
} catch (\GuzzleHttp\Exception\ClientException $e) {
if ($e->getCode() === 401) {
Log::warning("Unauthorized. Refreshing token...");
// Refresh token
$auth_data = $this->service_token->refresh_token($this->user_refresh_token);
if (!isset($auth_data['access'])) {
Log::error("Token refresh failed.");
throw new Exception("Token refresh failed.");
}
// Update tokens
$this->user_token = $auth_data['access'];
$this->user_refresh_token = $auth_data['refresh'];
// Update headers
$options['headers']['Authorization'] = "Bearer {$this->user_token}";
// Retry request
return $this->client->get($url, $options);
}
throw $e;
} catch (\GuzzleHttp\Exception\ServerException | \GuzzleHttp\Exception\ConnectException $e) {
// Handle 502 or connection issues
if ($e->getCode() === 502) {
Log::warning("502 Bad Gateway - Retrying in {$initialDelay} seconds...");
} else {
Log::error("Network error - Retrying in {$initialDelay} seconds...");
}
$retryCount++;
sleep($initialDelay);
$initialDelay *= 2; // Exponential backoff
}
}
Log::error("Max retries reached. Failing request.");
throw new Exception("Max retries reached. Failing request.");
};
do {
$url = "{$this->simbg_host}/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
$fetch_data = $fetchData($url);
if (!$fetch_data) {
Log::error("Failed to fetch data on page {$currentPage} after retries.");
throw new Exception("Failed to fetch data on page {$currentPage} after retries.");
}
$response = json_decode($fetch_data->getBody()->getContents(), true);
if (!isset($response['data'])) {
Log::error("Invalid API response on page {$currentPage}");
throw new Exception("Invalid API response on page {$currentPage}");
}
$data = $response['data'];
$totalPage = isset($response['total_page']) ? (int) $response['total_page'] : 1;
$saved_data = [];
foreach ($data as $item) {
$saved_data[] = [
'uuid' => $item['uid'] ?? null,
'name' => $item['name'] ?? null,
'owner_name' => $item['owner_name'] ?? null,
'application_type' => $item['application_type'] ?? null,
'application_type_name' => $item['application_type_name'] ?? null,
'condition' => $item['condition'] ?? null,
'registration_number' => $item['registration_number'] ?? null,
'document_number' => $item['document_number'] ?? null,
'address' => $item['address'] ?? null,
'status' => $item['status'] ?? null,
'status_name' => $item['status_name'] ?? null,
'slf_status' => $item['slf_status'] ?? null,
'slf_status_name' => $item['slf_status_name'] ?? null,
'function_type' => $item['function_type'] ?? null,
'consultation_type' => $item['consultation_type'] ?? null,
'due_date' => $item['due_date'] ?? null,
'land_certificate_phase' => $item['land_certificate_phase'] ?? null,
'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(),
];
}
if (!empty($saved_data)) {
PbgTask::upsert($saved_data, ['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'
]);
}
Log::info("Page {$currentPage} fetched & saved", ['records' => count($saved_data)]);
$currentPage++;
} while ($currentPage <= $totalPage);
return true;
} catch (Exception $e) {
Log::error("Error fetching PBG tasks", ['error' => $e->getMessage()]);
throw $e;
}
}
}

View File

@@ -179,60 +179,59 @@ class ServiceSIMBG
}
$mapToUpsert = [];
foreach($sheetData as $data){
$mapToUpsert[] =
[
'no_registrasi' => $data['no__registrasi'] ?? null,
'jenis_konsultasi' => $data['jenis_konsultasi'] ?? null,
'fungsi_bg' => $data['fungsi_bg'] ?? null,
'tgl_permohonan' => $this->convertToDate($data['tgl_permohonan']),
'status_verifikasi' => $data['status_verifikasi'] ?? null,
'status_permohonan' => $this->convertToDate($data['status_permohonan']),
'alamat_pemilik' => $data['alamat_pemilik'] ?? null,
'no_hp' => $data['no__hp'] ?? null,
'email' => $data['e_mail'] ?? null,
'tanggal_catatan' => $this->convertToDate($data['tanggal_catatan']),
'catatan_kekurangan_dokumen' => $data['catatan_kekurangan_dokumen'] ?? null,
'gambar' => $data['gambar'] ?? null,
'krk_kkpr' => $data['krk_kkpr'] ?? null,
'no_krk' => $data['no__krk'] ?? null,
'lh' => $data['lh'] ?? null,
'ska' => $data['ska'] ?? null,
'keterangan' => $data['keterangan'] ?? null,
'helpdesk' => $data['helpdesk'] ?? null,
'pj' => $data['pj'] ?? null,
'kepemilikan' => $data['kepemilikan'] ?? null,
'potensi_taru' => $data['potensi_taru'] ?? null,
'validasi_dinas' => $data['validasi_dinas'] ?? null,
'kategori_retribusi' => $data['kategori_retribusi'] ?? null,
'no_urut_ba_tpt' => $data['no__urut_ba_tpt__2024_0001_'] ?? null,
'tanggal_ba_tpt' => $this->convertToDate($data['tanggal_ba_tpt']),
'no_urut_ba_tpa' => $data['no__urut_ba_tpa'] ?? null,
'tanggal_ba_tpa' => $this->convertToDate($data['tanggal_ba_tpa']),
'no_urut_skrd' => $data['no__urut_skrd__2024_0001_'] ?? null,
'tanggal_skrd' => $this->convertToDate($data['tanggal_skrd']),
'ptsp' => $data['ptsp'] ?? null,
'selesai_terbit' => $data['selesai_terbit'] ?? null,
'tanggal_pembayaran' => $this->convertToDate($data['tanggal_pembayaran__yyyy_mm_dd_']),
'format_sts' => $data['format_sts'] ?? null,
'tahun_terbit' => (int) $data['tahun_terbit'] ?? null,
'tahun_berjalan' => (int) $data['tahun_berjalan'] ?? null,
'kelurahan' => $data['kelurahan'] ?? null,
'kecamatan' => $data['kecamatan'] ?? null,
'lb' => $this->convertToDecimal($data['lb']) ?? null,
'tb' => $this->convertToDecimal($data['tb']) ?? null,
'jlb' => (int) $data['jlb'] ?? null,
'unit' => (int) $data['unit'] ?? null,
'usulan_retribusi' => (int) $data['usulan_retribusi'] ?? null,
'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__simbg_']) ?? null,
'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__pad_']) ?? null,
'denda' => $this->convertToDecimal($data['denda']) ?? null,
'latitude' => $data['latitude'] ?? null,
'longitude' => $data['longitude'] ?? null,
'nik_nib' => $data['nik_nib'] ?? null,
'dok_tanah' => $data['dok__tanah'] ?? null,
'temuan' => $data['temuan'] ?? null,
];
foreach ($sheetData as $data) {
$mapToUpsert[] = [
'no_registrasi' => $this->cleanString($data['no__registrasi'] ?? null),
'jenis_konsultasi' => $this->cleanString($data['jenis_konsultasi'] ?? null),
'fungsi_bg' => $this->cleanString($data['fungsi_bg'] ?? null),
'tgl_permohonan' => $this->convertToDate($this->cleanString($data['tgl_permohonan'] ?? null)),
'status_verifikasi' => $this->cleanString($data['status_verifikasi'] ?? null),
'status_permohonan' => $this->convertToDate($this->cleanString($data['status_permohonan'] ?? null)),
'alamat_pemilik' => $this->cleanString($data['alamat_pemilik'] ?? null),
'no_hp' => $this->cleanString($data['no__hp'] ?? null),
'email' => $this->cleanString($data['e_mail'] ?? null),
'tanggal_catatan' => $this->convertToDate($this->cleanString($data['tanggal_catatan'] ?? null)),
'catatan_kekurangan_dokumen' => $this->cleanString($data['catatan_kekurangan_dokumen'] ?? null),
'gambar' => $this->cleanString($data['gambar'] ?? null),
'krk_kkpr' => $this->cleanString($data['krk_kkpr'] ?? null),
'no_krk' => $this->cleanString($data['no__krk'] ?? null),
'lh' => $this->cleanString($data['lh'] ?? null),
'ska' => $this->cleanString($data['ska'] ?? null),
'keterangan' => $this->cleanString($data['keterangan'] ?? null),
'helpdesk' => $this->cleanString($data['helpdesk'] ?? null),
'pj' => $this->cleanString($data['pj'] ?? null),
'kepemilikan' => $this->cleanString($data['kepemilikan'] ?? null),
'potensi_taru' => $this->cleanString($data['potensi_taru'] ?? null),
'validasi_dinas' => $this->cleanString($data['validasi_dinas'] ?? null),
'kategori_retribusi' => $this->cleanString($data['kategori_retribusi'] ?? null),
'no_urut_ba_tpt' => $this->cleanString($data['no__urut_ba_tpt__2024_0001_'] ?? null),
'tanggal_ba_tpt' => $this->convertToDate($this->cleanString($data['tanggal_ba_tpt'] ?? null)),
'no_urut_ba_tpa' => $this->cleanString($data['no__urut_ba_tpa'] ?? null),
'tanggal_ba_tpa' => $this->convertToDate($this->cleanString($data['tanggal_ba_tpa'] ?? null)),
'no_urut_skrd' => $this->cleanString($data['no__urut_skrd__2024_0001_'] ?? null),
'tanggal_skrd' => $this->convertToDate($this->cleanString($data['tanggal_skrd'] ?? null)),
'ptsp' => $this->cleanString($data['ptsp'] ?? null),
'selesai_terbit' => $this->cleanString($data['selesai_terbit'] ?? null),
'tanggal_pembayaran' => $this->convertToDate($this->cleanString($data['tanggal_pembayaran__yyyy_mm_dd_'] ?? null)),
'format_sts' => $this->cleanString($data['format_sts'] ?? null),
'tahun_terbit' => (int) ($data['tahun_terbit'] ?? null),
'tahun_berjalan' => (int) ($data['tahun_berjalan'] ?? null),
'kelurahan' => $this->cleanString($data['kelurahan'] ?? null),
'kecamatan' => $this->cleanString($data['kecamatan'] ?? null),
'lb' => $this->convertToDecimal($data['lb'] ?? null),
'tb' => $this->convertToDecimal($data['tb'] ?? null),
'jlb' => (int) ($data['jlb'] ?? null),
'unit' => (int) ($data['unit'] ?? null),
'usulan_retribusi' => (int) ($data['usulan_retribusi'] ?? null),
'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__simbg_'] ?? null),
'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__pad_'] ?? null),
'denda' => $this->convertToDecimal($data['denda'] ?? null),
'latitude' => $this->cleanString($data['latitude'] ?? null),
'longitude' => $this->cleanString($data['longitude'] ?? null),
'nik_nib' => $this->cleanString($data['nik_nib'] ?? null),
'dok_tanah' => $this->cleanString($data['dok__tanah'] ?? null),
'temuan' => $this->cleanString($data['temuan'] ?? null),
];
}
$batchSize = 1000;
@@ -339,20 +338,12 @@ class ServiceSIMBG
$decodedResponse = json_decode($response->getContent(), true);
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
Log::warning("Token is invalid, refreshing token...");
// Regenerate token
$initResToken = $this->getToken();
// Check if new token is valid
if (!empty($initResToken->original['data']['token']['access'])) {
$new_token = $initResToken->original['data']['token']['access'];
// **Fix: Update headers before retrying**
$headers['Authorization'] = "Bearer " . $new_token;
Log::info("Token refreshed successfully, retrying API request...");
continue; // Retry with new token
continue;
} else {
Log::error("Failed to refresh token");
return $this->resError("Failed to refresh token");
@@ -464,29 +455,21 @@ class ServiceSIMBG
'Authorization' => "Bearer " . $token,
];
for ($attempt = 0; $attempt < 2; $attempt++) { // Try twice (original + retry)
for ($attempt = 0; $attempt < 2; $attempt++) {
$res = $this->service_client->get($url, $headers);
// Check if response is JsonResponse and decode it
if ($res instanceof \Illuminate\Http\JsonResponse) {
$decodedResponse = json_decode($res->getContent(), true);
// Handle invalid token case
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
Log::warning("Token is invalid, refreshing token...");
// Regenerate the token
$initResToken = $this->getToken();
// Check if the new token is valid
if (!empty($initResToken->original['data']['token']['access'])) {
$new_token = $initResToken->original['data']['token']['access'];
// **Fix: Update headers with the new token**
$headers['Authorization'] = "Bearer " . $new_token;
Log::info("Token refreshed successfully, retrying API request...");
continue; // Retry the request with the new token
continue;
} else {
Log::error("Failed to refresh token");
return $this->resError("Failed to refresh token");
@@ -494,7 +477,6 @@ class ServiceSIMBG
}
}
// If request succeeds, break out of retry loop
break;
}
@@ -502,7 +484,6 @@ class ServiceSIMBG
$responseData = $res->original ?? [];
$data = $responseData['data']['data'] ?? [];
if (empty($data)) {
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid, 'response' => $responseData]);
return false;
}
@@ -583,7 +564,7 @@ class ServiceSIMBG
foreach ($datas as $data) {
$task_assignments[] = [
'pbg_task_uid' => $uuid, // Assuming this is a foreign key
'pbg_task_uid' => $uuid,
'user_id' => $data['user_id'],
'name' => $data['name'],
'username' => $data['username'],
@@ -592,22 +573,23 @@ class ServiceSIMBG
'role' => $data['role'],
'role_name' => $data['role_name'],
'is_active' => $data['is_active'],
'file' => json_encode($data['file']), // Store as JSON if it's an array
'expertise' => $data['expertise'],
'experience' => $data['experience'],
'file' => !empty($data['file']) ? json_encode($data['file']) : null,
'expertise' => !empty($data['expertise']) ? json_encode($data['expertise']) : null,
'experience' => !empty($data['experience']) ? json_encode($data['experience']) : null,
'is_verif' => $data['is_verif'],
'uid' => $data['uid'], // Unique identifier
'uid' => $data['uid'],
'status' => $data['status'],
'status_name' => $data['status_name'],
'note' => $data['note'],
'ta_id' => $data['id'],
'created_at' => now(),
'updated_at' => now(),
];
}
TaskAssignment::upsert(
$task_assignments, // Data to insert/update
['uid'], // Unique key for conflict resolution
['name', 'username', 'email', 'phone_number', 'role', 'role_name', 'is_active', 'file', 'expertise', 'experience', 'is_verif', 'status', 'status_name', 'note', 'updated_at']
$task_assignments,
['uid'],
['ta_id','name', 'username', 'email', 'phone_number', 'role', 'role_name', 'is_active', 'file', 'expertise', 'experience', 'is_verif', 'status', 'status_name', 'note', 'updated_at']
);
return true;
}catch(Exception $e){
@@ -673,4 +655,9 @@ class ServiceSIMBG
return null;
}
}
private function cleanString($value)
{
return isset($value) ? trim(strip_tags($value)) : null;
}
}

View File

@@ -0,0 +1,351 @@
<?php
namespace App\Services;
use App\Models\GlobalSetting;
use App\Models\PbgTask;
use App\Models\PbgTaskIndexIntegrations;
use App\Models\PbgTaskPrasarana;
use App\Models\PbgTaskRetributions;
use App\Models\TaskAssignment;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
class ServiceTabPbgTask
{
private $client;
private $simbg_host;
private $fetch_per_page;
private $service_token;
private $user_token;
private $user_refresh_token;
public function __construct(Client $client, ServiceTokenSIMBG $service_token)
{
$settings = GlobalSetting::whereIn('key', ['SIMBG_HOST', 'FETCH_PER_PAGE'])
->pluck('value', 'key');
$this->simbg_host = trim((string) ($settings['SIMBG_HOST'] ?? ""));
$this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? "10"));
$this->client = $client;
$this->service_token = $service_token;
$auth_data = $this->service_token->get_token();
$this->user_token = $auth_data['access'];
$this->user_refresh_token = $auth_data['refresh'];
}
public function run_service()
{
try {
$pbg_tasks = PbgTask::all();
foreach ($pbg_tasks as $pbg_task) {
$this->scraping_task_assignments($pbg_task->uuid);
$this->scraping_task_retributions($pbg_task->uuid);
$this->scraping_task_integrations($pbg_task->uuid);
// Process task assignments here if needed
Log::info("Successfully fetched for UUID: {$pbg_task->uuid}");
}
} catch (\Exception $e) {
Log::error("Failed to scrape task assignments: " . $e->getMessage());
throw $e;
}
}
private function scraping_task_assignments($uuid)
{
$url = "{$this->simbg_host}/api/pbg/v1/list-tim-penilai/{$uuid}/?page=1&size=10";
$options = [
'headers' => [
'Authorization' => "Bearer {$this->user_token}",
'Content-Type' => 'application/json'
]
];
$maxRetries = 3;
$initialDelay = 1;
$retriedAfter401 = false;
for ($retryCount = 0; $retryCount < $maxRetries; $retryCount++) {
try {
$response = $this->client->get($url, $options);
$responseData = json_decode($response->getBody()->getContents(), true);
if (empty($responseData['data']) || !is_array($responseData['data'])) {
return true;
}
$task_assignments = [];
foreach ($responseData['data'] as $data) {
$task_assignments[] = [
'pbg_task_uid' => $uuid,
'user_id' => $data['user_id'] ?? null,
'name' => $data['name'] ?? null,
'username' => $data['username'] ?? null,
'email' => $data['email'] ?? null,
'phone_number' => $data['phone_number'] ?? null,
'role' => $data['role'] ?? null,
'role_name' => $data['role_name'] ?? null,
'is_active' => $data['is_active'] ?? false,
'file' => !empty($data['file']) ? json_encode($data['file']) : null,
'expertise' => !empty($data['expertise']) ? json_encode($data['expertise']) : null,
'experience' => !empty($data['experience']) ? json_encode($data['experience']) : null,
'is_verif' => $data['is_verif'] ?? false,
'uid' => $data['uid'] ?? null,
'status' => $data['status'] ?? null,
'status_name' => $data['status_name'] ?? null,
'note' => $data['note'] ?? null,
'ta_id' => $data['id'] ?? null,
'created_at' => now(),
'updated_at' => now(),
];
}
if (!empty($task_assignments)) {
TaskAssignment::upsert(
$task_assignments,
['uid'],
['ta_id', 'name', 'username', 'email', 'phone_number', 'role', 'role_name', 'is_active', 'file', 'expertise', 'experience', 'is_verif', 'status', 'status_name', 'note', 'updated_at']
);
}
return $responseData;
} catch (\GuzzleHttp\Exception\ClientException $e) {
if ($e->getCode() === 401 && !$retriedAfter401) {
Log::warning("401 Unauthorized - Refreshing token and retrying...");
$this->refreshToken();
$options['headers']['Authorization'] = "Bearer {$this->user_token}";
$retriedAfter401 = true;
continue; // Retry with new token
}
throw $e;
} catch (\GuzzleHttp\Exception\ServerException | \GuzzleHttp\Exception\ConnectException $e) {
if ($e->getCode() === 502) {
Log::warning("502 Bad Gateway - Retrying in {$initialDelay} seconds...");
} else {
Log::error("Network error ({$e->getCode()}) - Retrying in {$initialDelay} seconds...");
}
sleep($initialDelay);
$initialDelay *= 2;
} catch (\Exception $e) {
Log::error("Unexpected error: " . $e->getMessage());
throw $e;
}
}
Log::error("Failed to fetch task assignments for UUID {$uuid} after {$maxRetries} retries.");
throw new \Exception("Failed to fetch task assignments for UUID {$uuid} after retries.");
}
private function scraping_task_retributions($uuid)
{
$url = "{$this->simbg_host}/api/pbg/v1/detail/" . $uuid . "/retribution/submit/";
$options = [
'headers' => [
'Authorization' => "Bearer {$this->user_token}",
'Content-Type' => 'application/json'
]
];
$maxRetries = 3;
$initialDelay = 1;
$retriedAfter401 = false;
for ($retryCount = 0; $retryCount < $maxRetries; $retryCount++) {
try {
$response = $this->client->get($url, $options);
$responseData = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
if (empty($responseData['data']) || !is_array($responseData['data'])) {
return true;
}
$data = $responseData['data'];
$detailCreatedAt = isset($data['created_at'])
? Carbon::parse($data['created_at'])->format('Y-m-d H:i:s')
: null;
$detailUpdatedAt = isset($data['updated_at'])
? Carbon::parse($data['updated_at'])->format('Y-m-d H:i:s')
: null;
$pbg_task_retributions = PbgTaskRetributions::updateOrCreate(
['detail_id' => $data['id']],
[
'detail_uid' => $data['uid'] ?? null,
'detail_created_at' => $detailCreatedAt ?? null,
'detail_updated_at' => $detailUpdatedAt ?? null,
'luas_bangunan' => $data['luas_bangunan'] ?? null,
'indeks_lokalitas' => $data['indeks_lokalitas'] ?? null,
'wilayah_shst' => $data['wilayah_shst'] ?? null,
'kegiatan_id' => $data['kegiatan']['id'] ?? null,
'kegiatan_name' => $data['kegiatan']['name'] ?? null,
'nilai_shst' => $data['nilai_shst'] ?? null,
'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null,
'indeks_bg_terbangun' => $data['indeks_bg_terbangun'] ?? null,
'nilai_retribusi_bangunan' => $data['nilai_retribusi_bangunan'] ?? null,
'nilai_prasarana' => $data['nilai_prasarana'] ?? null,
'created_by' => $data['created_by'] ?? null,
'pbg_document' => $data['pbg_document'] ?? null,
'underpayment' => $data['underpayment'] ?? null,
'skrd_amount' => $data['skrd_amount'] ?? null,
'pbg_task_uid' => $uuid,
]
);
$pbg_task_retribution_id = $pbg_task_retributions->id;
$prasaranaData = $data['prasarana'] ?? [];
if (!empty($prasaranaData)) {
$insertData = array_map(fn($item) => [
'pbg_task_uid' => $uuid,
'pbg_task_retribution_id' => $pbg_task_retribution_id,
'prasarana_id' => $item['id'] ?? null,
'prasarana_type' => $item['prasarana_type'] ?? null,
'building_type' => $item['building_type'] ?? null,
'total' => $item['total'] ?? null,
'quantity' => $item['quantity'] ?? null,
'unit' => $item['unit'] ?? null,
'index_prasarana' => $item['index_prasarana'] ?? null,
], $prasaranaData);
PbgTaskPrasarana::upsert($insertData, ['prasarana_id']);
}
return $responseData;
} catch (\GuzzleHttp\Exception\ClientException $e) {
if ($e->getCode() === 401 && !$retriedAfter401) {
Log::warning("401 Unauthorized - Refreshing token and retrying...");
$this->refreshToken();
$options['headers']['Authorization'] = "Bearer {$this->user_token}";
$retriedAfter401 = true;
continue;
}
return false;
} catch (\GuzzleHttp\Exception\ServerException | \GuzzleHttp\Exception\ConnectException $e) {
if ($e->getCode() === 502) {
Log::warning("502 Bad Gateway - Retrying in {$initialDelay} seconds...");
} else {
Log::error("Network error ({$e->getCode()}) - Retrying in {$initialDelay} seconds...");
}
sleep($initialDelay);
$initialDelay *= 2;
} catch (\GuzzleHttp\Exception\RequestException $e) {
Log::error("Request error ({$e->getCode()}): " . $e->getMessage());
return false;
} catch (\JsonException $e) {
Log::error("JSON decoding error: " . $e->getMessage());
return false;
} catch (\Throwable $e) {
Log::critical("Unhandled error: " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
return false;
}
}
Log::error("Failed to fetch task retributions for UUID {$uuid} after retries.");
throw new \Exception("Failed to fetch task retributions for UUID {$uuid} after retries.");
}
private function scraping_task_integrations($uuid){
$url = "{$this->simbg_host}/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/";
$options = [
'headers' => [
'Authorization' => "Bearer {$this->user_token}",
'Content-Type' => 'application/json'
]
];
$maxRetries = 3;
$initialDelay = 1;
$retriedAfter401 = false;
for ($retryCount = 0; $retryCount < $maxRetries; $retryCount++) {
try {
$response = $this->client->get($url, $options);
$responseData = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
if (empty($responseData['data']) || !is_array($responseData['data'])) {
return true;
}
$data = $responseData['data'];
$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,
'indeks_parameter_ketinggian' => $data['indeks_parameter_ketinggian'] ?? null,
'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null,
'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null,
'total' => $data['total'] ?? null,
];
if (!empty($integrations)) {
PbgTaskIndexIntegrations::upsert($integrations, ['pbg_task_uid'], ['indeks_fungsi_bangunan',
'indeks_parameter_kompleksitas', 'indeks_parameter_permanensi', 'indeks_parameter_ketinggian', 'faktor_kepemilikan', 'indeks_terintegrasi', 'total']);
}
return $responseData;
} catch (\GuzzleHttp\Exception\ClientException $e) {
if ($e->getCode() === 401 && !$retriedAfter401) {
Log::warning("401 Unauthorized - Refreshing token and retrying...");
$this->refreshToken();
$options['headers']['Authorization'] = "Bearer {$this->user_token}";
$retriedAfter401 = true;
continue;
}
return false;
} catch (\GuzzleHttp\Exception\ServerException | \GuzzleHttp\Exception\ConnectException $e) {
if ($e->getCode() === 502) {
Log::warning("502 Bad Gateway - Retrying in {$initialDelay} seconds...");
} else {
Log::error("Network error ({$e->getCode()}) - Retrying in {$initialDelay} seconds...");
}
sleep($initialDelay);
$initialDelay *= 2;
} catch (\GuzzleHttp\Exception\RequestException $e) {
Log::error("Request error ({$e->getCode()}): " . $e->getMessage());
return false;
} catch (\JsonException $e) {
Log::error("JSON decoding error: " . $e->getMessage());
return false;
} catch (\Throwable $e) {
Log::critical("Unhandled error: " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
return false;
}
}
Log::error("Failed to fetch task index integration for UUID {$uuid} after retries.");
throw new \Exception("Failed to fetch task index integration for UUID {$uuid} after retries.");
}
private function refreshToken()
{
try {
$newAuthToken = $this->service_token->refresh_token($this->user_refresh_token);
$this->user_token = $newAuthToken['access'];
$this->user_refresh_token = $newAuthToken['refresh'];
if (!$this->user_token) {
Log::error("Token refresh failed: No token received.");
throw new \Exception("Failed to refresh token.");
}
Log::info("Token refreshed successfully.");
} catch (\Exception $e) {
Log::error("Token refresh error: " . $e->getMessage());
throw new \Exception("Token refresh failed.");
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace App\Services;
use App\Models\GlobalSetting;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Log;
class ServiceTokenSIMBG
{
private $client;
private $login_url;
private $email;
private $password;
private $simbg_host;
private $fetch_per_page;
private $refresh_url;
public function __construct()
{
$settings = GlobalSetting::whereIn('key', [
'SIMBG_EMAIL', 'SIMBG_PASSWORD', 'SIMBG_HOST', 'FETCH_PER_PAGE'
])->pluck('value', 'key');
$this->email = trim((string) ($settings['SIMBG_EMAIL'] ?? ""));
$this->password = trim((string) ($settings['SIMBG_PASSWORD'] ?? ""));
$this->simbg_host = trim((string) ($settings['SIMBG_HOST'] ?? ""));
$this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? ""));
$this->client = new Client();
$this->login_url = $this->simbg_host . "/api/user/v1/auth/login/";
$this->refresh_url = $this->simbg_host. "/api/user/v1/auth/token/refresh/";
}
public function get_token(){
try {
$response = $this->client->request('POST', $this->login_url, [
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'email' => $this->email,
'password' => $this->password
]
]);
$data = json_decode($response->getBody()->getContents(), true);
return $data['token'];
} catch (RequestException $e) {
Log::error("Failed to get token", [
'error' => $e->getMessage(),
'response' => $e->getResponse() ? $e->getResponse()->getBody()->getContents() : null
]);
return null;
}
}
public function refresh_token(string $refresh_token){
try {
$response = $this->client->request('POST', $this->refresh_url, [
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'refresh' => $refresh_token
]
]);
$data = json_decode($response->getBody()->getContents(), true);
return $data;
} catch (\Throwable $th) {
Log::error("Failed to refresh token", [
'error' => $th->getMessage()
]);
return null;
}
}
}

View File

@@ -79,6 +79,8 @@ return [
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
PDO::ATTR_TIMEOUT => 40000,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION wait_timeout=40000; SET SESSION interactive_timeout=40000;"
]) : [],
],

View File

@@ -112,7 +112,7 @@ return [
// set timeout queue
'worker' => [
'timeout' => 300
'timeout' => 40000
]
];

View File

@@ -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('task_assignments', function (Blueprint $table) {
$table->json('expertise')->nullable()->change();
$table->json('experience')->nullable()->change();
$table->bigInteger('ta_id')->nullable()->after('id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('task_assignments', function (Blueprint $table) {
$table->text('expertise')->nullable()->change();
$table->text('experience')->nullable()->change();
$table->dropColumn('ta_id');
});
}
};

View File

@@ -0,0 +1,40 @@
<?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('task_assignments', function (Blueprint $table) {
$indexes = DB::select("SHOW INDEXES FROM task_assignments WHERE Key_name = 'task_assignments_email_unique'");
if (!empty($indexes)) {
$table->dropUnique('task_assignments_email_unique');
}
$indexes = DB::select("SHOW INDEXES FROM task_assignments WHERE Key_name = 'task_assignments_username_unique'");
if (!empty($indexes)) {
$table->dropUnique('task_assignments_username_unique');
}
$table->string('email')->nullable()->change();
$table->string('username')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('task_assignments', function (Blueprint $table) {
//
});
}
};

View File

@@ -15,17 +15,23 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
// User::factory(10)->create();
User::updateOrCreate(
['email' => 'user@demo.com'], // Kondisi pencarian
[
'name' => 'Darkone',
'email_verified_at' => now(),
'password' => Hash::make('password'),
'firstname' => 'John',
'lastname' => 'Doe',
'position' => 'crusial',
'remember_token' => Str::random(10),
]
);
User::factory()->create([
'name' => 'Darkone',
'email' => 'user@demo.com',
'email_verified_at' => now(),
'password' => Hash::make('password'),
'firstname' => 'John',
'lastname' => 'Doe',
'position' => 'crusial',
'remember_token' => Str::random(10),
$this->call([
RoleSeeder::class,
MenuSeeder::class,
UsersRoleMenuSeeder::class
]);
}
}

View File

@@ -0,0 +1,287 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Menu;
use Illuminate\Support\Arr;
class MenuSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$menus = [
[
"name" => "Dashboard",
"url" => "/dashboard",
"icon" => "mingcute:home-3-line",
"parent_id" => null,
"sort_order" => 1,
"children" => [
[
"name" => "Dashboard Pimpinan",
"url" => "dashboard.home",
"icon" => null,
"sort_order" => 1,
],
[
"name" => "Dashboard PBG",
"url" => "dashboard.pbg",
"icon" => null,
"sort_order" => 2,
],
[
"name" => "Dashboard Potensi",
"url" => '/potentials',
"icon" => null,
"sort_order" => 3,
"children" => [
[
"name" => "Luar Sistem",
"url" => "dashboard.potentials.inside_system",
"icon" => null,
"sort_order" => 1,
],
[
"name" => "Dalam Sistem",
"url" => "dashboard.potentials.outside_system",
"icon" => null,
"sort_order" => 2,
],
]
],
[
"name" => "PETA",
"url" => "dashboard.maps",
"icon" => null,
"sort_order" => 4,
],
],
],
[
"name" => "Master",
"url" => "/master",
"icon" => "mingcute:cylinder-line",
"parent_id" => null,
"sort_order" => 2,
"children" => [
[
"name" => "Users",
"url" => "users.index",
"icon" => null,
"sort_order" => 1,
],
]
],
[
"name" => "Settings",
"url" => "/settings",
"icon" => "mingcute:settings-6-line",
"parent_id" => null,
"sort_order" => 3,
"children" => [
[
"name" => "Syncronize",
"url" => "settings.syncronize",
"icon" => null,
"sort_order" => 1,
],
[
"name" => "Menu",
"url" => "menus.index",
"icon" => null,
"sort_order" => 2,
],
[
"name" => "Role",
"url" => "roles.index",
"icon" => null,
"sort_order" => 3,
],
]
],
[
"name" => "Data Settings",
"url" => "/data-settings",
"icon" => "mingcute:settings-1-line",
"parent_id" => null,
"sort_order" => 4,
"children" => [
[
"name" => "Setting Dashboard",
"url" => "data-settings.index",
"icon" => null,
"sort_order" => 1,
],
]
],
[
"name" => "Data",
"url" => "/data",
"icon" => "mingcute:task-line",
"parent_id" => null,
"sort_order" => 5,
"children" => [
[
"name" => "PBG",
"url" => "pbg-task.index",
"icon" => null,
"sort_order" => 1,
],
[
"name" => "Reklame",
"url" => "web-advertisements.index",
"icon" => null,
"sort_order" => 2,
],
[
"name" => "Usaha atau Industri",
"url" => "business-industries.index",
"icon" => null,
"sort_order" => 3,
],
[
"name" => "UMKM",
"url" => "web-umkm.index",
"icon" => null,
"sort_order" => 4,
],
[
"name" => "Pariwisata",
"url" => "web-tourisms.index",
"icon" => null,
"sort_order" => 5,
],
[
"name" => "Tata Ruang",
"url" => "web-spatial-plannings.index",
"icon" => null,
"sort_order" => 6,
],
[
"name" => "PDAM",
"url" => "customers",
"icon" => null,
"sort_order" => 7,
],
[
"name" => "Google Sheets",
"url" => "google-sheets",
"icon" => null,
"sort_order" => 8,
],
[
"name" => "TPA TPT",
"url" => "tpa-tpt.index",
"icon" => null,
"sort_order" => 9,
],
]
],
[
"name" => "Laporan",
"url" => "/laporan",
"icon" => "mingcute:report-line",
"parent_id" => null,
"sort_order" => 6,
"children" => [
[
"name" => "Lap Pariwisata",
"url" => "tourisms-report.index",
"icon" => null,
"sort_order" => 1,
],
[
"name" => "Lap Pimpinan",
"url" => "bigdata-resumes",
"icon" => null,
"sort_order" => 2,
],
[
"name" => "Rekap Pembayaran",
"url" => "payment-recaps",
"icon" => null,
"sort_order" => 3,
],
[
"name" => "Lap Rekap Data Pembayaran",
"url" => "report-payment-recaps",
"icon" => null,
"sort_order" => 4,
],
[
"name" => "Lap PBG (PTSP)",
"url" => "report-pbg-ptsp",
"icon" => null,
"sort_order" => 5,
],
]
],
[
"name" => "Neng Bedas",
"url" => "/chat",
"icon" => "mingcute:wechat-line",
"parent_id" => null,
"sort_order" => 7,
"children" => [
[
"name" => "Chat",
"url" => "main-chatbot.index",
"icon" => null,
"sort_order" => 1,
],
]
],
[
"name" => "Approval",
"url" => "/approval",
"icon" => "mingcute:user-follow-2-line",
"parent_id" => null,
"sort_order" => 8,
"children" => [
[
"name" => "Approval Pejabat",
"url" => "approval-list",
"icon" => null,
"sort_order" => 1,
],
]
],
[
"name" => "Tools",
"url" => "/tools",
"icon" => "mingcute:tool-line",
"parent_id" => null,
"sort_order" => 9,
"children" => [
[
"name" => "Undangan",
"url" => "invitations",
"icon" => null,
"sort_order" => 1,
],
]
],
];
foreach ($menus as $menuData) {
$this->createOrUpdateMenu($menuData);
}
}
private function createOrUpdateMenu($menuData, $parentId = null){
$menuData['parent_id'] = $parentId;
$menu = Menu::updateOrCreate(['name' => $menuData['name']], Arr::except($menuData, ['children']));
if(!empty($menuData['children'])){
foreach($menuData['children'] as $child){
$this->createOrUpdateMenu($child, $menu->id);
}
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Database\Seeders;
use App\Models\Role;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class RoleSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$roles = [
[
"name" => "superadmin",
"description" => "show all menus for super admins",
],
[
"name" => "admin",
"description" => "show only necessary menus for admins",
],
[
"name" => "operator",
"description" => "show only necessary menus for operators",
],
[
"name" => "user",
"description" => "show only necessary menus for users",
]
];
Role::upsert($roles, ['name']);
}
}

View File

@@ -13,301 +13,54 @@ class UsersRoleMenuSeeder extends Seeder
/**
* Run the database seeds.
*/
public function run(): void
public function run(): void
{
$roles = [
[
"name" => "superadmin",
"description" => "show all menus for super admins",
// Fetch roles in a single query
$roles = Role::whereIn('name', ['superadmin', 'admin', 'operator'])->get()->keyBy('name');
// Fetch all menus in a single query and index by name
$menus = Menu::whereIn('name', [
'Dashboard', 'Master', 'Settings', 'Data Settings', 'Data', 'Laporan', 'Neng Bedas',
'Approval', 'Tools', 'Dashboard Pimpinan', 'Dashboard PBG', 'Users', 'Syncronize',
'Menu', 'Role', 'Setting Dashboard', 'PBG', 'Reklame', 'Usaha atau Industri', 'Pariwisata',
'Lap Pariwisata', 'UMKM', 'Dashboard Potensi', 'Tata Ruang', 'PDAM', 'PETA',
'Lap Pimpinan', 'Chat', 'Dalam Sistem', 'Luar Sistem', 'Google Sheets', 'TPA TPT',
'Approval Pejabat', 'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)'
])->get()->keyBy('name');
// Define access levels for each role
$permissions = [
'superadmin' => [
'Dashboard', 'Master', 'Settings', 'Data Settings', 'Data', 'Laporan', 'Neng Bedas',
'Approval', 'Tools', 'Dashboard Pimpinan', 'Dashboard PBG', 'Users', 'Syncronize',
'Menu', 'Role', 'Setting Dashboard', 'PBG', 'Reklame', 'Usaha atau Industri', 'Pariwisata',
'Lap Pariwisata', 'UMKM', 'Dashboard Potensi', 'Tata Ruang', 'PDAM', 'Dalam Sistem',
'Luar Sistem', 'Lap Pimpinan', 'Chat', 'Google Sheets', 'TPA TPT', 'Approval Pejabat',
'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)'
],
[
"name" => "admin",
"description" => "show only necessary menus for admins",
],
[
"name" => "operator",
"description" => "show only necessary menus for operators",
]
'admin' => ['Dashboard', 'Master', 'Settings'],
'operator' => ['Dashboard', 'Data', 'Laporan']
];
Role::upsert($roles, ['name']);
// Define permission levels
$superadminPermissions = ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true];
$adminPermissions = ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true];
$operatorPermissions = ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false];
$parent_menus = [
[
"name" => "Dashboard",
"url" => "/dashboard",
"icon" => "mingcute:home-3-line",
"parent_id" => null,
"sort_order" => 1,
],
[
"name" => "Master",
"url" => "/master",
"icon" => "mingcute:cylinder-line",
"parent_id" => null,
"sort_order" => 2,
],
[
"name" => "Settings",
"url" => "/settings",
"icon" => "mingcute:settings-6-line",
"parent_id" => null,
"sort_order" => 3,
],
[
"name" => "Data Settings",
"url" => "/data-settings",
"icon" => "mingcute:settings-1-line",
"parent_id" => null,
"sort_order" => 4,
],
[
"name" => "Data",
"url" => "/data",
"icon" => "mingcute:task-line",
"parent_id" => null,
"sort_order" => 5,
],
[
"name" => "Laporan",
"url" => "/laporan",
"icon" => "mingcute:report-line",
"parent_id" => null,
"sort_order" => 6,
],
[
"name" => "Neng Bedas",
"url" => "/chat",
"icon" => "mingcute:wechat-line",
"parent_id" => null,
"sort_order" => 7,
]
];
foreach ($parent_menus as $parent_menu) {
Menu::firstOrCreate(['name' => $parent_menu['name']], $parent_menu);
// Assign menus to roles
foreach ($permissions as $roleName => $menuNames) {
$role = $roles[$roleName] ?? null;
if ($role) {
$role->menus()->sync(
collect($menuNames)->mapWithKeys(fn($menuName) => [
$menus[$menuName]->id => ($roleName === 'superadmin' ? $superadminPermissions :
($roleName === 'admin' ? $adminPermissions : $operatorPermissions))
])->toArray()
);
}
}
// Attach Menus to Roles
$superadmin = Role::where('name', 'superadmin')->first();
$admin = Role::where('name', 'admin')->first();
$operator = Role::where('name', 'operator')->first();
$dashboard = Menu::where('name', 'Dashboard')->first();
$master = Menu::where('name', 'Master')->first();
$settings = Menu::where('name', 'Settings')->first();
$dataSettings = Menu::where('name', 'Data Settings')->first();
$data = Menu::where('name', 'Data')->first();
$laporan = Menu::where('name', 'Laporan')->first();
$chat_bedas = Menu::where('name', 'Neng Bedas')->first();
// create children menu
$children_menus = [
[
"name" => "Dashboard Pimpinan",
"url" => "dashboard.home",
"icon" => null,
"parent_id" => $dashboard->id,
"sort_order" => 1,
],
[
"name" => "Dashboard PBG",
"url" => "dashboard.pbg",
"icon" => null,
"parent_id" => $dashboard->id,
"sort_order" => 2,
],
[
"name" => "Dashboard Potensi",
"url" => "dashboard.lack_of_potential",
"icon" => null,
"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",
"icon" => null,
"parent_id" => $master->id,
"sort_order" => 1,
],
[
"name" => "Syncronize",
"url" => "settings.syncronize",
"icon" => null,
"parent_id" => $settings->id,
"sort_order" => 1,
],
[
"name" => "Menu",
"url" => "menus.index",
"icon" => null,
"parent_id" => $settings->id,
"sort_order" => 2,
],
[
"name" => "Role",
"url" => "roles.index",
"icon" => null,
"parent_id" => $settings->id,
"sort_order" => 3,
],
[
"name" => "Setting Dashboard",
"url" => "data-settings.index",
"icon" => null,
"parent_id" => $dataSettings->id,
"sort_order" => 1,
],
[
"name" => "PBG",
"url" => "pbg-task.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 1,
],
[
"name" => "Reklame",
"url" => "web.advertisements.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 2,
],
[
"name" => "Usaha atau Industri",
"url" => "business-industries.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 3,
],
[
"name" => "UMKM",
"url" => "web-umkm.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 4,
],
[
"name" => "Pariwisata",
"url" => "web-tourisms.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 5,
],
[
"name" => "Tata Ruang",
"url" => "web-spatial-plannings.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 6,
],
[
"name" => "PDAM",
"url" => "customers",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 7,
],
[
"name" => "Lap Pariwisata",
"url" => "tourisms-report.index",
"icon" => null,
"parent_id" => $laporan->id,
"sort_order" => 1,
],
[
"name" => "Lap Pimpinan",
"url" => "bigdata-resumes",
"icon" => null,
"parent_id" => $laporan->id,
"sort_order" => 2,
],
[
"name" => "Chat",
"url" => "main-chatbot.index",
"icon" => null,
"parent_id" => $chat_bedas->id,
"sort_order" => 1,
],
];
foreach ($children_menus as $child_menu) {
Menu::firstOrCreate(['name' => $child_menu['name']], $child_menu);
}
$dashboard_pimpinan = Menu::where('name', 'Dashboard Pimpinan')->first();
$dashboard_pbg = Menu::where('name', 'Dashboard PBG')->first();
$users = Menu::where('name', 'Users')->first();
$syncronize = Menu::where('name', 'Syncronize')->first();
$setting_menu = Menu::where('name', 'Menu')->first();
$setting_role = Menu::where('name', 'Role')->first();
$setting_dashboard = Menu::where('name', 'Setting Dashboard')->first();
$setting_pbg = Menu::where('name', 'PBG')->first();
$reklame = Menu::where('name', 'Reklame')->first();
$businessIndustries = Menu::where('name', 'Usaha atau Industri')->first();
$pariwisata = Menu::where('name', 'Pariwisata')->first();
$laporan_pariwisata = Menu::where('name', 'Lap Pariwisata')->first();
$umkm = Menu::where('name', 'UMKM')->first();
$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();
$chatbot = Menu::where('name', 'Chat')->first();
// Superadmin gets all menus
$superadmin->menus()->sync([
// parent
$dashboard->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$master->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$settings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$dataSettings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$data->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$laporan->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$chat_bedas->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
// children
$dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$users->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$syncronize->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$setting_menu->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$setting_role->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$setting_dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$setting_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$reklame->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$businessIndustries->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$pariwisata->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$laporan_pariwisata->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$umkm->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$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],
$chatbot->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
]);
// Admin gets limited menus
$admin->menus()->sync([
$dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$master->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$settings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
]);
// Operator gets only basic menus with full permissions
$operator->menus()->sync([
$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]);
User::findOrFail(1)->roles()->sync([$roles['superadmin']->id]);
}
}

View File

@@ -19,6 +19,9 @@ COMPOSER_ALLOW_SUPERUSER=1 composer install --no-interaction --optimize-autoload
echo "🗄️ Running migrations..."
php artisan migrate --force
echo "Running seeders..."
php artisan db:seed
echo "⚡ Optimizing application..."
php artisan optimize:clear

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/leaflet/layers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

View File

@@ -2,30 +2,6 @@ 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() {
@@ -131,7 +107,6 @@ class FormValidation {
}
document.addEventListener("DOMContentLoaded", function (e) {
new Components().init(), new FormValidation().init();
// new InitDatePicker().init();
});
class ThemeLayout {
constructor() {

View File

@@ -0,0 +1,90 @@
import { Grid } from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config";
class Approval {
constructor() {
this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
this.initTableApproval();
}
initTableApproval() {
let tableContainer = document.getElementById("table-approvals");
this.table = new Grid({
columns: [
"ID",
{ name: "Name", width: "15%" },
{ name: "Condition", width: "7%" },
"Registration Number",
"Document Number",
{ name: "Address", width: "30%" },
"Status",
"Function Type",
"Consultation Type",
{ name: "Due Date", width: "10%" },
{
name: "Action",
formatter: (cell) => {
return gridjs.html(`
<div class="d-flex justify-content-center align-items-center gap-2">
<button class="btn btn-sm btn-success approve-btn" data-id="${cell}">
Approve
</button>
<button class="btn btn-sm btn-danger reject-btn" data-id="${cell}">
Reject
</button>
</div>
`);
},
},
],
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
server: {
url: `${GlobalConfig.apiHost}/api/request-assignments`,
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.condition,
item.registration_number,
item.document_number,
item.address,
item.status_name,
item.function_type,
item.consultation_type,
item.due_date,
item.id,
]),
total: (data) => data.meta.total,
},
}).render(tableContainer);
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new Approval();
});

View File

@@ -2,7 +2,6 @@ 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 {
@@ -13,23 +12,16 @@ class BigdataResume {
this.table = null;
// Initialize functions
this.initTableDataSettings();
// this.initEvents();
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);
}
});
async initEvents() {
await this.initBigdataResumeTable();
// this.handleSearch();
}
initTableDataSettings() {
async initBigdataResumeTable() {
let tableContainer = document.getElementById("table-bigdata-resumes");
this.table = new Grid({
columns: [
{ name: "ID" },
@@ -53,7 +45,9 @@ class BigdataResume {
{ name: "Total Proses Dinas Teknis" },
{
name: "Created",
attributes: { style: "width: 200px; white-space: nowrap;" }, // Set width dynamically
attributes: {
style: "width: 200px; white-space: nowrap;",
},
},
],
pagination: {
@@ -66,11 +60,12 @@ class BigdataResume {
},
},
sort: true,
// search: {
// server: {
// url: (prev, keyword) => `${prev}?search=${keyword}`,
// },
// },
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/bigdata-report`,
headers: {
@@ -110,61 +105,72 @@ class BigdataResume {
total: (data) => data.total,
},
width: "auto",
}).render(tableContainer);
}
handleSearch() {}
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!",
fixedHeader: true,
});
if (result.isConfirmed) {
try {
let response = await fetch(
`${GlobalConfig.apiHost}/api/data-settings/${id}`,
{
method: "DELETE",
credentials: "include",
return new Promise((resolve) => {
this.table.render(tableContainer);
this.table.on("ready", resolve); // Tunggu event "ready"
});
}
handleSearch() {
document.getElementById("search-btn").addEventListener("click", () => {
let searchValue = document.getElementById("search-box").value;
if (!this.table) {
// Ensure table is initialized
console.error("Table element not found!");
return;
}
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/bigdata-report?search=${searchValue}`,
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();
}
}
then: (data) => {
return 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),
item.waiting_click_dpmptsp_count,
addThousandSeparators(
item.waiting_click_dpmptsp_sum
),
item.issuance_realization_pbg_count,
addThousandSeparators(
item.issuance_realization_pbg_sum
),
item.process_in_technical_office_count,
addThousandSeparators(
item.process_in_technical_office_sum
),
moment(item.created_at).format(
"YYYY-MM-DD H:mm:ss"
),
]);
},
total: (data) => data.total,
},
})
.forceRender();
});
}
}
document.addEventListener("DOMContentLoaded", function (e) {

View File

@@ -12,6 +12,8 @@ const spinner = document.getElementById("spinner");
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
(dropzonePreviewNode.id = ""),
dropzonePreviewNode &&
((previewTemplate = dropzonePreviewNode.parentNode.innerHTML),
@@ -34,7 +36,7 @@ const toast = new bootstrap.Toast(toastNotification);
response.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/business-industries";
window.location.href = `/data/business-industries?menu_id=${menuId}`;
}, 2000);
});
this.on("error", function (file, errorMessage) {

View File

@@ -35,7 +35,8 @@ class BusinessIndustries {
tableContainer.innerHTML = "";
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
let canDelete = tableContainer.getAttribute("data-destroyer") === "1";
let menuId = tableContainer.getAttribute("data-menuId");
// Create a new Grid.js instance only if it doesn't exist
this.table = new Grid({
columns: [
@@ -56,17 +57,16 @@ class BusinessIndustries {
{
name: "Action",
formatter: (cell) => {
let buttons = `<div class="d-flex justify-content-center gap-2">`;
if (canUpdate) {
buttons += `
<a href="/data/business-industries/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<a href="/data/business-industries/${cell}/edit?menu_id=${menuId}" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
`;
}
if (canDelete) {
buttons += `
<button data-id="${cell}" class="btn btn-sm btn-red btn-delete-business-industry d-inline-flex align-items-center justify-content-center">
@@ -74,9 +74,9 @@ class BusinessIndustries {
</button>
`;
}
buttons += `</div>`;
return gridjs.html(buttons);
},
},
@@ -95,6 +95,7 @@ class BusinessIndustries {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/api-business-industries`,

View File

@@ -13,6 +13,8 @@ class UpdateBusinessIndustries {
const spinner = document.getElementById("spinner");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
if (!submitButton) {
console.error("Error: Submit button not found!");
return;
@@ -53,7 +55,7 @@ class UpdateBusinessIndustries {
data.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/business-industries";
window.location.href = `/data/business-industries?menu_id=${menuId}`;
}, 2000);
} else {
// Show error toast with message from API

View File

@@ -6,6 +6,7 @@ class CreateCustomer {
initCreateCustomer() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
document
.getElementById("btnCreateCustomer")
.addEventListener("click", async function () {
@@ -41,7 +42,7 @@ class CreateCustomer {
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/customers";
window.location.href = `/data/customers?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -6,6 +6,7 @@ class UpdateCustomer {
initUpdateCustomer() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
document
.getElementById("btnUpdateCustomer")
.addEventListener("click", async function () {
@@ -41,7 +42,7 @@ class UpdateCustomer {
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/customers";
window.location.href = `/data/customers?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -32,6 +32,7 @@ class Customers {
tableContainer.innerHTML = "";
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
let canDelete = tableContainer.getAttribute("data-destroyer") === "1";
let menuId = tableContainer.getAttribute("data-menuId");
this.table = new Grid({
columns: [
"ID",
@@ -45,15 +46,15 @@ class Customers {
name: "Action",
formatter: (cell) => {
let buttons = "";
if (canUpdate) {
buttons += `
<a href="/data/customers/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<a href="/data/customers/${cell}/edit?menu_id=${menuId}" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
`;
}
if (canDelete) {
buttons += `
<button data-id="${cell}" class="btn btn-sm btn-red btn-delete-customers d-inline-flex align-items-center justify-content-center">
@@ -61,12 +62,14 @@ class Customers {
</button>
`;
}
if (!canUpdate && !canDelete) {
buttons = `<span class="text-muted">No Privilege</span>`;
}
return gridjs.html(`<div class="d-flex justify-content-center gap-2">${buttons}</div>`);
return gridjs.html(
`<div class="d-flex justify-content-center gap-2">${buttons}</div>`
);
},
},
],
@@ -84,6 +87,7 @@ class Customers {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/customers`,
@@ -109,6 +113,8 @@ class Customers {
}).render(tableContainer);
}
handleSearch() {}
async handleDelete(deleteButton) {
const id = deleteButton.getAttribute("data-id");

View File

@@ -20,6 +20,7 @@ class UploadCustomers {
initDropzone() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
var previewTemplate,
dropzonePreviewNode = document.querySelector(
"#dropzone-preview-list"
@@ -46,7 +47,7 @@ class UploadCustomers {
response.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/customers";
window.location.href = `/data/customers?menu_id=${menuId}`;
}, 2000);
});
this.on("error", function (file, errorMessage) {

View File

@@ -0,0 +1,194 @@
import Big from "big.js";
import GlobalConfig, { addThousandSeparators } from "../../global-config.js";
import InitDatePicker from "../../utils/InitDatePicker.js";
class DashboardPotentialInsideSystem {
async init() {
new InitDatePicker(
"#datepicker-lack-of-potential",
this.handleChangedDate.bind(this)
).init();
this.bigTotalLackPotential = 0;
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;
let dataReportTourism = this.allCountData.data_report;
this.totalVilla = dataReportTourism
.filter((item) => item.kbli_title.toLowerCase() === "vila")
.reduce((sum, item) => sum + item.total_records, 0);
this.totalRestoran = dataReportTourism
.filter((item) => item.kbli_title.toLowerCase() === "restoran")
.reduce((sum, item) => sum + item.total_records, 0);
this.totalPariwisata = dataReportTourism.reduce(
(sum, item) => sum + item.total_records,
0
);
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
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(filterDate) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterDate}`,
{
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 {
total: data.total_potensi.sum,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return null;
}
}
async getDataSettings(string_key) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/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;
}
}
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")
.forEach((element) => {
element.innerText = ``;
});
document
.querySelectorAll(".document-total.chart-lack-of-potential")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
this.bigTotalLackPotential.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-lack-of-potential")
.forEach((element) => {
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.getElementById("tata-ruang-count").innerText =
this.tataRuangCount;
document.getElementById("tata-ruang-usaha-count").innerText =
this.tataRuangCount;
document.getElementById("restoran-count").innerText =
this.totalRestoran;
document.getElementById("villa-count").innerText = this.totalVilla;
document.getElementById("pariwisata-count").innerText =
this.totalPariwisata;
}
}
document.addEventListener("DOMContentLoaded", async function (e) {
await new DashboardPotentialInsideSystem().init();
});
function resizeDashboard() {
let targetElement = document.getElementById("lack-of-potential-wrapper");
let dashboardElement = document.getElementById(
"lack-of-potential-fixed-container"
);
let targetWidth = targetElement.offsetWidth;
let dashboardWidth = 1400;
let scaleFactor = (targetWidth / dashboardWidth).toFixed(2);
// Prevent scaling beyond 1 (100%) to avoid overflow
scaleFactor = Math.min(scaleFactor, 1);
dashboardElement.style.transformOrigin = "left top";
dashboardElement.style.transition = "transform 0.2s ease-in-out";
dashboardElement.style.transform = `scale(${scaleFactor})`;
// Ensure horizontal scrolling is allowed if necessary
document.body.style.overflowX = "auto";
}
window.addEventListener("load", resizeDashboard);
window.addEventListener("resize", resizeDashboard);

View File

@@ -0,0 +1,121 @@
import InitDatePicker from "../../utils/InitDatePicker.js";
import GlobalConfig, { addThousandSeparators } from "../../global-config.js";
class DashboardPotentialOutsideSystem {
async init() {
new InitDatePicker(
"#datepicker-outside-system",
this.handleChangedDate.bind(this)
).init();
this.bigTotalLackPotential = 0;
this.dataResume = await this.getBigDataResume("latest");
console.log(this.dataResume);
this.initChartNonBusiness();
this.initChartBusiness();
}
async handleChangedDate(filterDate) {
this.dataResume = await this.getBigDataResume(filterDate);
this.initChartNonBusiness();
this.initChartBusiness();
}
async getBigDataResume(filterDate) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterDate}`,
{
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);
}
return await response.json();
} catch (error) {
console.error("Error fetching chart data:", error);
return null;
}
}
initChartNonBusiness() {
const nonBusinessDoc = this.dataResume?.non_business_document ?? {};
document
.querySelectorAll(".document-count.outside-system-non-business")
.forEach((element) => {
element.innerText = `${nonBusinessDoc.count ?? 0}`;
});
document
.querySelectorAll(".document-total.outside-system-non-business")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
(nonBusinessDoc.sum ?? 0).toString()
)}`;
});
document
.querySelectorAll(".small-percentage.outside-system-non-business")
.forEach((element) => {
element.innerText = `${nonBusinessDoc.percentage ?? 0}%`;
});
}
initChartBusiness() {
const businessDoc = this.dataResume?.business_document ?? {};
document
.querySelectorAll(".document-count.outside-system-business")
.forEach((element) => {
element.innerText = `${businessDoc.count ?? 0}`;
});
document
.querySelectorAll(".document-total.outside-system-business")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
(businessDoc.sum ?? 0).toString()
)}`;
});
document
.querySelectorAll(".small-percentage.outside-system-business")
.forEach((element) => {
element.innerText = `${businessDoc.percentage ?? 0}%`;
});
}
}
document.addEventListener("DOMContentLoaded", async function (e) {
await new DashboardPotentialOutsideSystem().init();
});
function resizeDashboard() {
let targetElement = document.getElementById("outside-system-wrapper");
let dashboardElement = document.getElementById(
"outside-system-fixed-container"
);
let targetWidth = targetElement.offsetWidth;
let dashboardWidth = 1400;
let scaleFactor = (targetWidth / dashboardWidth).toFixed(2);
// Prevent scaling beyond 1 (100%) to avoid overflow
scaleFactor = Math.min(scaleFactor, 1);
dashboardElement.style.transformOrigin = "left top";
dashboardElement.style.transition = "transform 0.2s ease-in-out";
dashboardElement.style.transform = `scale(${scaleFactor})`;
// Ensure horizontal scrolling is allowed if necessary
document.body.style.overflowX = "auto";
}
window.addEventListener("load", resizeDashboard);
window.addEventListener("resize", resizeDashboard);

View File

@@ -1,6 +1,7 @@
document.addEventListener("DOMContentLoaded", function (e) {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
document
.getElementById("btnCreateDataSettings")
.addEventListener("click", async function () {
@@ -37,7 +38,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
result.data.message;
toast.show();
setTimeout(() => {
window.location.href = "/data-settings";
window.location.href = `/data-settings?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -32,7 +32,8 @@ class DataSettings {
tableContainer.innerHTML = "";
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
let canDelete = tableContainer.getAttribute("data-destroyer") === "1"
let canDelete = tableContainer.getAttribute("data-destroyer") === "1";
let menuId = tableContainer.getAttribute("data-menuId");
// Create a new Grid.js instance only if it doesn't exist
this.table = new Grid({
columns: [
@@ -45,15 +46,15 @@ class DataSettings {
width: "120px",
formatter: function (cell) {
let buttons = "";
if (canUpdate) {
buttons += `
<a href="/data-settings/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<a href="/data-settings/${cell}/edit?menu_id=${menuId}" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
`;
}
if (canDelete) {
buttons += `
<button class="btn btn-sm btn-red d-inline-flex align-items-center justify-content-center btn-delete-data-settings" data-id="${cell}">
@@ -61,12 +62,14 @@ class DataSettings {
</button>
`;
}
if (!canUpdate && !canDelete) {
buttons = `<span class="text-muted">No Privilege</span>`;
}
return gridjs.html(`<div class="d-flex justify-content-center gap-2">${buttons}</div>`);
return gridjs.html(
`<div class="d-flex justify-content-center gap-2">${buttons}</div>`
);
},
},
],
@@ -84,6 +87,7 @@ class DataSettings {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/data-settings`,

View File

@@ -6,6 +6,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
let toast = new bootstrap.Toast(
document.getElementById("toastNotification")
);
let menuId = document.getElementById("menuId").value;
submitButton.addEventListener("click", async function () {
let submitButton = this;
@@ -36,7 +37,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
toastMessage.innerText = result.data.message;
toast.show();
setTimeout(() => {
window.location.href = "/data-settings";
window.location.href = `/data-settings?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -8,6 +8,7 @@ import GeneralTable from "../../table-generator.js";
const tableElement = document.getElementById("reklame-data-table");
const canUpdate = tableElement.getAttribute("data-updater") === "1";
const canDelete = tableElement.getAttribute("data-destroyer") === "1";
let menuId = document.getElementById("menuId").value;
const dataAdvertisementsColumns = [
"No",
@@ -23,24 +24,25 @@ const dataAdvertisementsColumns = [
{
name: "Actions",
width: "120px",
formatter: function(cell, row) {
formatter: function (cell, row) {
const id = row.cells[10].data;
const model = "data/advertisements";
const model = `data/web-advertisements`;
let actionButtons = '<div class="d-flex justify-items-end gap-10">';
let hasPrivilege = false;
// Tampilkan tombol Edit jika user punya akses update
if (canUpdate) {
hasPrivilege = true;
actionButtons += `
<button class="btn btn-warning me-2 btn-edit"
data-id="${id}"
data-model="${model}">
data-model="${model}"
data-menu="${menuId}">
<i class='bx bx-edit'></i>
</button>`;
}
// Tampilkan tombol Delete jika user punya akses delete
if (canDelete) {
hasPrivilege = true;
@@ -50,13 +52,17 @@ const dataAdvertisementsColumns = [
<i class='bx bxs-trash'></i>
</button>`;
}
actionButtons += '</div>';
actionButtons += "</div>";
// Jika tidak memiliki akses, tampilkan teks "No Privilege"
return gridjs.html(hasPrivilege ? actionButtons : '<span class="text-muted">No Privilege</span>');
}
}
return gridjs.html(
hasPrivilege
? actionButtons
: '<span class="text-muted">No Privilege</span>'
);
},
},
];
document.addEventListener("DOMContentLoaded", () => {
@@ -86,4 +92,4 @@ document.addEventListener("DOMContentLoaded", () => {
};
table.init();
});
});

View File

@@ -5,6 +5,8 @@ document.addEventListener("DOMContentLoaded", function () {
const modalButton = document.querySelector(".btn-modal");
const form = document.querySelector("form#create-update-form");
var authLogo = document.querySelector(".auth-logo");
let menuId = document.getElementById("menuId").value;
console.log(menuId);
if (!saveButton || !form) return;
@@ -73,7 +75,7 @@ document.addEventListener("DOMContentLoaded", function () {
}, 2000);
setTimeout(() => {
window.location.href = "/data/web-advertisements";
window.location.href = `/data/web-advertisements?menu_id=${menuId}`;
}, 1000);
} else {
if (authLogo) {

View File

@@ -28,6 +28,7 @@ console.log(dropzonePreviewNode);
.getAttribute("content")}`,
},
init: function () {
let menuId = document.getElementById("menuId").value;
// Listen for the success event
this.on("success", function (file, response) {
console.log("File successfully uploaded:", file);
@@ -39,7 +40,7 @@ console.log(dropzonePreviewNode);
"Upload Files";
// Tunggu sebentar lalu reload halaman
setTimeout(() => {
window.location.href = "/data/web-advertisements";
window.location.href = `/data/web-advertisements?menu_id=${menuId}`;
}, 2000);
});
// Listen for the error event

View File

View File

View File

@@ -0,0 +1,211 @@
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";
class GoogleSheets {
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.initTableGoogleSheets();
this.initEvents();
}
initEvents() {
document.body.addEventListener("click", async (event) => {
const deleteButton = event.target.closest(
".btn-delete-google-sheet"
);
if (deleteButton) {
event.preventDefault();
await this.handleDelete(deleteButton);
}
});
}
initTableGoogleSheets() {
let tableContainer = document.getElementById(
"table-data-google-sheets"
);
if (!tableContainer) {
console.error("Table container not found!");
return;
}
// Clear previous table content
tableContainer.innerHTML = "";
// Get user permissions from data attributes
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
let canDelete = tableContainer.getAttribute("data-destroyer") === "1";
this.table = new Grid({
columns: [
"ID",
"No Registratsi",
"No KRK",
"Format STS",
"Fungsi BG",
"Selesai Terbit",
"Selesai Verifikasi",
"Tanggal Permohonan",
{
name: "Action",
formatter: (cell) => {
let buttons = "";
buttons += `
<a href="/data/google-sheets/${cell}" class="btn btn-primary btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-show'></i>
</a>
`;
if (canUpdate) {
buttons += `
<a href="#" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
`;
}
if (canDelete) {
buttons += `
<button data-id="${cell}" class="btn btn-sm btn-red btn-delete-google-sheet d-inline-flex align-items-center justify-content-center">
<i class='bx bxs-trash'></i>
</button>
`;
}
if (!canUpdate && !canDelete) {
buttons = `<span class="text-muted">No Privilege</span>`;
}
return gridjs.html(
`<div class="d-flex justify-content-center gap-2">${buttons}</div>`
);
},
},
],
pagination: {
limit: 50,
server: {
url: (prev, page) => {
let separator = prev.includes("?") ? "&" : "?";
return `${prev}${separator}page=${page + 1}`;
},
},
},
sort: true,
search: {
server: {
url: (prev, keyword) => {
let separator = prev.includes("?") ? "&" : "?";
return `${prev}${separator}search=${encodeURIComponent(
keyword
)}`;
},
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/pbg-task-google-sheet`,
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (data) => {
if (!data || !data.data) {
console.warn("⚠️ No data received from API");
return [];
}
return data.data.map((item) => {
console.log("🔹 Processing Item:", item);
return [
item.id,
item.no_registrasi,
item.no_krk,
item.format_sts,
item.fungsi_bg,
item.selesai_terbit,
item.selesai_verifikasi,
item.tgl_permohonan,
item.id,
];
});
},
total: (data) => {
let totalRecords = data?.meta?.total || 0;
return totalRecords;
},
catch: (error) => {
console.error("❌ Error fetching data:", error);
},
},
}).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/pbg-task-google-sheet/${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 GoogleSheets();
});

View File

@@ -8,6 +8,7 @@ import GeneralTable from "../../table-generator.js";
const tableElement = document.getElementById("spatial-planning-data-table");
const canUpdate = tableElement.getAttribute("data-updater") === "1";
const canDelete = tableElement.getAttribute("data-destroyer") === "1";
let menuId = document.getElementById("menuId").value;
const dataSpatialPlanningColumns = [
"No",
@@ -23,7 +24,7 @@ const dataSpatialPlanningColumns = [
widht: "120px",
formatter: function (cell, row) {
const id = row.cells[8].data;
const model = "data/spatial-plannings";
const model = "data/web-spatial-plannings";
let actionButtons = '<div class="d-flex justify-items-end gap-10">';
let hasPrivilege = false;
@@ -34,7 +35,8 @@ const dataSpatialPlanningColumns = [
actionButtons += `
<button class="btn btn-warning me-2 btn-edit"
data-id="${id}"
data-model="${model}">
data-model="${model}"
data-menu="${menuId}">
<i class='bx bx-edit'></i>
</button>`;
}
@@ -49,10 +51,14 @@ const dataSpatialPlanningColumns = [
</button>`;
}
actionButtons += '</div>';
actionButtons += "</div>";
// Jika tidak memiliki akses, tampilkan teks "No Privilege"
return gridjs.html(hasPrivilege ? actionButtons : '<span class="text-muted">No Privilege</span>');
return gridjs.html(
hasPrivilege
? actionButtons
: '<span class="text-muted">No Privilege</span>'
);
},
},
];

View File

@@ -5,6 +5,7 @@ document.addEventListener("DOMContentLoaded", function () {
const modalButton = document.querySelector(".btn-modal");
const form = document.querySelector("form#create-update-form");
var authLogo = document.querySelector(".auth-logo");
let menuId = document.getElementById("menuId").value;
if (!saveButton || !form) return;
@@ -73,7 +74,7 @@ document.addEventListener("DOMContentLoaded", function () {
}, 3000);
setTimeout(() => {
window.location.href = "/data/web-spatial-plannings";
window.location.href = `/data/web-spatial-plannings?menu_id=${menuId}`;
}, 3000);
} else {
if (authLogo) {

View File

@@ -28,6 +28,7 @@ console.log(dropzonePreviewNode);
.getAttribute("content")}`,
},
init: function () {
let menuId = document.getElementById("menuId").value;
// Listen for the success event
this.on("success", function (file, response) {
console.log("File successfully uploaded:", file);
@@ -39,7 +40,7 @@ console.log(dropzonePreviewNode);
"Upload Files";
// Tunggu sebentar lalu reload halaman
setTimeout(() => {
window.location.href = "/data/web-spatial-plannings";
window.location.href = `/data/web-spatial-plannings?menu_id=${menuId}`;
}, 2000);
});
// Listen for the error event

View File

@@ -11,6 +11,7 @@ const tableElement = document.getElementById("tourisms-data-table");
const canView = "1";
const canUpdate = tableElement.getAttribute("data-updater") === "1";
const canDelete = tableElement.getAttribute("data-destroyer") === "1";
let menuId = document.getElementById("menuId").value;
const dataTourismsColumns = [
"No",
@@ -32,7 +33,7 @@ const dataTourismsColumns = [
const district = row.cells[4].data;
const long = row.cells[9].data;
const lat = row.cells[10].data;
const model = "data/tourisms";
const model = "data/web-tourisms";
let actionButtons = '<div class="d-flex justify-items-end gap-10">';
let hasPrivilege = false;
@@ -44,16 +45,17 @@ const dataTourismsColumns = [
<button class="btn btn-info me-2 btn-view"
data-long="${long}" data-lat="${lat}" data-district="${district}">
<i class='bx bx-map'></i>
</button>`
</button>`;
}
// Tampilkan tombol Edit jika user punya akses update
if (canUpdate) {
hasPrivilege = true;
actionButtons += `
<button class="btn btn-warning me-2 btn-edit"
data-id="${id}"
data-model="${model}">
data-model="${model}"
data-menu="${menuId}">
<i class='bx bx-edit'></i>
</button>`;
}
@@ -65,13 +67,17 @@ const dataTourismsColumns = [
<button class="btn btn-red btn-delete"
data-id="${id}">
<i class='bx bxs-trash'></i>
</button>`
</button>`;
}
actionButtons += '</div>';
actionButtons += "</div>";
// Jika tidak memiliki akses, tampilkan teks "No Privilege"
return gridjs.html(hasPrivilege ? actionButtons : '<span class="text-muted">No Privilege</span>');
}
return gridjs.html(
hasPrivilege
? actionButtons
: '<span class="text-muted">No Privilege</span>'
);
},
},
];
@@ -82,6 +88,15 @@ document.addEventListener("DOMContentLoaded", () => {
`${GlobalConfig.apiHost}`,
dataTourismsColumns
);
const customIcon = new L.Icon({
iconUrl: "/leaflet/marker-icon.png",
iconRetinaUrl: "leaflet/marker-icon-2x.png",
shadowUrl: "/leaflet/marker-shadow.png",
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41],
});
table.processData = function (data) {
return data.data.map((item) => {
@@ -139,14 +154,20 @@ document.addEventListener("DOMContentLoaded", () => {
var modalInstance = new bootstrap.Modal(modal);
modalInstance.show();
const loading = document.getElementById("loading");
loading.style.display = "block";
setTimeout(() => {
if (!map) {
map = L.map("map").setView([lat, long], 14);
// Tambahkan tile layer (peta dasar)
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '&copy; OpenStreetMap contributors'
}).addTo(map);
L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
attribution: "&copy; OpenStreetMap contributors",
}
).addTo(map);
} else {
map.setView([lat, long], 14);
}
@@ -156,65 +177,75 @@ document.addEventListener("DOMContentLoaded", () => {
}
// Tambahkan marker untuk lokasi
L.marker([lat, long]).addTo(map)
.bindPopup(`<b>${district}</b><br>Lat: ${lat}, Long: ${long}`)
L.marker([lat, long], { icon: customIcon })
.addTo(map)
.bindPopup(
`<b>${district}</b><br>Lat: ${lat}, Long: ${long}`
)
.openPopup();
// Tambahkan GeoJSON ke dalam peta
fetch(`/storage/maps/tourisms/${district.toUpperCase()}.json`)
.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 Zona<\/td>\s*<td>(.*?)<\/td>/
);
.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 Zona<\/td>\s*<td>(.*?)<\/td>/
);
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());
})
.catch((error) => {
console.error("Error loading GeoJSON:", error);
});
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());
loading.style.display = "none";
})
.catch((error) => {
console.error("Error loading GeoJSON:", error);
loading.style.display = "none";
});
}, 500);
}
});

View File

@@ -5,6 +5,7 @@ document.addEventListener("DOMContentLoaded", function () {
const modalButton = document.querySelector(".btn-modal");
const form = document.querySelector("form#create-update-form");
var authLogo = document.querySelector(".auth-logo");
let menuId = document.getElementById("menuId").value;
if (!saveButton || !form) return;
@@ -73,7 +74,7 @@ document.addEventListener("DOMContentLoaded", function () {
}, 3000);
setTimeout(() => {
window.location.href = "/data/web-tourisms";
window.location.href = `/data/web-tourisms?menu_id=${menuId}`;
}, 3000);
} else {
if (authLogo) {

View File

@@ -28,6 +28,7 @@ console.log(dropzonePreviewNode);
.getAttribute("content")}`,
},
init: function () {
let menuId = document.getElementById("menuId").value;
// Listen for the success event
this.on("success", function (file, response) {
console.log("File successfully uploaded:", file);
@@ -39,7 +40,7 @@ console.log(dropzonePreviewNode);
"Upload Files";
// Tunggu sebentar lalu reload halaman
setTimeout(() => {
window.location.href = "/data/web-tourisms";
window.location.href = `/data/web-tourisms?menu_id=${menuId}`;
}, 2000);
});
// Listen for the error event

View File

@@ -7,6 +7,7 @@ import GeneralTable from "../../table-generator.js";
const tableElement = document.getElementById("umkm-data-table");
const canUpdate = tableElement.getAttribute("data-updater") === "1";
const canDelete = tableElement.getAttribute("data-destroyer") === "1";
let menuId = document.getElementById("menuId").value;
const dataUMKMColumns = [
"No",
@@ -31,24 +32,25 @@ const dataUMKMColumns = [
{
name: "Actions",
widht: "120px",
formatter: function(cell, row) {
formatter: function (cell, row) {
const id = row.cells[19].data;
const model = "data/umkm";
const model = "data/web-umkm";
let actionButtons = '<div class="d-flex justify-items-end gap-10">';
let hasPrivilege = false;
// Tampilkan tombol Edit jika user punya akses update
if (canUpdate) {
hasPrivilege = true;
actionButtons += `
<button class="btn btn-warning me-2 btn-edit"
data-id="${id}"
data-model="${model}">
data-model="${model}"
data-menu="${menuId}">
<i class='bx bx-edit'></i>
</button>`;
}
// Tampilkan tombol Delete jika user punya akses delete
if (canDelete) {
hasPrivilege = true;
@@ -58,13 +60,17 @@ const dataUMKMColumns = [
<i class='bx bxs-trash'></i>
</button>`;
}
actionButtons += '</div>';
actionButtons += "</div>";
// Jika tidak memiliki akses, tampilkan teks "No Privilege"
return gridjs.html(hasPrivilege ? actionButtons : '<span class="text-muted">No Privilege</span>');
}
}
return gridjs.html(
hasPrivilege
? actionButtons
: '<span class="text-muted">No Privilege</span>'
);
},
},
];
document.addEventListener("DOMContentLoaded", () => {
@@ -103,4 +109,4 @@ document.addEventListener("DOMContentLoaded", () => {
};
table.init();
});
});

View File

@@ -5,6 +5,7 @@ document.addEventListener("DOMContentLoaded", function () {
const modalButton = document.querySelector(".btn-modal");
const form = document.querySelector("form#create-update-form");
var authLogo = document.querySelector(".auth-logo");
let menuId = document.getElementById("menuId").value;
if (!saveButton || !form) return;
@@ -74,7 +75,7 @@ document.addEventListener("DOMContentLoaded", function () {
}, 2000);
setTimeout(() => {
window.location.href = "/data/web-umkm";
window.location.href = `/data/web-umkm?menu_id=${menuId}`;
}, 1000);
} else {
if (authLogo) {

View File

@@ -28,6 +28,7 @@ console.log(dropzonePreviewNode);
.getAttribute("content")}`,
},
init: function () {
let menuId = document.getElementById("menuId").value;
// Listen for the success event
this.on("success", function (file, response) {
console.log("File successfully uploaded:", file);
@@ -39,7 +40,7 @@ console.log(dropzonePreviewNode);
"Upload Files";
// Tunggu sebentar lalu reload halaman
setTimeout(() => {
window.location.href = "/data/web-umkm";
window.location.href = `/data/web-umkm?menu_id=${menuId}`;
}, 2000);
});
// Listen for the error event

View File

@@ -0,0 +1,56 @@
import { Grid } from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
// Fungsi untuk menghasilkan data dummy
function generateDummyInvitations(count = 10) {
const statuses = ["Terkirim", "Gagal", "Menunggu"];
const dummyData = [];
for (let i = 1; i <= count; i++) {
const email = `user${i}@example.com`;
const status = statuses[Math.floor(Math.random() * statuses.length)];
const createdAt = new Date(
Date.now() - Math.floor(Math.random() * 100000000)
)
.toISOString()
.replace("T", " ")
.substring(0, 19);
dummyData.push([i, email, status, createdAt]);
}
return dummyData;
}
class Invitations {
constructor() {
this.table = null;
this.initEvents();
}
initEvents() {
this.initTableInvitations();
}
initTableInvitations() {
let tableContainer = document.getElementById("table-invitations");
this.table = new Grid({
columns: [
{ name: "ID" },
{ name: "Email" },
{ name: "Status" },
{ name: "Created" },
],
data: generateDummyInvitations(50), // Bisa ganti jumlah data dummy
pagination: {
limit: 10,
},
sort: true,
search: true,
}).render(tableContainer);
}
}
document.addEventListener("DOMContentLoaded", function () {
new Invitations();
});

View File

@@ -1,6 +1,7 @@
document.addEventListener("DOMContentLoaded", function (e) {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
document
.getElementById("btnCreateUsers")
.addEventListener("click", async function () {
@@ -45,7 +46,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/master/users";
window.location.href = `/master/users?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -6,6 +6,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
let toast = new bootstrap.Toast(
document.getElementById("toastNotification")
);
let menuId = document.getElementById("menuId").value;
submitButton.addEventListener("click", async function () {
let submitButton = this;
@@ -36,7 +37,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
toastMessage.innerText = result.message;
toast.show();
setTimeout(() => {
window.location.href = "/master/users";
window.location.href = `/master/users?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -9,9 +9,8 @@ class UsersTable {
}
initTableUsers() {
let tableContainer = document.getElementById(
"table-users"
);
let tableContainer = document.getElementById("table-users");
let menuId = tableContainer.getAttribute("data-menuId");
tableContainer.innerHTML = "";
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
@@ -26,13 +25,15 @@ class UsersTable {
"Roles",
{
name: "Action",
formatter: (cell) =>{
formatter: (cell) => {
if (!canUpdate) {
return gridjs.html(`<span class="text-muted">No Privilege</span>`);
return gridjs.html(
`<span class="text-muted">No Privilege</span>`
);
}
return gridjs.html(`
<div class="d-flex justify-content-center">
<a href="/master/users/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<a href="/master/users/${cell}/edit?menu_id=${menuId}" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
</div>
@@ -54,6 +55,7 @@ class UsersTable {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/users`,

View File

@@ -6,6 +6,7 @@ class CreateMenu {
initCreateMenu() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
document
.getElementById("btnCreateMenus")
.addEventListener("click", async function () {
@@ -41,7 +42,7 @@ class CreateMenu {
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/menus";
window.location.href = `/menus?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -31,35 +31,7 @@ class Menus {
tableContainer.innerHTML = "";
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
let canDelete = tableContainer.getAttribute("data-destroyer") === "1";
if (this.table) {
// If table exists, update its data instead of recreating
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/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();
return;
}
let menuId = tableContainer.getAttribute("data-menuId");
this.table = new Grid({
columns: [
@@ -73,15 +45,15 @@ class Menus {
name: "Action",
formatter: (cell) => {
let buttons = `<div class="d-flex justify-content-center align-items-center gap-2">`;
if (canUpdate) {
buttons += `
<a href="/menus/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<a href="/menus/${cell}/edit?menu_id=${menuId}" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
`;
}
if (canDelete) {
buttons += `
<button data-id="${cell}" class="btn btn-red btn-sm btn-delete-menu d-inline-flex align-items-center justify-content-center">
@@ -89,13 +61,13 @@ class Menus {
</button>
`;
}
if (!canUpdate && !canDelete) {
buttons += `<span class="text-muted">No Privilege</span>`;
}
buttons += `</div>`;
return gridjs.html(buttons);
},
},
@@ -114,6 +86,7 @@ class Menus {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
server: {
url: `${GlobalConfig.apiHost}/api/menus`,

View File

@@ -6,6 +6,7 @@ class UpdateMenu {
initUpdateMenu() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
let menuId = document.getElementById("menuId").value;
document
.getElementById("btnUpdateMenus")
.addEventListener("click", async function () {
@@ -41,7 +42,7 @@ class UpdateMenu {
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/menus";
window.location.href = `/menus?menu_id=${menuId}`;
}, 2000);
} else {
let error = await response.json();

View File

@@ -0,0 +1,104 @@
import { Grid } from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js";
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
import moment from "moment";
import InitDatePicker from "../utils/InitDatePicker.js";
class PaymentRecaps {
constructor() {
this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
this.initTablePaymentRecaps();
this.initFilterDatepicker();
}
initFilterDatepicker() {
new InitDatePicker(
"#datepicker-payment-recap",
this.handleChangeFilterDate.bind(this)
).init();
}
handleChangeFilterDate(strDate) {
console.log("filter date : ", strDate);
}
formatCategory(category) {
const categoryMap = {
potention_sum: "Potensi",
non_verified_sum: "Belum Terverifikasi",
verified_sum: "Terverifikasi",
business_sum: "Usaha",
non_business_sum: "Non Usaha",
spatial_sum: "Tata Ruang",
waiting_click_dpmptsp_sum: "Menunggu Klik DPMPTSP",
issuance_realization_pbg_sum: "Realisasi Terbit PBG",
process_in_technical_office_sum: "Proses Di Dinas Teknis",
};
return categoryMap[category] || category; // Return mapped value or original category
}
initTablePaymentRecaps() {
let tableContainer = document.getElementById("table-payment-recaps");
this.table = new Grid({
columns: [
{ name: "Kategori", data: (row) => row[0] },
{ name: "Nominal", data: (row) => row[1] },
{
name: "Created",
data: (row) => row[2],
attributes: { style: "width: 200px; white-space: nowrap;" },
},
],
pagination: {
limit: 10,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
server: {
url: `${GlobalConfig.apiHost}/api/payment-recaps`,
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (response) => {
console.log("API Response:", response); // Debugging
if (!response.data || !Array.isArray(response.data)) {
console.error(
"Error: Data is not an array",
response.data
);
return [];
}
return response.data.map((item) => [
this.formatCategory(item.category ?? "Unknown"), // Ensure category is not null
addThousandSeparators(
Number(item.nominal).toString() || 0
), // Ensure nominal is a valid number
moment(item.created_at).isValid()
? moment(item.created_at).format(
"YYYY-MM-DD H:mm:ss"
)
: "-", // Handle invalid dates
]);
},
total: (response) => response.pagination?.total || 0,
},
width: "auto",
fixedHeader: true,
}).render(tableContainer);
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new PaymentRecaps();
});

View File

@@ -2,10 +2,15 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config";
import { Dropzone } from "dropzone";
Dropzone.autoDiscover = false;
class PbgTasks {
init() {
this.initTableRequestAssignment();
this.handleSendNotification();
this.handleUpload();
this.handleUploadBeritaAcara();
}
initTableRequestAssignment() {
@@ -28,30 +33,39 @@ class PbgTasks {
{ name: "Due Date", width: "10%" },
{
name: "Action",
formatter: function (cell) {
let tableContainer = document.getElementById("table-pbg-tasks");
let canUpdate = tableContainer.getAttribute("data-updater") === "1";
formatter: (cell) => {
let tableContainer =
document.getElementById("table-pbg-tasks");
let canUpdate =
tableContainer.getAttribute("data-updater") === "1";
if (!canUpdate) {
return gridjs.html(`
<span class="text-muted">No Privilege</span>
`);
return gridjs.html(
`<span class="text-muted">No Privilege</span>`
);
}
return gridjs.html(`
<div class="d-flex justify-content-center align-items-center gap-2">
<a href="/pbg-task/${cell}" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
Detail
</a>
<button class="btn btn-sm btn-info upload-btn" data-id="${cell}">
Upload Bukti Bayar
</button>
<button class="btn btn-sm btn-info upload-btn-berita-acara" data-id="${cell}">
Buat Berita Acara
</button>
</div>
`);
},
}
},
],
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
pagination: {
limit: 15,
@@ -90,6 +104,96 @@ class PbgTasks {
},
}).render(document.getElementById("table-pbg-tasks"));
}
handleSendNotification() {
this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
document
.getElementById("sendNotificationBtn")
.addEventListener("click", () => {
let notificationStatus =
document.getElementById("notificationStatus").value;
// Show success toast
this.toastMessage.innerText = "Notifikasi berhasil dikirim!";
this.toast.show();
// Close modal after sending
let modal = bootstrap.Modal.getInstance(
document.getElementById("sendNotificationModal")
);
modal.hide();
});
}
handleUpload() {
// Handle button click to show modal
document.addEventListener("click", function (event) {
if (event.target.classList.contains("upload-btn")) {
// Show modal
let uploadModal = new bootstrap.Modal(
document.getElementById("uploadModal")
);
uploadModal.show();
}
});
let dropzone = new Dropzone("#singleFileDropzone", {
url: "/upload-bukti-bayar", // Adjust to your backend endpoint
maxFiles: 1, // Allow only 1 file
maxFilesize: 5, // Limit size to 5MB
acceptedFiles: ".jpg,.png,.pdf", // Allowed file types
autoProcessQueue: false, // Prevent automatic upload
addRemoveLinks: true, // Show remove button
dictDefaultMessage: "Drop your file here or click to upload.",
init: function () {
let dz = this;
// Remove previous file when a new file is added
dz.on("addedfile", function () {
if (dz.files.length > 1) {
dz.removeFile(dz.files[0]);
}
});
// Handle upload button click
document
.getElementById("uploadBtn")
.addEventListener("click", function () {
if (dz.getQueuedFiles().length > 0) {
dz.processQueue(); // Manually process upload
} else {
alert("Please select a file to upload.");
}
});
// Success callback
dz.on("success", function (file, response) {
alert("File uploaded successfully!");
dz.removeAllFiles(); // Clear after upload
});
// Error callback
dz.on("error", function (file, errorMessage) {
alert("Upload failed: " + errorMessage);
});
},
});
}
handleUploadBeritaAcara() {
// Handle button click to show modal
document.addEventListener("click", function (event) {
if (event.target.classList.contains("upload-btn-berita-acara")) {
// Show modal
let uploadModal = new bootstrap.Modal(
document.getElementById("uploadBeritaAcara")
);
uploadModal.show();
}
});
}
}
document.addEventListener("DOMContentLoaded", function (e) {

View File

@@ -0,0 +1,69 @@
import { Grid } from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config";
class PbgTaskAssignments {
init() {
this.initTablePbgTaskAssignments();
}
initTablePbgTaskAssignments() {
let tableContainer = document.getElementById(
"table-pbg-task-assignments"
);
let uuid = document.getElementById("uuid").value;
new Grid({
columns: [
"ID",
"Nama",
"Email",
"Nomor Telepon",
"Keahlian",
"Status",
],
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
debounceTimeout: 1000,
},
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
server: {
url: `${GlobalConfig.apiHost}/api/task-assignments/${uuid}`,
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.email,
item.phone_number,
item.expertise,
item.status_name,
]),
total: (data) => data.meta.total,
},
}).render(tableContainer);
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new PbgTaskAssignments().init();
});

Some files were not shown because too many files have changed in this diff Show More