diff --git a/app/Http/Controllers/Api/ImportDatasourceController.php b/app/Http/Controllers/Api/ImportDatasourceController.php index dd634a6..f77c4a0 100644 --- a/app/Http/Controllers/Api/ImportDatasourceController.php +++ b/app/Http/Controllers/Api/ImportDatasourceController.php @@ -2,13 +2,17 @@ namespace App\Http\Controllers\Api; +use App\Enums\ImportDatasourceStatus; use App\Http\Controllers\Controller; use App\Http\Resources\ImportDatasourceResource; use App\Models\ImportDatasource; +use App\Traits\GlobalApiResponse; +use Exception; use Illuminate\Http\Request; class ImportDatasourceController extends Controller { + use GlobalApiResponse; /** * Display a listing of the resource. */ @@ -23,6 +27,19 @@ class ImportDatasourceController extends Controller return ImportDatasourceResource::collection($query->paginate()); } + public function checkImportDatasource(){ + try{ + $data = ImportDatasource::where("status",ImportDatasourceStatus::Processing->value )->count(); + $result = [ + "can_execute" => $data === 0, + "total_processing" => $data + ]; + return response()->json( $result , 200); + }catch(Exception $ex){ + return response()->json(["message" => $ex->getMessage(), 500]); + } + } + /** * Store a newly created resource in storage. */ diff --git a/app/Http/Controllers/Api/ScrapingController.php b/app/Http/Controllers/Api/ScrapingController.php new file mode 100644 index 0000000..ef8b7ff --- /dev/null +++ b/app/Http/Controllers/Api/ScrapingController.php @@ -0,0 +1,61 @@ +value)->count(); + if($check_datasource > 0){ + return $this->resError("Failed to execute while processing another scraping"); + } + + // run service artisan command + Artisan::call("app:execute-scraping"); + return $this->resSuccess("Success execute scraping service please wait"); + } + + /** + * 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) + { + // + } +} diff --git a/app/Models/ImportDatasource.php b/app/Models/ImportDatasource.php index 887c14b..9bdb995 100644 --- a/app/Models/ImportDatasource.php +++ b/app/Models/ImportDatasource.php @@ -2,10 +2,12 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class ImportDatasource extends Model { + use HasFactory; protected $table = 'import_datasources'; protected $fillable = [ 'id', diff --git a/app/ServiceClient.php b/app/ServiceClient.php index 3595bce..21edd49 100644 --- a/app/ServiceClient.php +++ b/app/ServiceClient.php @@ -26,13 +26,15 @@ class ServiceClient ); } - public function makeRequest($url, $method = 'GET', $body = null, $headers = []){ + public function makeRequest($url, $method = 'GET', $body = null, $headers = [], $timeout = 300){ try { $headers = array_merge($this->headers, $headers); $options = [ 'headers' => $headers, + 'timeout' => $timeout, + 'connect_timeout' => 60 ]; if ($body) { diff --git a/app/ServiceSIMBG.php b/app/ServiceSIMBG.php index c65a52d..b4f17d2 100644 --- a/app/ServiceSIMBG.php +++ b/app/ServiceSIMBG.php @@ -48,21 +48,13 @@ class ServiceSIMBG return $res; } - public function syncIndexIntegration($uuid) + public function syncIndexIntegration($uuid, $token) { $clientHelper = new ServiceClient($this->simbg_host); $url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/"; - $resToken = $this->getToken(); - - if (!isset($resToken) || empty($resToken->original['data']['token']['access'])) { - // Log error - Log::error("Token not retrieved for syncIndexIntegration", ['uuid' => $uuid]); - return null; - } - - $apiToken = $resToken->original['data']['token']['access']; + $headers = [ - 'Authorization' => "Bearer " . $apiToken, + 'Authorization' => "Bearer " . $token, ]; $res = $clientHelper->get($url, $headers); @@ -70,16 +62,16 @@ class ServiceSIMBG if (empty($res->original['success']) || !$res->original['success']) { // Log error Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]); - return null; + return false; } $data = $res->original['data']['data'] ?? null; if (!$data) { Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]); - return null; + return false; } - PbgTaskIndexIntegrations::updateOrCreate( + $resultData = PbgTaskIndexIntegrations::updateOrCreate( ['pbg_task_uid' => $uuid], [ 'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null, @@ -93,19 +85,25 @@ class ServiceSIMBG ); // Log success - Log::info("syncIndexIntegration completed successfully", ['uuid' => $uuid]); + if ($resultData->wasRecentlyCreated) { + Log::info("integration created successfully", ['uuid' => $uuid]); + } else { + Log::info("integration updated successfully", ['uuid' => $uuid]); + } + + return true; } public function syncTaskList() { $clientHelper = new ServiceClient($this->simbg_host); - $resToken = $this->getToken(); + $initResToken = $this->getToken(); $importDatasource = ImportDatasource::create([ 'status' => ImportDatasourceStatus::Processing->value, ]); - if (empty($resToken->original['data']['token']['access'])) { + if (empty($initResToken->original['data']['token']['access'])) { $importDatasource->update([ 'status' => ImportDatasourceStatus::Failed->value, 'message' => 'Failed to retrieve token' @@ -113,14 +111,14 @@ class ServiceSIMBG return $this->resError("Failed to retrieve token"); } - $apiToken = $resToken->original['data']['token']['access']; + $apiToken = $initResToken->original['data']['token']['access']; $headers = ['Authorization' => "Bearer " . $apiToken]; $url = "/api/pbg/v1/list/?page=1&size={$this->fetch_per_page}&sort=ASC"; $initialResponse = $clientHelper->get($url, $headers); $totalPage = $initialResponse->original['data']['total_page'] ?? 0; - if ($totalPage === 0) { + if ($totalPage == 0) { $importDatasource->update([ 'status' => ImportDatasourceStatus::Failed->value, 'message' => 'Invalid response: no total_page' @@ -132,14 +130,26 @@ class ServiceSIMBG for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) { $pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC"; - $token = $this->getToken(); + $getToken = $this->getToken(); + if (empty($getToken->original['data']['token']['access'])) { + $importDatasource->update([ + 'status' => ImportDatasourceStatus::Failed->value, + 'message' => 'Failed to retrieve token' + ]); + break; + } + $token = $getToken->original['data']['token']['access']; $headers = ['Authorization' => "Bearer " . $token]; $response = $clientHelper->get($pageUrl, $headers); $tasks = $response->original['data']['data'] ?? []; if (empty($tasks)) { + $importDatasource->update([ + 'status' => ImportDatasourceStatus::Failed->value, + 'message' => 'No data found on page' + ]); Log::warning("No data found on page", ['page' => $currentPage]); - continue; + break; } Log::info("executed page", ['page' => $currentPage, 'total' => $totalPage]); @@ -170,15 +180,20 @@ class ServiceSIMBG 'created_at' => now(), ]; - $this->syncIndexIntegration($item['uid']); - $this->syncTaskDetailSubmit($item['uid']); + $this->syncIndexIntegration($item['uid'], $token); + $this->syncTaskDetailSubmit($item['uid'], $token); $savedCount++; } catch (Exception $e) { $failedCount++; + $importDatasource->update([ + 'status' => ImportDatasourceStatus::Failed->value, + 'message' => "Successfully processed: $savedCount, Failed: $failedCount" + ]); Log::error("Failed to process task", [ 'error' => $e->getMessage(), 'task' => $item, ]); + break; } } @@ -201,21 +216,13 @@ class ServiceSIMBG } - public function syncTaskDetailSubmit($uuid) + public function syncTaskDetailSubmit($uuid, $token) { $clientHelper = new ServiceClient($this->simbg_host); - $resToken = $this->getToken(); - if (!isset($resToken) || empty($resToken->original['data']['token']['access'])) { - // Log error - Log::error("Token not retrieved for syncTaskDetailSubmit"); - return null; - } - - $apiToken = $resToken->original['data']['token']['access']; $url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/"; $headers = [ - 'Authorization' => "Bearer " . $apiToken, + 'Authorization' => "Bearer " . $token, ]; $res = $clientHelper->get($url, $headers); @@ -223,13 +230,13 @@ class ServiceSIMBG if (empty($res->original['success']) || !$res->original['success']) { // Log error Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]); - return null; + return false; } $data = $res->original['data']['data'] ?? []; if (empty($data)) { Log::error("No data returned from API", ['url' => $url, 'uuid' => $uuid]); - return null; + return false; } $detailCreatedAt = isset($data['created_at']) @@ -281,7 +288,8 @@ class ServiceSIMBG PbgTaskPrasarana::upsert($insertData, ['pbg_task_uid', 'prasarana_id']); } - Log::info("syncTaskDetailSubmit completed successfully", ['uuid' => $uuid]); + Log::info("retribution and prasarana successfully", ['uuid' => $uuid]); + return true; } } diff --git a/app/Traits/GlobalApiResponse.php b/app/Traits/GlobalApiResponse.php index 8d84aca..faff6ad 100644 --- a/app/Traits/GlobalApiResponse.php +++ b/app/Traits/GlobalApiResponse.php @@ -12,10 +12,6 @@ trait GlobalApiResponse 'success' => true, 'data' => $result ]; - - if(!empty($message)){ - $response['message'] = $message; - } return response()->json($response, $code); } diff --git a/resources/js/master/users/users.js b/resources/js/master/users/users.js index bd42f27..90a3b13 100644 --- a/resources/js/master/users/users.js +++ b/resources/js/master/users/users.js @@ -37,7 +37,9 @@ class UsersTable { url: `${GlobalConfig.apiHost}/api/users`, credentials: "include", headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, "Content-Type": "application/json", }, then: (data) => diff --git a/resources/js/request-assignment/request-assignment.js b/resources/js/request-assignment/request-assignment.js index 3c7ec21..f270293 100644 --- a/resources/js/request-assignment/request-assignment.js +++ b/resources/js/request-assignment/request-assignment.js @@ -11,15 +11,15 @@ class RequestAssignment { new Grid({ columns: [ "ID", - "Name", - "Condition", + {name: "Name", width: "15%"}, + {name: "Condition", width: "7%"}, "Registration Number", "Document Number", - "Address", + {name: "Address", width: "30%"}, "Status", "Function Type", "Consultation Type", - "Due Date", + {name: "Due Date", width: "7%"}, ], search: { server: { @@ -40,7 +40,9 @@ class RequestAssignment { url: `${GlobalConfig.apiHost}/api/request-assignments`, credentials: "include", headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, "Content-Type": "application/json", }, then: (data) => diff --git a/resources/js/settings/general/general-settings.js b/resources/js/settings/general/general-settings.js index ef29a76..383ab6b 100644 --- a/resources/js/settings/general/general-settings.js +++ b/resources/js/settings/general/general-settings.js @@ -22,8 +22,8 @@ class SyncronizeTask { console.log("cell data", cell); return gridjs.html(`
`); }, diff --git a/resources/js/settings/syncronize/syncronize.js b/resources/js/settings/syncronize/syncronize.js index eb10c58..a09b5d1 100644 --- a/resources/js/settings/syncronize/syncronize.js +++ b/resources/js/settings/syncronize/syncronize.js @@ -6,7 +6,7 @@ import GlobalConfig from "../../global-config.js"; class SyncronizeTask { init() { this.initTableImportDatasources(); - this.onSyncSubmit(); + this.handleSubmitSync(); } initTableImportDatasources() { new Grid({ @@ -46,20 +46,71 @@ class SyncronizeTask { }, }).render(document.getElementById("table-import-datasources")); } - onSyncSubmit() { - const form = document.getElementById("sync-form"); - if (form) { - form.addEventListener("submit", function (event) { - event.preventDefault(); // Prevent the default form submission - - const button = document.getElementById("btn-sync-submit"); - if (button) { - button.disabled = true; - button.innerText = "Processing..."; - } - form.submit(); - }); - } + handleSubmitSync() { + const button = document.getElementById("btn-sync-submit"); + + // Check if the button should be enabled or disabled based on the status + fetch(`${GlobalConfig.apiHost}/api/import-datasource/check-datasource`, { + method: "GET", + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + } + }) + .then(response => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) + .then(data => { + console.log("data check button sync", data.can_execute); + button.disabled = !data.can_execute; + + // If the button is enabled, add click event to trigger sync + if (!button.disabled) { + button.addEventListener("click", function(e) { + button.disabled = true; // Disable button to prevent multiple clicks + button.textContent = "Syncing..."; // Change button text to show syncing + + // Trigger the scraping API call + fetch(`${GlobalConfig.apiHost}/api/scraping`, { + method: "GET", + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + } + }) + .then(response => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) + .then(data => { + console.log("data sync button", data); + alert("Synchronization executed successfully"); + window.location.reload(); + }) + .catch(err => { + console.error("Fetch error:", err); + alert("An error occurred during synchronization"); + }) + .finally(() => { + button.disabled = false; // Re-enable the button after the request is complete + button.textContent = "Sync Data"; // Reset button text + }); + }); + } + }) + .catch(err => { + console.error("Fetch error:", err); + alert("An error occurred while checking the datasource"); + }); } } document.addEventListener("DOMContentLoaded", function (e) { diff --git a/resources/scss/config/_variables.scss b/resources/scss/config/_variables.scss index 8b92a24..8a129fc 100755 --- a/resources/scss/config/_variables.scss +++ b/resources/scss/config/_variables.scss @@ -64,10 +64,10 @@ $blue: #1a80f8; $indigo: #53389f; $purple: #7e67fe; $pink: #ff86c8; -$red: #ed321f; +$red: #bd1300; $orange: #f0934e; -$yellow: #fb9f68; -$green: #21d760; +$yellow: #dfdc40; +$green: #00c91b; $teal: #040505; $cyan: #1ab0f8; // scss-docs-end color-variables @@ -81,7 +81,7 @@ $purple: $purple; $pink: $pink; $danger: $red; $orange: $orange; -$warning: $orange; +$warning: $yellow; $success: $green; $info: $cyan; $light: $gray-200; diff --git a/resources/views/layouts/partials/topbar.blade.php b/resources/views/layouts/partials/topbar.blade.php index 9b6cae6..39795b6 100755 --- a/resources/views/layouts/partials/topbar.blade.php +++ b/resources/views/layouts/partials/topbar.blade.php @@ -11,25 +11,25 @@ -