Compare commits
11 Commits
feature/ch
...
fix/add-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c529a5d511 | ||
|
|
ff244039ff | ||
|
|
55902042f4 | ||
|
|
c67aa979c2 | ||
|
|
fbaa33ae13 | ||
|
|
9516b6f575 | ||
|
|
ffc08f26cc | ||
|
|
e0c35b8897 | ||
|
|
22ee7502ad | ||
|
|
2f3bc172eb | ||
|
|
1f33d0de4e |
@@ -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"
|
||||
77
README.md
77
README.md
@@ -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,71 @@ 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
|
||||
```
|
||||
|
||||
- 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
|
||||
|
||||
@@ -34,7 +34,7 @@ class ExecuteScraping extends Command
|
||||
}
|
||||
public function handle()
|
||||
{
|
||||
SyncronizeSIMBG::dispatch();
|
||||
SyncronizeSIMBG::dispatch()->onQueue('default');
|
||||
Log::info("running scheduler daily scraping");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
64
app/Http/Controllers/Api/TaskAssignmentsController.php
Normal file
64
app/Http/Controllers/Api/TaskAssignmentsController.php
Normal 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)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,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)));
|
||||
}
|
||||
|
||||
16
app/Http/Controllers/Dashboards/PotentialsController.php
Normal file
16
app/Http/Controllers/Dashboards/PotentialsController.php
Normal 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');
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class SyncronizeController extends Controller
|
||||
|
||||
public function syncIndexIntegration(Request $request, $uuid){
|
||||
$token = $request->get('token');
|
||||
$res = $this->service_simbg->syncIndexIntegration($uuid, $token);
|
||||
$res = $this->service_simbg->syncIndexIntegration($uuid);
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -42,4 +42,9 @@ class SyncronizeController extends Controller
|
||||
$res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function syncTaskAssignments($uuid){
|
||||
$res = $this->service_simbg->syncTaskAssignments($uuid);
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
||||
19
app/Http/Resources/TaskAssignmentsResource.php
Normal file
19
app/Http/Resources/TaskAssignmentsResource.php
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ class SyncronizeSIMBG implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -21,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,9 @@ class PbgTask extends Model
|
||||
public function googleSheet(){
|
||||
return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number');
|
||||
}
|
||||
|
||||
public function taskAssignments()
|
||||
{
|
||||
return $this->hasMany(TaskAssignment::class, 'pbg_task_uid', 'uuid');
|
||||
}
|
||||
}
|
||||
|
||||
28
app/Models/TaskAssignment.php
Normal file
28
app/Models/TaskAssignment.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class TaskAssignment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
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', 'tas_id'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'is_verif' => 'boolean',
|
||||
'file' => 'array', // JSON field casting
|
||||
];
|
||||
|
||||
public function pbgTask()
|
||||
{
|
||||
return $this->belongsTo(PbgTask::class, 'pbg_task_uid', 'uuid');
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(GoogleSheetService::class, function () {
|
||||
return new GoogleSheetService();
|
||||
});
|
||||
$this->app->singleton(ServiceSIMBG::class, function ($app) {
|
||||
return new ServiceSIMBG($app->make(GoogleSheetService::class));
|
||||
});
|
||||
|
||||
@@ -51,10 +51,26 @@ class ServiceClient
|
||||
|
||||
$resultResponse = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
|
||||
return $this->resSuccess($resultResponse);
|
||||
} catch (Exception $e) {
|
||||
\Log::error('error from client service'. $e->getMessage());
|
||||
return $this->resError($e->getMessage());
|
||||
}
|
||||
} catch (\GuzzleHttp\Exception\ClientException $e) {
|
||||
// Handle 4xx errors (e.g., 401 Unauthorized)
|
||||
$responseBody = (string) $e->getResponse()->getBody();
|
||||
$errorResponse = json_decode($responseBody, true);
|
||||
|
||||
if (isset($errorResponse['code']) && $errorResponse['code'] === 'token_not_valid') {
|
||||
return $this->resError('Invalid token, please refresh your token.', $errorResponse, 401);
|
||||
}
|
||||
|
||||
return $this->resError('Client error from API', $errorResponse, $e->getResponse()->getStatusCode());
|
||||
} catch (\GuzzleHttp\Exception\ServerException $e) {
|
||||
// Handle 5xx errors (e.g., Internal Server Error)
|
||||
return $this->resError('Server error from API', (string) $e->getResponse()->getBody(), 500);
|
||||
} catch (\GuzzleHttp\Exception\RequestException $e) {
|
||||
// Handle network errors (e.g., timeout, connection issues)
|
||||
return $this->resError('Network error: ' . $e->getMessage(), null, 503);
|
||||
} catch (Exception $e) {
|
||||
// Handle unexpected errors
|
||||
return $this->resError('Unexpected error: ' . $e->getMessage(), null, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk melakukan permintaan GET
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\ImportDatasource;
|
||||
use App\Models\PbgTaskIndexIntegrations;
|
||||
use App\Models\PbgTaskPrasarana;
|
||||
use App\Models\PbgTaskRetributions;
|
||||
use App\Models\TaskAssignment;
|
||||
use Exception;
|
||||
use App\Models\PbgTask;
|
||||
use App\Traits\GlobalApiResponse;
|
||||
@@ -66,12 +67,19 @@ class ServiceSIMBG
|
||||
}
|
||||
}
|
||||
|
||||
public function syncIndexIntegration($uuids, $token)
|
||||
public function syncIndexIntegration($uuids)
|
||||
{
|
||||
try{
|
||||
if(empty($uuids)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$initResToken = $this->getToken();
|
||||
if (empty($initResToken->original['data']['token']['access'])) {
|
||||
Log::error("API response indicates failure", ['token' => 'Failed to retrieve token']);
|
||||
return false;
|
||||
}
|
||||
$token = $initResToken->original['data']['token']['access'];
|
||||
|
||||
$integrations = [];
|
||||
foreach($uuids as $uuid){
|
||||
@@ -120,6 +128,7 @@ class ServiceSIMBG
|
||||
public function syncTaskPBG()
|
||||
{
|
||||
try {
|
||||
Log::info("Processing google sheet sync");
|
||||
$importDatasource = ImportDatasource::create([
|
||||
'status' => ImportDatasourceStatus::Processing->value,
|
||||
]);
|
||||
@@ -145,14 +154,14 @@ class ServiceSIMBG
|
||||
// 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"] = $row[2] ?? null;
|
||||
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $row[3] ?? null;
|
||||
$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"] = $row[2] ?? null;
|
||||
$data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $row[4] ?? null;
|
||||
$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"] = $row[2] ?? null;
|
||||
$data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $row[3] ?? null;
|
||||
$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"
|
||||
@@ -160,6 +169,8 @@ class ServiceSIMBG
|
||||
}
|
||||
}
|
||||
|
||||
Log::info("data setting result", ['result' => $data_setting_result]);
|
||||
|
||||
foreach ($data_setting_result as $key => $value) {
|
||||
DataSetting::updateOrInsert(
|
||||
["key" => $key], // Find by key
|
||||
@@ -167,7 +178,6 @@ class ServiceSIMBG
|
||||
);
|
||||
}
|
||||
$mapToUpsert = [];
|
||||
$count = 0;
|
||||
|
||||
foreach($sheetData as $data){
|
||||
$mapToUpsert[] =
|
||||
@@ -289,7 +299,7 @@ class ServiceSIMBG
|
||||
if (empty($initResToken->original['data']['token']['access'])) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Failed to retrieve token'
|
||||
'response_body' => 'Failed to retrieve token'
|
||||
]);
|
||||
return $this->resError("Failed to retrieve token");
|
||||
}
|
||||
@@ -303,20 +313,49 @@ class ServiceSIMBG
|
||||
if ($totalPage == 0) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Invalid response: no total_page'
|
||||
'response_body' => 'Invalid response: no total_page'
|
||||
]);
|
||||
return $this->resError("Invalid response from API");
|
||||
}
|
||||
|
||||
$savedCount = $failedCount = 0;
|
||||
|
||||
Log::info("Fetching tasks", ['total page' => $totalPage]);
|
||||
|
||||
for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) {
|
||||
try {
|
||||
$pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
|
||||
|
||||
Log::info("Fetching tasks", ['currentPage' => $currentPage]);
|
||||
$headers = [
|
||||
'Authorization' => "Bearer " . $apiToken, // Update headers
|
||||
];
|
||||
|
||||
for ($attempt = 0; $attempt < 2; $attempt++) { // Try twice (original + retry)
|
||||
|
||||
$response = $this->service_client->get($pageUrl, $headers);
|
||||
|
||||
if ($response instanceof \Illuminate\Http\JsonResponse) {
|
||||
$decodedResponse = json_decode($response->getContent(), true);
|
||||
|
||||
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
|
||||
$initResToken = $this->getToken();
|
||||
|
||||
if (!empty($initResToken->original['data']['token']['access'])) {
|
||||
$new_token = $initResToken->original['data']['token']['access'];
|
||||
$headers['Authorization'] = "Bearer " . $new_token;
|
||||
continue;
|
||||
} else {
|
||||
Log::error("Failed to refresh token");
|
||||
return $this->resError("Failed to refresh token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success case, break loop
|
||||
break;
|
||||
}
|
||||
|
||||
$response = $this->service_client->get($pageUrl, $headers);
|
||||
$tasks = $response->original['data']['data'] ?? [];
|
||||
|
||||
if (empty($tasks)) {
|
||||
@@ -351,6 +390,7 @@ class ServiceSIMBG
|
||||
];
|
||||
|
||||
$this->syncTaskDetailSubmit($item['uid'], $apiToken);
|
||||
$this->syncTaskAssignments($item['uid']);
|
||||
$savedCount++;
|
||||
} catch (Exception $e) {
|
||||
$failedCount++;
|
||||
@@ -371,7 +411,7 @@ class ServiceSIMBG
|
||||
]);
|
||||
|
||||
$uuids = array_column($tasksCollective, 'uuid');
|
||||
$this->syncIndexIntegration($uuids, $apiToken);
|
||||
$this->syncIndexIntegration($uuids);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::error("Failed to process page", [
|
||||
@@ -400,34 +440,51 @@ class ServiceSIMBG
|
||||
if (isset($importDatasource)) {
|
||||
$importDatasource->update([
|
||||
'status' => ImportDatasourceStatus::Failed->value,
|
||||
'message' => 'Critical failure: ' . $e->getMessage()
|
||||
'response_body' => 'Critical failure: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
return $this->resError("Critical failure occurred: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function syncTaskDetailSubmit($uuid, $token)
|
||||
{
|
||||
try{
|
||||
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/";
|
||||
|
||||
$headers = [
|
||||
'Authorization' => "Bearer " . $token,
|
||||
];
|
||||
|
||||
$res = $this->service_client->get($url, $headers);
|
||||
|
||||
if (empty($res->original['success']) || !$res->original['success']) {
|
||||
// Log error
|
||||
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
|
||||
return false;
|
||||
|
||||
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);
|
||||
|
||||
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
|
||||
$initResToken = $this->getToken();
|
||||
|
||||
if (!empty($initResToken->original['data']['token']['access'])) {
|
||||
$new_token = $initResToken->original['data']['token']['access'];
|
||||
|
||||
$headers['Authorization'] = "Bearer " . $new_token;
|
||||
continue;
|
||||
} else {
|
||||
Log::error("Failed to refresh token");
|
||||
return $this->resError("Failed to refresh token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$data = $res->original['data']['data'] ?? [];
|
||||
|
||||
// Ensure response is valid before accessing properties
|
||||
$responseData = $res->original ?? [];
|
||||
$data = $responseData['data']['data'] ?? [];
|
||||
if (empty($data)) {
|
||||
Log::error("No data returned from API", ['url' => $url, 'uuid' => $uuid]);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -489,6 +546,59 @@ class ServiceSIMBG
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function syncTaskAssignments($uuid){
|
||||
try{
|
||||
$init_token = $this->getToken();
|
||||
$token = $init_token->original['data']['token']['access'];
|
||||
$url = "/api/pbg/v1/list-tim-penilai/". $uuid . "/?page=1&size=10";
|
||||
$headers = [
|
||||
'Authorization' => "Bearer " . $token,
|
||||
];
|
||||
|
||||
$response = $this->service_client->get($url, $headers);
|
||||
$datas = $response->original['data']['data'] ?? [];
|
||||
if(empty($datas)){
|
||||
return false;
|
||||
}
|
||||
$task_assignments = [];
|
||||
|
||||
foreach ($datas as $data) {
|
||||
$task_assignments[] = [
|
||||
'pbg_task_uid' => $uuid,
|
||||
'user_id' => $data['user_id'],
|
||||
'name' => $data['name'],
|
||||
'username' => $data['username'],
|
||||
'email' => $data['email'],
|
||||
'phone_number' => $data['phone_number'],
|
||||
'role' => $data['role'],
|
||||
'role_name' => $data['role_name'],
|
||||
'is_active' => $data['is_active'],
|
||||
'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'],
|
||||
'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,
|
||||
['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){
|
||||
Log::error("Failed to sync task assignments", ['error' => $e->getMessage()]);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
protected function convertToDecimal(?string $value): ?float
|
||||
{
|
||||
if (empty($value)) {
|
||||
@@ -522,8 +632,10 @@ class ServiceSIMBG
|
||||
return null;
|
||||
}
|
||||
|
||||
$cleaned = str_replace('.','', $value);
|
||||
|
||||
// Otherwise, cast to integer
|
||||
return (int) $value;
|
||||
return (int) $cleaned;
|
||||
}
|
||||
|
||||
protected function convertToDate($dateString)
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
],
|
||||
"dev": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
|
||||
@@ -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;"
|
||||
]) : [],
|
||||
],
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ return [
|
||||
// set timeout queue
|
||||
|
||||
'worker' => [
|
||||
'timeout' => 300
|
||||
'timeout' => 40000
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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::create('task_assignments', function (Blueprint $table) {
|
||||
$table->id(); // Auto-increment primary key
|
||||
|
||||
// Foreign key reference to pbg_tasks (uid column)
|
||||
$table->string('pbg_task_uid');
|
||||
$table->foreign('pbg_task_uid')->references('uuid')->on('pbg_task')->onDelete('cascade');
|
||||
|
||||
$table->unsignedBigInteger('user_id'); // Reference to users table
|
||||
$table->string('name');
|
||||
$table->string('username')->unique();
|
||||
$table->string('email')->unique();
|
||||
$table->string('phone_number')->nullable();
|
||||
$table->unsignedInteger('role'); // Assuming role is numeric
|
||||
$table->string('role_name');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->json('file')->nullable(); // Store as JSON if 'file' is an array
|
||||
$table->string('expertise')->nullable();
|
||||
$table->string('experience')->nullable();
|
||||
$table->boolean('is_verif')->default(false);
|
||||
$table->string('uid')->unique();
|
||||
$table->unsignedTinyInteger('status')->default(0); // Assuming status is a small integer
|
||||
$table->string('status_name')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('task_assignments');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -119,7 +119,7 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
],
|
||||
[
|
||||
"name" => "Dashboard Potensi",
|
||||
"url" => "dashboard.lack_of_potential",
|
||||
"url" => null,
|
||||
"icon" => null,
|
||||
"parent_id" => $dashboard->id,
|
||||
"sort_order" => 3,
|
||||
@@ -236,6 +236,20 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
"parent_id" => $chat_bedas->id,
|
||||
"sort_order" => 1,
|
||||
],
|
||||
[
|
||||
"name" => "Dalam Sistem",
|
||||
"url" => "dashboard.potentials.inside_system",
|
||||
"icon" => null,
|
||||
"parent_id" => Menu::where('name', 'Dashboard Potensi')->first()->id,
|
||||
"sort_order" => 1,
|
||||
],
|
||||
[
|
||||
"name" => "Luar Sistem",
|
||||
"url" => "dashboard.potentials.outside_system",
|
||||
"icon" => null,
|
||||
"parent_id" => Menu::where('name', 'Dashboard Potensi')->first()->id,
|
||||
"sort_order" => 2,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($children_menus as $child_menu) {
|
||||
@@ -261,6 +275,8 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
$peta = Menu::where('name', 'PETA')->first();
|
||||
$bigdata_resume = Menu::where('name', 'Lap Pimpinan')->first();
|
||||
$chatbot = Menu::where('name', 'Chat')->first();
|
||||
$dalam_sistem = Menu::where('name', 'Dalam Sistem')->first();
|
||||
$luar_sistem = Menu::where('name', 'Luar Sistem')->first();
|
||||
|
||||
// Superadmin gets all menus
|
||||
$superadmin->menus()->sync([
|
||||
@@ -289,7 +305,9 @@ class UsersRoleMenuSeeder extends Seeder
|
||||
$lack_of_potentials->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$spatial_plannings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$pdam->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
// $peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$dalam_sistem->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||
$luar_sistem->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],
|
||||
]);
|
||||
|
||||
@@ -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: {
|
||||
@@ -70,6 +64,7 @@ class BigdataResume {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/bigdata-report`,
|
||||
@@ -109,60 +104,73 @@ class BigdataResume {
|
||||
},
|
||||
total: (data) => data.total,
|
||||
},
|
||||
}).render(tableContainer);
|
||||
}
|
||||
async handleDelete(deleteButton) {
|
||||
const id = deleteButton.getAttribute("data-id");
|
||||
|
||||
const result = await Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "You won't be able to revert this!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#3085d6",
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
width: "auto",
|
||||
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) {
|
||||
|
||||
@@ -77,6 +77,7 @@ class BusinessIndustries {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/api-business-industries`,
|
||||
|
||||
@@ -66,6 +66,7 @@ class Customers {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/customers`,
|
||||
@@ -91,6 +92,8 @@ class Customers {
|
||||
}).render(tableContainer);
|
||||
}
|
||||
|
||||
handleSearch() {}
|
||||
|
||||
async handleDelete(deleteButton) {
|
||||
const id = deleteButton.getAttribute("data-id");
|
||||
|
||||
|
||||
194
resources/js/dashboards/potentials/inside_system.js
Normal file
194
resources/js/dashboards/potentials/inside_system.js
Normal 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);
|
||||
121
resources/js/dashboards/potentials/outside_system.js
Normal file
121
resources/js/dashboards/potentials/outside_system.js
Normal 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);
|
||||
@@ -67,6 +67,7 @@ class DataSettings {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/data-settings`,
|
||||
|
||||
@@ -44,6 +44,7 @@ class UsersTable {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/users`,
|
||||
|
||||
@@ -28,35 +28,35 @@ class Menus {
|
||||
initTableMenus() {
|
||||
let tableContainer = document.getElementById("table-menus");
|
||||
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
// }
|
||||
|
||||
this.table = new Grid({
|
||||
columns: [
|
||||
@@ -96,6 +96,7 @@ class Menus {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/menus`,
|
||||
|
||||
@@ -36,6 +36,7 @@ class PbgTasks {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
pagination: {
|
||||
limit: 15,
|
||||
|
||||
69
resources/js/pbg-task/show.js
Normal file
69
resources/js/pbg-task/show.js
Normal 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();
|
||||
});
|
||||
@@ -65,6 +65,7 @@ class Roles {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
server: {
|
||||
url: `${GlobalConfig.apiHost}/api/roles`,
|
||||
|
||||
@@ -18,12 +18,15 @@ class GeneralTable {
|
||||
server: {
|
||||
url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||
},
|
||||
debounceTimeout: 1000,
|
||||
},
|
||||
pagination: this.options.pagination || {
|
||||
limit: 15,
|
||||
server: {
|
||||
url: (prev, page) =>
|
||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${page + 1}`,
|
||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||
page + 1
|
||||
}`,
|
||||
},
|
||||
},
|
||||
sort: this.options.sort || true,
|
||||
@@ -48,22 +51,29 @@ class GeneralTable {
|
||||
processData(data) {
|
||||
return data.data.map((item) => {
|
||||
return this.columns.map((column) => {
|
||||
return item[column] || '';
|
||||
return item[column] || "";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleActions() {
|
||||
document.addEventListener("click", (event) => {
|
||||
if (event.target && event.target.classList.contains('btn-edit')) {
|
||||
if (event.target && event.target.classList.contains("btn-edit")) {
|
||||
this.handleEdit(event);
|
||||
}
|
||||
else if (event.target && event.target.classList.contains('btn-delete')) {
|
||||
} else if (
|
||||
event.target &&
|
||||
event.target.classList.contains("btn-delete")
|
||||
) {
|
||||
this.handleDelete(event);
|
||||
}
|
||||
else if (event.target && event.target.classList.contains('btn-create')) {
|
||||
} else if (
|
||||
event.target &&
|
||||
event.target.classList.contains("btn-create")
|
||||
) {
|
||||
this.handleCreate(event);
|
||||
} else if (event.target && event.target.classList.contains('btn-bulk-create')) {
|
||||
} else if (
|
||||
event.target &&
|
||||
event.target.classList.contains("btn-bulk-create")
|
||||
) {
|
||||
this.handleBulkCreate(event);
|
||||
}
|
||||
});
|
||||
@@ -72,28 +82,28 @@ class GeneralTable {
|
||||
// Fungsi untuk menangani create
|
||||
handleCreate(event) {
|
||||
// Menggunakan model dan ID untuk membangun URL dinamis
|
||||
const model = event.target.getAttribute('data-model'); // Mengambil model dari data-model
|
||||
const model = event.target.getAttribute("data-model"); // Mengambil model dari data-model
|
||||
window.location.href = `${this.baseUrl}/${model}/create`;
|
||||
}
|
||||
|
||||
handleBulkCreate(event) {
|
||||
// Menggunakan model dan ID untuk membangun URL dinamis
|
||||
const model = event.target.getAttribute('data-model');
|
||||
const model = event.target.getAttribute("data-model");
|
||||
window.location.href = `${this.baseUrl}/${model}/bulk-create`;
|
||||
}
|
||||
|
||||
// Fungsi untuk menangani edit
|
||||
handleEdit(event) {
|
||||
const id = event.target.getAttribute('data-id');
|
||||
const model = event.target.getAttribute('data-model'); // Mengambil model dari data-model
|
||||
console.log('Editing record with ID:', id);
|
||||
const id = event.target.getAttribute("data-id");
|
||||
const model = event.target.getAttribute("data-model"); // Mengambil model dari data-model
|
||||
console.log("Editing record with ID:", id);
|
||||
// Menggunakan model dan ID untuk membangun URL dinamis
|
||||
window.location.href = `${this.baseUrl}/${model}/${id}/edit`;
|
||||
}
|
||||
|
||||
// Fungsi untuk menangani delete
|
||||
handleDelete(event) {
|
||||
const id = event.target.getAttribute('data-id');
|
||||
const id = event.target.getAttribute("data-id");
|
||||
console.log(id);
|
||||
// if (confirm("Are you sure you want to delete this item?")) {
|
||||
// this.deleteRecord(id);
|
||||
@@ -105,7 +115,7 @@ class GeneralTable {
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#d33",
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonText: "Yes, delete it!"
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
this.deleteRecord(id);
|
||||
@@ -114,8 +124,8 @@ class GeneralTable {
|
||||
text: "Your record has been deleted.",
|
||||
icon: "success",
|
||||
showConfirmButton: false, // Menghilangkan tombol OK
|
||||
timer: 2000 // Menutup otomatis dalam 2 detik (opsional)
|
||||
});
|
||||
timer: 2000, // Menutup otomatis dalam 2 detik (opsional)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -123,8 +133,9 @@ class GeneralTable {
|
||||
async deleteRecord(id) {
|
||||
try {
|
||||
console.log(id);
|
||||
const response = await fetch(`${this.apiUrl}/${id}`, { // Menambahkan model dalam URL
|
||||
method: 'DELETE',
|
||||
const response = await fetch(`${this.apiUrl}/${id}`, {
|
||||
// Menambahkan model dalam URL
|
||||
method: "DELETE",
|
||||
headers: this.options.headers || {
|
||||
Authorization: `Bearer ${document
|
||||
.querySelector('meta[name="api-token"]')
|
||||
@@ -136,10 +147,14 @@ class GeneralTable {
|
||||
location.reload();
|
||||
} else {
|
||||
const data = await response.json();
|
||||
showErrorAlert(`Failed to delete record: ${data.message || "Unknown error"}`);
|
||||
showErrorAlert(
|
||||
`Failed to delete record: ${
|
||||
data.message || "Unknown error"
|
||||
}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting data:', error);
|
||||
console.error("Error deleting data:", error);
|
||||
showErrorAlert("Error deleting data. Please try again.");
|
||||
}
|
||||
}
|
||||
@@ -148,7 +163,7 @@ class GeneralTable {
|
||||
// Fungsi untuk menampilkan alert
|
||||
function showErrorAlert(message) {
|
||||
const alertContainer = document.getElementById("alert-container");
|
||||
|
||||
|
||||
alertContainer.innerHTML = `
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
${message}
|
||||
@@ -157,4 +172,4 @@ function showErrorAlert(message) {
|
||||
`;
|
||||
}
|
||||
|
||||
export default GeneralTable;
|
||||
export default GeneralTable;
|
||||
|
||||
234
resources/scss/dashboards/potentials/_inside_system.scss
Normal file
234
resources/scss/dashboards/potentials/_inside_system.scss
Normal file
@@ -0,0 +1,234 @@
|
||||
//
|
||||
// inside_system.scss
|
||||
//
|
||||
|
||||
.square {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.dia-top-left-bottom-right:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(
|
||||
to top right,
|
||||
transparent calc(50% - 2px),
|
||||
black,
|
||||
transparent calc(50% + 2px)
|
||||
);
|
||||
}
|
||||
|
||||
.dia-top-right-bottom-left:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(
|
||||
to top left,
|
||||
transparent calc(50% - 2px),
|
||||
black,
|
||||
transparent calc(50% + 2px)
|
||||
);
|
||||
}
|
||||
|
||||
.lack-of-potential-wrapper {
|
||||
background-image: url("/public/images/bg-dashboard.jpg");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.lack-of-potential-wrapper::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
// #lack-of-potential-fixed-container {
|
||||
// min-width: 1110px;
|
||||
// max-width: unset; /* Allow it to grow if needed */
|
||||
// }
|
||||
|
||||
// @media (max-width: 768px) {
|
||||
// #lack-of-potential-fixed-container {
|
||||
// transform: scale(0.8); /* Adjust the scale as needed */
|
||||
// }
|
||||
// }
|
||||
|
||||
// line degrees
|
||||
.line {
|
||||
background-color: black;
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
}
|
||||
.home-to-non-usaha {
|
||||
width: 100px;
|
||||
top: 13%;
|
||||
left: 38%;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.restoran-to-bapenda {
|
||||
width: 110px;
|
||||
top: 14%;
|
||||
left: 60%;
|
||||
transform: rotate(40deg);
|
||||
}
|
||||
.pbb-to-bapenda {
|
||||
width: 80px;
|
||||
top: 21%;
|
||||
left: 80%;
|
||||
}
|
||||
.reklame-to-bapenda {
|
||||
width: 120px;
|
||||
left: 75%;
|
||||
top: 30%;
|
||||
transform: rotateZ(142deg);
|
||||
}
|
||||
.non-usaha-to-bapenda {
|
||||
width: 116px;
|
||||
left: 18%;
|
||||
top: 33%;
|
||||
transform: rotateZ(124deg);
|
||||
}
|
||||
.non-usaha-to-pdam {
|
||||
width: 100px;
|
||||
left: 38%;
|
||||
top: 34%;
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
.non-usaha-to-kecamatan {
|
||||
width: 140px;
|
||||
left: 55%;
|
||||
top: 33%;
|
||||
transform: rotateZ(237deg);
|
||||
}
|
||||
.bapenda-to-usaha {
|
||||
width: 114px;
|
||||
left: 18%;
|
||||
top: 49%;
|
||||
transform: rotateZ(56deg);
|
||||
}
|
||||
.pdam-to-usaha {
|
||||
width: 88px;
|
||||
left: 39%;
|
||||
top: 49%;
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
.kecamatan-to-usaha {
|
||||
width: 118px;
|
||||
left: 56%;
|
||||
top: 50%;
|
||||
transform: rotateZ(117deg);
|
||||
}
|
||||
.usaha-to-villa {
|
||||
width: 100px;
|
||||
left: 10%;
|
||||
top: 63%;
|
||||
transform: rotateZ(143deg);
|
||||
}
|
||||
.usaha-to-pabrik {
|
||||
width: 150px;
|
||||
left: 15%;
|
||||
top: 70%;
|
||||
transform: rotateZ(143deg);
|
||||
}
|
||||
.usaha-to-pariwisata {
|
||||
width: 150px;
|
||||
left: 43%;
|
||||
top: 70%;
|
||||
transform: rotateZ(38deg);
|
||||
}
|
||||
.usaha-to-protocol {
|
||||
width: 106px;
|
||||
left: 36%;
|
||||
top: 71%;
|
||||
transform: rotateZ(86deg);
|
||||
}
|
||||
.pariwisata-to-disbudpar {
|
||||
width: 86px;
|
||||
left: 54%;
|
||||
top: 83%;
|
||||
transform: rotateZ(150deg);
|
||||
}
|
||||
.non-usaha-to-wasdal {
|
||||
width: 300px;
|
||||
left: -32%;
|
||||
top: 34%;
|
||||
transform: rotateZ(226deg);
|
||||
}
|
||||
.usaha-to-wasdal {
|
||||
width: 300px;
|
||||
left: -34%;
|
||||
top: 50%;
|
||||
transform: rotateZ(129deg);
|
||||
}
|
||||
.wasdal-to-upt {
|
||||
width: 155px;
|
||||
left: 3%;
|
||||
top: -67%;
|
||||
transform: rotateZ(127deg);
|
||||
}
|
||||
.wasdal-to-satpol {
|
||||
width: 155px;
|
||||
left: 19%;
|
||||
top: -52%;
|
||||
transform: rotateZ(76deg);
|
||||
}
|
||||
.wasdal-to-kejari {
|
||||
width: 182px;
|
||||
left: 25%;
|
||||
top: -55%;
|
||||
transform: rotateZ(51deg);
|
||||
}
|
||||
.wasdal-to-tni {
|
||||
width: 260px;
|
||||
left: 29%;
|
||||
top: -62%;
|
||||
transform: rotateZ(30deg);
|
||||
}
|
||||
.wasdal-to-potential {
|
||||
width: 50px;
|
||||
left: 28%;
|
||||
top: 41%;
|
||||
}
|
||||
.potential-to-tata-ruang {
|
||||
width: 50px;
|
||||
left: 72%;
|
||||
top: 41%;
|
||||
}
|
||||
.tata-ruang-to-non-usaha {
|
||||
width: 220px;
|
||||
left: 0%;
|
||||
top: 30%;
|
||||
transform: rotateZ(144deg);
|
||||
}
|
||||
.tata-ruang-to-usaha {
|
||||
width: 280px;
|
||||
left: 0%;
|
||||
top: 52%;
|
||||
transform: rotateZ(224deg);
|
||||
}
|
||||
.tata-ruang-to-peta {
|
||||
width: 122px;
|
||||
left: 8%;
|
||||
top: 41%;
|
||||
}
|
||||
.peta-to-tapak {
|
||||
width: 30px;
|
||||
left: 47%;
|
||||
top: 41%;
|
||||
}
|
||||
59
resources/scss/dashboards/potentials/_outside_system.scss
Normal file
59
resources/scss/dashboards/potentials/_outside_system.scss
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// outside_system.scss
|
||||
//
|
||||
.square {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.dia-top-left-bottom-right:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(
|
||||
to top right,
|
||||
transparent calc(50% - 2px),
|
||||
black,
|
||||
transparent calc(50% + 2px)
|
||||
);
|
||||
}
|
||||
|
||||
.dia-top-right-bottom-left:after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(
|
||||
to top left,
|
||||
transparent calc(50% - 2px),
|
||||
black,
|
||||
transparent calc(50% + 2px)
|
||||
);
|
||||
}
|
||||
|
||||
.outside-system-wrapper {
|
||||
background-image: url("/public/images/bg-dashboard.jpg");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
max-width: 90vw;
|
||||
}
|
||||
|
||||
.outside-system-wrapper::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
//
|
||||
// _gridjs.scss
|
||||
//
|
||||
//
|
||||
|
||||
.gridjs-container {
|
||||
color: var(--#{$prefix}body-color);
|
||||
@@ -13,7 +13,6 @@
|
||||
border: 1px solid var(--#{$prefix}border-color);
|
||||
border-radius: 0px;
|
||||
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
@@ -24,11 +23,12 @@
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar:horizontal {
|
||||
height: 5px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(var(--#{$prefix}dark-rgb), .075);
|
||||
// background-color: rgba(var(--#{$prefix}dark-rgb), 0.075);
|
||||
background-color: $primary;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
@@ -69,15 +69,13 @@ th {
|
||||
&.gridjs-th {
|
||||
border-top: 0;
|
||||
color: var(--#{$prefix}body-color);
|
||||
background-color: rgba(var(--#{$prefix}light-rgb), .75);
|
||||
|
||||
background-color: rgba(var(--#{$prefix}light-rgb), 0.75);
|
||||
}
|
||||
|
||||
&.gridjs-th-sort {
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: rgba(var(--#{$prefix}light-rgb), .85);
|
||||
background-color: rgba(var(--#{$prefix}light-rgb), 0.85);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,7 +97,6 @@ th {
|
||||
}
|
||||
|
||||
.gridjs-pagination {
|
||||
|
||||
.gridjs-pages button {
|
||||
background-color: transparent;
|
||||
color: var(--#{$prefix}link-color);
|
||||
@@ -166,7 +163,8 @@ input.gridjs-input {
|
||||
background-color: $input-bg;
|
||||
color: $input-color;
|
||||
line-height: $input-line-height;
|
||||
padding: $input-padding-y $input-padding-x $input-padding-y $input-padding-x * 2.5;
|
||||
padding: $input-padding-y $input-padding-x $input-padding-y $input-padding-x *
|
||||
2.5;
|
||||
border-radius: $input-border-radius;
|
||||
@include font-size($input-font-size);
|
||||
|
||||
@@ -203,30 +201,26 @@ th.gridjs-th-sort .gridjs-th-content {
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
&.gridjs-sort-asc,
|
||||
&.gridjs-sort-desc {
|
||||
background-size: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// gridjs selection
|
||||
.gridjs-tr-selected {
|
||||
td {
|
||||
background-color: $table-active-bg;
|
||||
}
|
||||
|
||||
.gridjs-td .gridjs-checkbox[type=checkbox] {
|
||||
.gridjs-td .gridjs-checkbox[type="checkbox"] {
|
||||
background-color: $form-check-input-checked-bg-color;
|
||||
border-color: $form-check-input-checked-border-color;
|
||||
|
||||
@if $enable-gradients {
|
||||
background-image: escape-svg($form-check-input-checked-bg-image),
|
||||
var(--#{$prefix}gradient);
|
||||
}
|
||||
|
||||
@else {
|
||||
var(--#{$prefix}gradient);
|
||||
} @else {
|
||||
background-image: escape-svg($form-check-input-checked-bg-image);
|
||||
}
|
||||
}
|
||||
@@ -253,7 +247,6 @@ button {
|
||||
}
|
||||
|
||||
.gridjs-border-none {
|
||||
|
||||
td.gridjs-td,
|
||||
th.gridjs-th {
|
||||
border-right-width: 0;
|
||||
@@ -267,11 +260,10 @@ button {
|
||||
|
||||
[data-bs-theme="dark"] {
|
||||
button {
|
||||
|
||||
&.gridjs-sort-neutral,
|
||||
&.gridjs-sort-asc,
|
||||
&.gridjs-sort-desc {
|
||||
filter: $btn-close-white-filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
@import "components/widgets";
|
||||
@import "components/circle";
|
||||
@import "components/custom_circle";
|
||||
@import "dashboards/lack-of-potential";
|
||||
@import "dashboards/potentials/inside_system";
|
||||
@import "dashboards/potentials/outside_system";
|
||||
|
||||
// Plugin
|
||||
@import "plugins/simplebar";
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
<x-toast-notification />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card w-100">
|
||||
<div class="card-body">
|
||||
<div id="table-bigdata-resumes"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="card w-100 h-100">
|
||||
<div class="card-body">
|
||||
<div id="table-bigdata-resumes"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
115
resources/views/dashboards/potentials/inside_system.blade.php
Normal file
115
resources/views/dashboards/potentials/inside_system.blade.php
Normal file
@@ -0,0 +1,115 @@
|
||||
@extends('layouts.vertical', ['subtitle' => 'Dashboards'])
|
||||
|
||||
@section('css')
|
||||
@vite(['resources/scss/dashboards/potentials/_inside_system.scss'])
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@include('layouts.partials.page-title', ['title' => 'Dashboards', 'subtitle' => 'Dalam Sistem'])
|
||||
|
||||
<div class="lack-of-potential-wrapper">
|
||||
<div class="row" id="lack-of-potential-wrapper">
|
||||
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
|
||||
<h2 class="text-danger m-0">
|
||||
ANALISA BIG DATA MELALUI APLIKASI <br> SIBEDAS PBG DALAM SISTEM
|
||||
</h2>
|
||||
<div class="text-black text-end d-flex flex-column align-items-end me-3">
|
||||
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-lack-of-potential" placeholder="Filter Date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
|
||||
<div style="position: absolute; top: 200px; left: 50px;">
|
||||
<x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" visible_data="true" data_id="restoran-count" data_count="0" />
|
||||
<div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div>
|
||||
<x-custom-circle title="PBB Bangunan" visible_data="true" data_id="pbb-bangunan-count" data_count="0" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div>
|
||||
<x-custom-circle title="Reklame" visible_data="true" data_id="reklame-count" data_count="0" size="small" style="background-color: #0e4753;" />
|
||||
<div class="square dia-top-right-bottom-left" style="top:140px;left:50px;width:150px;height:120px;"></div>
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 300px; left: 200px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-100px;left:30px;width:150px;height:120px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-100px;left:120px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="BAPENDA" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="PDAM" visible_data="true" data_id="pdam-count" data_count="0" visible_data_type="true" data_type="Pelanggan" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="KECAMATAN" size="small" style="float:left;background-color: #234f6c;" />
|
||||
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 0px; left: 270px;">
|
||||
<div class="square" style="width:5px;height:600px;background-color:black;left:70px;top:50px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:350px;left:-50px;width:120px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:350px;left:70px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="Rumah Tinggal" size="small" style="background-color: #234f6c;margin:auto;" />
|
||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:150px;" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 650px; left: 110px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:40px;width:200px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div>
|
||||
<x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" visible_data="true" data_id="villa-count" data_count="0" />
|
||||
<x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" />
|
||||
<x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" visible_data="true" data_id="pariwisata-count" data_count="0" />
|
||||
<div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div>
|
||||
<x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 280px; left: 550px;">
|
||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:-150px;width:200px;height:180px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:70px;left:-150px;width:200px;height:130px;"></div>
|
||||
<x-custom-circle title="Tim Wasdal Gabungan" size="large" style="background-color: #da6635;float:left" />
|
||||
<div class="square" style="width:650px;height:5px;background-color:black;left:100px;top:75px;"></div>
|
||||
@component('components.circle', [
|
||||
'document_title' => 'Kekurangan Potensi',
|
||||
'document_color' => '#ff5757',
|
||||
'document_type' => '',
|
||||
'document_id' => 'chart-lack-of-potential',
|
||||
'visible_small_circle' => false,
|
||||
'style' => 'margin-left:180px;top:-20px;'
|
||||
])
|
||||
@endcomponent
|
||||
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" visible_data="true" data_id="tata-ruang-count" data_count="0" />
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; top: 310px; left: 1150px;">
|
||||
<div class="square dia-top-left-bottom-right" style="top:90px;left:-100px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div>
|
||||
<x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" />
|
||||
<x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" />
|
||||
</div>
|
||||
|
||||
<x-custom-circle title="BPN" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" />
|
||||
|
||||
<div style="position: absolute; top: 470px; left: 430px;">
|
||||
<div class="square dia-top-right-bottom-left" style="top:-80px;left:20px;width:150px;height:120px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:-50px;left:100px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-50px;left:180px;width:100px;height:100px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:-60px;left:240px;width:120px;height:120px;"></div>
|
||||
<x-custom-circle title="UPT Wasdal" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="Satpol PP" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="KEJARI" size="small" style="background-color: #0f4853;float:left;" />
|
||||
<x-custom-circle title="TNI & POLRI" size="small" style="background-color: #0f4853;float:left;" />
|
||||
</div>
|
||||
|
||||
<x-custom-circle title="UUCK" size="small" style="background-color: #2390af;position:absolute;left:980px;top:500px;" />
|
||||
|
||||
<div style="position: absolute; top: 50px; left: 1100px;">
|
||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" visible_data="true" data_id="tata-ruang-usaha-count" data_count="0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
@vite(['resources/js/dashboards/potentials/inside_system.js'])
|
||||
@endsection
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<!-- base layout -->
|
||||
@extends('layouts.vertical', ['subtitle' => 'Dashboards'])
|
||||
<!-- style -->
|
||||
@section('css')
|
||||
@vite(['resources/scss/dashboards/potentials/_outside_system.scss'])
|
||||
@endsection
|
||||
<!-- content -->
|
||||
@section('content')
|
||||
@include('layouts.partials.page-title', ['title' => 'Dashboards', 'subtitle' => 'Luar Sistem'])
|
||||
<div class="outside-system-wrapper" id="outside-system-wrapper">
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
|
||||
<h2 class="text-danger m-0">
|
||||
ANALISA BIG DATA MELALUI APLIKASI <br>
|
||||
SIBEDAS PBG LUAR SISTEM
|
||||
</h2>
|
||||
<div class="text-black text-end d-flex flex-column align-items-end me-3">
|
||||
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-outside-system" placeholder="Filter Date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="outside-system-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
|
||||
<div style="position: absolute; top: 70px; left: 50px; width: 200px; height: 500px;">
|
||||
@component('components.circle', [
|
||||
'document_title' => 'Non Usaha',
|
||||
'document_color' => '#399787',
|
||||
'document_type' => 'Berkas',
|
||||
'document_id' => 'outside-system-non-business',
|
||||
'visible_small_circle' => true,
|
||||
'style' => 'top:10px;'
|
||||
])
|
||||
@endcomponent
|
||||
<div class="square dia-top-right-bottom-left" style="top:10px;left:180px;width:230px;height:120px;"></div>
|
||||
@component('components.circle', [
|
||||
'document_title' => 'Usaha',
|
||||
'document_color' => '#5e7c89',
|
||||
'document_type' => 'Berkas',
|
||||
'document_id' => 'outside-system-business',
|
||||
'visible_small_circle' => true,
|
||||
'style' => 'top:300px;'
|
||||
])
|
||||
@endcomponent
|
||||
<div class="square dia-top-right-bottom-left" style="top:320px;left:170px;width:200px;height:100px;"></div>
|
||||
|
||||
<div class="square dia-top-left-bottom-right" style="top:120px;left:180px;width:500px;height:120px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:410px;left:180px;width:500px;height:160px;"></div>
|
||||
</div>
|
||||
<div style="position: absolute; top: 50px; left: 350px; width: 200px; height: 550px;">
|
||||
<div class="square" style="width:200px;height:2px;background-color:black;left:100px;top:70px;"></div>
|
||||
<x-custom-circle title="Keterangan Rencana Kota (KRK)" size="large" style="background-color: #306364;position: absolute;" />
|
||||
<x-custom-circle title="Keterangan Rencana Kota (KRK)" size="large" style="background-color: #38b64b;position: absolute; top: 320px;" />
|
||||
<div class="square" style="width:200px;height:2px;background-color:black;left:100px;top:390px;"></div>
|
||||
</div>
|
||||
<div style="position: absolute; top: 50px; left: 600px; width: 200px; height: 650px;">
|
||||
<x-custom-circle title="Samirindu DPMPTSP" size="large" style="background-color: #0e4753;position: absolute; top: 0px;" />
|
||||
<x-custom-circle title="RAB dan Gambar" size="large" style="background-color: #f0195b;position: absolute; top: 160px;" />
|
||||
<x-custom-circle title="OSS RBA (Nasional)" size="large" style="background-color: #38b64b;position: absolute; top: 320px;" />
|
||||
<x-custom-circle title="Dokumen Lingkungan (DLH)" size="large" style="background-color: #393536;position: absolute; top: 480px;" />
|
||||
<div class="square dia-top-left-bottom-right" style="top:50px;left:120px;width:250px;height:250px;"></div>
|
||||
<div class="square dia-top-left-bottom-right" style="top:230px;left:120px;width:220px;height:100px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:320px;left:120px;width:200px;height:100px;"></div>
|
||||
<div class="square dia-top-right-bottom-left" style="top:350px;left:120px;width:250px;height:200px;"></div>
|
||||
</div>
|
||||
<div style="position: absolute; top: 50px; left: 900px; width: 200px; height: 550px;">
|
||||
<x-custom-circle title="Pemohon" size="large" style="background-color: #393536;position: absolute; top: 250px;" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
<!-- javascripts -->
|
||||
@section('scripts')
|
||||
@vite(['resources/js/dashboards/potentials/outside_system.js'])
|
||||
@endsection
|
||||
@@ -16,58 +16,49 @@
|
||||
<ul class="navbar-nav" id="navbar-nav">
|
||||
<li class="menu-title">Menu</li>
|
||||
|
||||
@foreach ($menus as $menu)
|
||||
<li class="nav-item">
|
||||
<!-- parent menu -->
|
||||
@if ($menu->parent_id == null)
|
||||
<a class="nav-link menu-arrow" href="#sidebar-{{$menu->id}}" data-bs-toggle="collapse" role="button"
|
||||
aria-expanded="true" aria-controls="sidebar-{{$menu->id}}">
|
||||
@php
|
||||
// Fungsi rekursif untuk menampilkan menu bertingkat dengan indentasi
|
||||
function renderMenu($menus, $depth = 0) {
|
||||
foreach ($menus as $menu) {
|
||||
$collapseId = "sidebar-" . $menu->id; // Unique ID untuk Bootstrap Collapse
|
||||
$hasChildren = $menu->children->count() > 0; // Cek apakah punya anak
|
||||
$marginLeft = $depth * 5; // Set jarak margin berdasarkan level
|
||||
|
||||
echo '<li class="nav-item">';
|
||||
|
||||
// Menu utama / anak (dengan dropdown jika punya anak)
|
||||
echo '<a class="nav-link ' . ($hasChildren ? 'menu-arrow' : '') . '"
|
||||
href="' . ($hasChildren ? "#$collapseId" : ($menu->url ? (Route::has($menu->url) ? route($menu->url, ['menu_id' => $menu->id]) : $menu->url . '?menu_id=' . $menu->id) : '#')) . '"
|
||||
' . ($hasChildren ? 'data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="' . $collapseId . '"' : '') . '>
|
||||
<span class="nav-icon">
|
||||
<iconify-icon icon="{{$menu->icon}}"></iconify-icon>
|
||||
<iconify-icon icon="' . $menu->icon . '"></iconify-icon>
|
||||
</span>
|
||||
<span class="nav-text">{{$menu->name}}</span>
|
||||
</a>
|
||||
@endif
|
||||
<!-- children menu foreach -->
|
||||
@if ($menu->children->count() > 0)
|
||||
<div class="collapse" id="sidebar-{{$menu->id}}">
|
||||
<ul class="nav sub-navbar-nav">
|
||||
@foreach ( $menu->children as $child)
|
||||
<li class="sub-nav-item">
|
||||
<a class="sub-nav-link" href="{{ $child->url ? (Route::has($child->url) ? route($child->url) : $child->url) : '#' }}">
|
||||
{{ $child->name }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
<span class="nav-text">' . $menu->name . '</span>
|
||||
</a>';
|
||||
// Jika menu punya anak, buat sub-menu
|
||||
if ($hasChildren) {
|
||||
echo '<div class="collapse" id="' . $collapseId . '">
|
||||
<ul class="nav sub-navbar-nav">';
|
||||
renderMenu($menu->children, $depth + 1); // Rekursi dengan level lebih dalam
|
||||
echo '</ul></div>';
|
||||
}
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
@php
|
||||
// Tampilkan hanya menu dengan parent_id NULL (menu utama)
|
||||
renderMenu($menus->where('parent_id', null));
|
||||
@endphp
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Efek Bintang -->
|
||||
<div class="animated-stars">
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
<div class="shooting-star"></div>
|
||||
@for ($i = 0; $i < 20; $i++)
|
||||
<div class="shooting-star"></div>
|
||||
@endfor
|
||||
</div>
|
||||
@@ -54,6 +54,13 @@
|
||||
.floating-icon.animate {
|
||||
animation: bounce 1s infinite;
|
||||
}
|
||||
.menu-arrow {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-link[aria-expanded="true"] .menu-arrow i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
<head>
|
||||
@@ -71,21 +78,9 @@
|
||||
@include('layouts.partials/topbar')
|
||||
|
||||
<div class="page-content">
|
||||
|
||||
{{-- <div class="container-fluid">
|
||||
|
||||
@yield('content')
|
||||
<div>
|
||||
<iconify-icon icon="solar:chat-square-outline" class="fs-35 align-middle"></iconify-icon>
|
||||
</div>
|
||||
|
||||
</div> --}}
|
||||
<div class="container-fluid">
|
||||
@yield('content')
|
||||
|
||||
{{-- <div class="floating-icon">
|
||||
|
||||
</div> --}}
|
||||
|
||||
@if (!Request::is('chatbot') && !Request::is('main-chatbot'))
|
||||
<a href="{{ route('chatbot.index') }}" class="floating-icon">
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@extends('layouts.vertical', ['subtitle' => 'Detail'])
|
||||
|
||||
@section('css')
|
||||
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@@ -92,6 +93,11 @@
|
||||
<span class="d-none d-sm-block">PBG Task Prasarana</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#pbgTaskAssignments" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
|
||||
<span class="d-none d-sm-block">Penugasan</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -246,6 +252,10 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="pbgTaskAssignments">
|
||||
<input type="hidden" id="uuid" value="{{ $data->uuid }}" />
|
||||
<div id="table-pbg-task-assignments"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,4 +265,5 @@
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
@vite(['resources/js/pbg-task/show.js'])
|
||||
@endsection
|
||||
@@ -15,6 +15,7 @@ use App\Http\Controllers\Api\RequestAssignmentController;
|
||||
use App\Http\Controllers\Api\RolesController;
|
||||
use App\Http\Controllers\Api\ScrapingController;
|
||||
use App\Http\Controllers\Api\SpatialPlanningsController;
|
||||
use App\Http\Controllers\Api\TaskAssignmentsController;
|
||||
use App\Http\Controllers\Api\UsersController;
|
||||
use App\Http\Controllers\Settings\SyncronizeController;
|
||||
use App\Http\Controllers\Api\AdvertisementController;
|
||||
@@ -102,6 +103,7 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
||||
Route::get('/get-user-token', [SyncronizeController::class, 'getUserToken'])->name('api.task.token');
|
||||
Route::get('/get-index-integration-retribution/{uuid}', [SyncronizeController::class, 'syncIndexIntegration'])->name('api.task.inntegration');
|
||||
Route::get('/sync-task-submit/{uuid}', [SyncronizeController::class, 'syncTaskDetailSubmit'])->name('api.task.submit');
|
||||
Route::get('/sync-task-assignments/{uuid}', [SyncronizeController::class, 'syncTaskAssignments'])->name('api.task.assignments');
|
||||
|
||||
// menus api
|
||||
Route::controller(MenusController::class)->group(function (){
|
||||
@@ -139,4 +141,9 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
||||
Route::get('/bigdata-resume', 'index')->name('api.bigdata-resume');
|
||||
Route::get('/bigdata-report', 'bigdata_report')->name('api.bigdata-report');
|
||||
});
|
||||
|
||||
// task-assignments
|
||||
Route::controller(TaskAssignmentsController::class)->group(function (){
|
||||
Route::get('/task-assignments/{uuid}', 'index')->name('api.task-assignments');
|
||||
});
|
||||
});
|
||||
@@ -4,10 +4,10 @@ use App\Http\Controllers\BigdataResumesController;
|
||||
use App\Http\Controllers\BusinessOrIndustriesController;
|
||||
use App\Http\Controllers\CustomersController;
|
||||
use App\Http\Controllers\Dashboards\LackOfPotentialController;
|
||||
use App\Http\Controllers\Dashboards\PotentialsController;
|
||||
use App\Http\Controllers\DataSettingController;
|
||||
use App\Http\Controllers\Dashboards\BigDataController;
|
||||
use App\Http\Controllers\GoogleApisController;
|
||||
use App\Http\Controllers\Home\HomeController;
|
||||
use App\Http\Controllers\Master\UsersController;
|
||||
use App\Http\Controllers\MenusController;
|
||||
use App\Http\Controllers\RequestAssignment\PbgTaskController;
|
||||
@@ -19,7 +19,6 @@ use App\Http\Controllers\Data\UmkmController;
|
||||
use App\Http\Controllers\Data\TourismController;
|
||||
use App\Http\Controllers\Data\SpatialPlanningController;
|
||||
use App\Http\Controllers\Report\ReportTourismController;
|
||||
use App\Http\Controllers\SpatialPlanningsController;
|
||||
use App\Http\Controllers\Chatbot\ChatbotController;
|
||||
use App\Http\Controllers\ChatbotPimpinan\ChatbotPimpinanController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@@ -37,6 +36,8 @@ Route::group(['middleware' => 'auth'], function(){
|
||||
Route::get('/dashboard-pbg', [BigDataController::class, 'pbg'])->name('dashboard.pbg');
|
||||
Route::get('/lack-of-potential', [LackOfPotentialController::class, 'lack_of_potential'])->name('dashboard.lack_of_potential');
|
||||
Route::get('/maps', [GoogleApisController::class, 'index'])->name('dashboard.maps');
|
||||
Route::get('/inside-system', [PotentialsController::class, 'inside_system'])->name('dashboard.potentials.inside_system');
|
||||
Route::get('/outside-system', [PotentialsController::class, 'outside_system'])->name('dashboard.potentials.outside_system');
|
||||
});
|
||||
|
||||
// settings
|
||||
|
||||
@@ -48,13 +48,13 @@ export default defineConfig({
|
||||
|
||||
//js-additional
|
||||
"resources/js/settings/syncronize/syncronize.js",
|
||||
"resources/js/pbg-task/index.js",
|
||||
"resources/js/settings/general/general-settings.js",
|
||||
"resources/js/tables/common-table.js",
|
||||
|
||||
// dashboards
|
||||
"resources/js/dashboards/bigdata.js",
|
||||
"resources/js/dashboards/lack-of-potential.js",
|
||||
"resources/js/dashboards/potentials/inside_system.js",
|
||||
"resources/js/dashboards/potentials/outside_system.js",
|
||||
// roles
|
||||
"resources/js/roles/index.js",
|
||||
"resources/js/roles/create.js",
|
||||
@@ -101,6 +101,9 @@ export default defineConfig({
|
||||
"resources/js/bigdata-resumes/index.js",
|
||||
"resources/js/chatbot/index.js",
|
||||
"resources/js/chatbot-pimpinan/index.js",
|
||||
//pbg-task
|
||||
"resources/js/pbg-task/index.js",
|
||||
"resources/js/pbg-task/show.js",
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user