add sync task assignment pbg

This commit is contained in:
arifal
2025-03-06 00:13:13 +07:00
parent 86d694bcac
commit 1f33d0de4e
14 changed files with 389 additions and 40 deletions

View File

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

View File

@@ -33,7 +33,7 @@ class SyncronizeController extends Controller
public function syncIndexIntegration(Request $request, $uuid){ public function syncIndexIntegration(Request $request, $uuid){
$token = $request->get('token'); $token = $request->get('token');
$res = $this->service_simbg->syncIndexIntegration($uuid, $token); $res = $this->service_simbg->syncIndexIntegration($uuid);
return $res; return $res;
} }
@@ -42,4 +42,9 @@ class SyncronizeController extends Controller
$res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token); $res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token);
return $res; return $res;
} }
public function syncTaskAssignments($uuid){
$res = $this->service_simbg->syncTaskAssignments($uuid);
return $res;
}
} }

View File

@@ -14,6 +14,7 @@ class SyncronizeSIMBG implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 1;
public function __construct() public function __construct()
{ {

View File

@@ -41,4 +41,9 @@ class PbgTask extends Model
public function googleSheet(){ public function googleSheet(){
return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number'); return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number');
} }
public function taskAssignments()
{
return $this->hasMany(TaskAssignment::class, 'pbg_task_uid', 'uuid');
}
} }

View 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'
];
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');
}
}

View File

@@ -19,6 +19,9 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
$this->app->singleton(GoogleSheetService::class, function () {
return new GoogleSheetService();
});
$this->app->singleton(ServiceSIMBG::class, function ($app) { $this->app->singleton(ServiceSIMBG::class, function ($app) {
return new ServiceSIMBG($app->make(GoogleSheetService::class)); return new ServiceSIMBG($app->make(GoogleSheetService::class));
}); });

View File

@@ -51,10 +51,26 @@ class ServiceClient
$resultResponse = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR); $resultResponse = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
return $this->resSuccess($resultResponse); return $this->resSuccess($resultResponse);
} catch (Exception $e) { } catch (\GuzzleHttp\Exception\ClientException $e) {
\Log::error('error from client service'. $e->getMessage()); // Handle 4xx errors (e.g., 401 Unauthorized)
return $this->resError($e->getMessage()); $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 // Fungsi untuk melakukan permintaan GET

View File

@@ -9,6 +9,7 @@ use App\Models\ImportDatasource;
use App\Models\PbgTaskIndexIntegrations; use App\Models\PbgTaskIndexIntegrations;
use App\Models\PbgTaskPrasarana; use App\Models\PbgTaskPrasarana;
use App\Models\PbgTaskRetributions; use App\Models\PbgTaskRetributions;
use App\Models\TaskAssignment;
use Exception; use Exception;
use App\Models\PbgTask; use App\Models\PbgTask;
use App\Traits\GlobalApiResponse; use App\Traits\GlobalApiResponse;
@@ -66,12 +67,19 @@ class ServiceSIMBG
} }
} }
public function syncIndexIntegration($uuids, $token) public function syncIndexIntegration($uuids)
{ {
try{ try{
if(empty($uuids)){ if(empty($uuids)){
return false; 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 = []; $integrations = [];
foreach($uuids as $uuid){ foreach($uuids as $uuid){
@@ -120,6 +128,7 @@ class ServiceSIMBG
public function syncTaskPBG() public function syncTaskPBG()
{ {
try { try {
Log::info("Processing google sheet sync");
$importDatasource = ImportDatasource::create([ $importDatasource = ImportDatasource::create([
'status' => ImportDatasourceStatus::Processing->value, '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 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 && isset($row[0]) && trim($row[0]) === "Grand Total") {
if ($found_section === "MENUNGGU_KLIK_DPMPTSP") { if ($found_section === "MENUNGGU_KLIK_DPMPTSP") {
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $row[2] ?? null; $data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $this->convertToInteger($row[2]) ?? null;
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $row[3] ?? null; $data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $this->convertToDecimal($row[3]) ?? null;
} elseif ($found_section === "REALISASI_TERBIT_PBG") { } elseif ($found_section === "REALISASI_TERBIT_PBG") {
$data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $row[2] ?? null; $data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $this->convertToInteger($row[2]) ?? null;
$data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $row[4] ?? null; $data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $this->convertToDecimal($row[4]) ?? null;
} elseif ($found_section === "PROSES_DINAS_TEKNIS") { } elseif ($found_section === "PROSES_DINAS_TEKNIS") {
$data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $row[2] ?? null; $data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $this->convertToInteger($row[2]) ?? null;
$data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $row[3] ?? null; $data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $this->convertToDecimal($row[3]) ?? null;
} }
// Reset section tracking after capturing "Grand Total" // 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) { foreach ($data_setting_result as $key => $value) {
DataSetting::updateOrInsert( DataSetting::updateOrInsert(
["key" => $key], // Find by key ["key" => $key], // Find by key
@@ -167,7 +178,6 @@ class ServiceSIMBG
); );
} }
$mapToUpsert = []; $mapToUpsert = [];
$count = 0;
foreach($sheetData as $data){ foreach($sheetData as $data){
$mapToUpsert[] = $mapToUpsert[] =
@@ -289,7 +299,7 @@ class ServiceSIMBG
if (empty($initResToken->original['data']['token']['access'])) { if (empty($initResToken->original['data']['token']['access'])) {
$importDatasource->update([ $importDatasource->update([
'status' => ImportDatasourceStatus::Failed->value, 'status' => ImportDatasourceStatus::Failed->value,
'message' => 'Failed to retrieve token' 'response_body' => 'Failed to retrieve token'
]); ]);
return $this->resError("Failed to retrieve token"); return $this->resError("Failed to retrieve token");
} }
@@ -303,20 +313,57 @@ class ServiceSIMBG
if ($totalPage == 0) { if ($totalPage == 0) {
$importDatasource->update([ $importDatasource->update([
'status' => ImportDatasourceStatus::Failed->value, 'status' => ImportDatasourceStatus::Failed->value,
'message' => 'Invalid response: no total_page' 'response_body' => 'Invalid response: no total_page'
]); ]);
return $this->resError("Invalid response from API"); return $this->resError("Invalid response from API");
} }
$savedCount = $failedCount = 0; $savedCount = $failedCount = 0;
Log::info("Fetching tasks", ['total page' => $totalPage]);
for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) { for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) {
try { try {
$pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC"; $pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
Log::info("Fetching tasks", ['currentPage' => $currentPage]); 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') {
Log::warning("Token is invalid, refreshing token...");
// Regenerate token
$initResToken = $this->getToken();
// Check if new token is valid
if (!empty($initResToken->original['data']['token']['access'])) {
$new_token = $initResToken->original['data']['token']['access'];
// **Fix: Update headers before retrying**
$headers['Authorization'] = "Bearer " . $new_token;
Log::info("Token refreshed successfully, retrying API request...");
continue; // Retry with new token
} 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'] ?? []; $tasks = $response->original['data']['data'] ?? [];
if (empty($tasks)) { if (empty($tasks)) {
@@ -351,6 +398,7 @@ class ServiceSIMBG
]; ];
$this->syncTaskDetailSubmit($item['uid'], $apiToken); $this->syncTaskDetailSubmit($item['uid'], $apiToken);
$this->syncTaskAssignments($item['uid']);
$savedCount++; $savedCount++;
} catch (Exception $e) { } catch (Exception $e) {
$failedCount++; $failedCount++;
@@ -371,7 +419,7 @@ class ServiceSIMBG
]); ]);
$uuids = array_column($tasksCollective, 'uuid'); $uuids = array_column($tasksCollective, 'uuid');
$this->syncIndexIntegration($uuids, $apiToken); $this->syncIndexIntegration($uuids);
} }
} catch (Exception $e) { } catch (Exception $e) {
Log::error("Failed to process page", [ Log::error("Failed to process page", [
@@ -400,34 +448,61 @@ class ServiceSIMBG
if (isset($importDatasource)) { if (isset($importDatasource)) {
$importDatasource->update([ $importDatasource->update([
'status' => ImportDatasourceStatus::Failed->value, 'status' => ImportDatasourceStatus::Failed->value,
'message' => 'Critical failure: ' . $e->getMessage() 'response_body' => 'Critical failure: ' . $e->getMessage()
]); ]);
} }
return $this->resError("Critical failure occurred: " . $e->getMessage()); return $this->resError("Critical failure occurred: " . $e->getMessage());
} }
} }
public function syncTaskDetailSubmit($uuid, $token) public function syncTaskDetailSubmit($uuid, $token)
{ {
try{ try{
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/"; $url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/";
$headers = [ $headers = [
'Authorization' => "Bearer " . $token, 'Authorization' => "Bearer " . $token,
]; ];
$res = $this->service_client->get($url, $headers); for ($attempt = 0; $attempt < 2; $attempt++) { // Try twice (original + retry)
$res = $this->service_client->get($url, $headers);
if (empty($res->original['success']) || !$res->original['success']) {
// Log error // Check if response is JsonResponse and decode it
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]); if ($res instanceof \Illuminate\Http\JsonResponse) {
return false; $decodedResponse = json_decode($res->getContent(), true);
// Handle invalid token case
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
Log::warning("Token is invalid, refreshing token...");
// Regenerate the token
$initResToken = $this->getToken();
// Check if the new token is valid
if (!empty($initResToken->original['data']['token']['access'])) {
$new_token = $initResToken->original['data']['token']['access'];
// **Fix: Update headers with the new token**
$headers['Authorization'] = "Bearer " . $new_token;
Log::info("Token refreshed successfully, retrying API request...");
continue; // Retry the request with the new token
} else {
Log::error("Failed to refresh token");
return $this->resError("Failed to refresh token");
}
}
}
// If request succeeds, break out of retry loop
break;
} }
$data = $res->original['data']['data'] ?? []; // Ensure response is valid before accessing properties
$responseData = $res->original ?? [];
$data = $responseData['data']['data'] ?? [];
if (empty($data)) { if (empty($data)) {
Log::error("No data returned from API", ['url' => $url, 'uuid' => $uuid]); Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid, 'response' => $responseData]);
return false; return false;
} }
@@ -489,6 +564,58 @@ class ServiceSIMBG
throw $e; 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, // Assuming this is a foreign key
'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' => json_encode($data['file']), // Store as JSON if it's an array
'expertise' => $data['expertise'],
'experience' => $data['experience'],
'is_verif' => $data['is_verif'],
'uid' => $data['uid'], // Unique identifier
'status' => $data['status'],
'status_name' => $data['status_name'],
'note' => $data['note'],
'created_at' => now(),
'updated_at' => now(),
];
}
TaskAssignment::upsert(
$task_assignments, // Data to insert/update
['uid'], // Unique key for conflict resolution
['name', 'username', 'email', 'phone_number', 'role', 'role_name', 'is_active', 'file', 'expertise', 'experience', 'is_verif', 'status', 'status_name', 'note', 'updated_at']
);
return true;
}catch(Exception $e){
Log::error("Failed to sync task assignments", ['error' => $e->getMessage()]);
throw $e;
}
}
protected function convertToDecimal(?string $value): ?float protected function convertToDecimal(?string $value): ?float
{ {
if (empty($value)) { if (empty($value)) {
@@ -522,8 +649,10 @@ class ServiceSIMBG
return null; return null;
} }
$cleaned = str_replace('.','', $value);
// Otherwise, cast to integer // Otherwise, cast to integer
return (int) $value; return (int) $cleaned;
} }
protected function convertToDate($dateString) protected function convertToDate($dateString)

View File

@@ -57,7 +57,7 @@
], ],
"dev": [ "dev": [
"Composer\\Config::disableProcessTimeout", "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": { "extra": {

View File

@@ -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');
}
};

View File

@@ -109,6 +109,7 @@ class BigdataResume {
}, },
total: (data) => data.total, total: (data) => data.total,
}, },
width: "auto",
}).render(tableContainer); }).render(tableContainer);
} }
async handleDelete(deleteButton) { async handleDelete(deleteButton) {

View File

@@ -2,6 +2,79 @@
@section('css') @section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) @vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
<style>
/* Ensure the Grid.js container allows full scrolling */
#table-bigdata-resumes {
overflow-x: auto;
overflow-y: hidden; /* Prevent vertical scrolling */
white-space: nowrap;
max-width: 100%;
height: 100%; /* Adjust height if necessary */
}
/* Ensure Grid.js wrapper is scrollable */
.gridjs-wrapper {
max-width: 100%;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
}
/* Make the entire scrollbar much bigger */
.gridjs-wrapper::-webkit-scrollbar {
height: 40px; /* Increase scrollbar height */
}
/* Scrollbar track (background) */
.gridjs-wrapper::-webkit-scrollbar-track {
background: #ddd;
border-radius: 20px;
}
/* Scrollbar thumb (draggable part) */
.gridjs-wrapper::-webkit-scrollbar-thumb {
background: #007bff;
border-radius: 20px;
width: 40px; /* Wider scrollbar thumb */
min-width: 40px;
}
/* Scrollbar thumb hover effect */
.gridjs-wrapper::-webkit-scrollbar-thumb:hover {
background: #0056b3;
}
/* Bigger Scrollbar Buttons */
.gridjs-wrapper::-webkit-scrollbar-button {
background: #007bff;
height: 40px; /* Force bigger button height */
width: 40px; /* Force bigger button width */
border-radius: 10px;
}
/* Left Scroll Button */
.gridjs-wrapper::-webkit-scrollbar-button:horizontal:decrement {
display: block;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M15 18l-6-6 6-6"/></svg>') no-repeat center;
background-size: 30px;
width: 40px; /* Ensure button size */
height: 40px;
}
/* Right Scroll Button */
.gridjs-wrapper::-webkit-scrollbar-button:horizontal:increment {
display: block;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M9 18l6-6-6-6"/></svg>') no-repeat center;
background-size: 30px;
width: 40px; /* Ensure button size */
height: 40px;
}
/* Scrollbar button hover effect */
.gridjs-wrapper::-webkit-scrollbar-button:hover {
background: #0056b3;
}
</style>
@endsection @endsection
@section('content') @section('content')
@@ -11,13 +84,13 @@
<x-toast-notification /> <x-toast-notification />
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card w-100"> <div class="card w-100 h-100">
<div class="card-body"> <div class="card-body">
<div id="table-bigdata-resumes"></div> <div id="table-bigdata-resumes"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@endsection @endsection

View File

@@ -92,6 +92,11 @@
<span class="d-none d-sm-block">PBG Task Prasarana</span> <span class="d-none d-sm-block">PBG Task Prasarana</span>
</a> </a>
</li> </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> </ul>
</div> </div>
<div class="card-body"> <div class="card-body">
@@ -246,6 +251,40 @@
@endif @endif
</div> </div>
</div> </div>
<div class="tab-pane" id="pbgTaskAssignments">
@if ($data->taskAssignments && $data->taskAssignments->isNotEmpty())
@foreach ($data->taskAssignments as $task_assignment)
<div class="border p-3 rounded shadow-sm col-md-4">
<div class="mb-3">
<dt>Nama</dt>
<dd>{{$task_assignment->name}}</dd>
</div>
<div class="mb-3">
<dt>Email</dt>
<dd>{{$task_assignment->email}}</dd>
</div>
<div class="mb-3">
<dt>Nomor Telepon</dt>
<dd>{{$task_assignment->phone_number}}</dd>
</div>
<div class="mb-3">
<dt>Keahlian</dt>
<dd>{{$task_assignment->expertise}}</dd>
</div>
<div class="mb-3">
<dt>Status</dt>
<dd>{{$task_assignment->status_name}}</dd>
</div>
</div>
@endforeach
@else
<div class="row">
<div class="col-md-12">
Data Not Available
</div>
</div>
@endif
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -102,6 +102,7 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
Route::get('/get-user-token', [SyncronizeController::class, 'getUserToken'])->name('api.task.token'); 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('/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-submit/{uuid}', [SyncronizeController::class, 'syncTaskDetailSubmit'])->name('api.task.submit');
Route::get('/sync-task-assignments/{uuid}', [SyncronizeController::class, 'syncTaskAssignments'])->name('api.task.assignments');
// menus api // menus api
Route::controller(MenusController::class)->group(function (){ Route::controller(MenusController::class)->group(function (){