Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fd1b33eea | |||
| fae6ef6c71 | |||
| f7a6c5a12d | |||
| 97930ed2bc | |||
| 99f5182503 | |||
| cb0574a8ef | |||
| 7f4e6d1b20 | |||
| 79a568a008 | |||
| ca4dbf0486 | |||
| f8c3816ca7 | |||
| 353efd16b0 | |||
| 798455de16 | |||
| 0ee7b75460 | |||
| a0be3a4c74 | |||
| c750177fee |
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\FileDirectory;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
@ -150,90 +149,6 @@ class AkreditasiInstrumenController extends Controller
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function hasFilesInPath(string $folderPath): bool
|
|
||||||
{
|
|
||||||
$escapedPath = addcslashes($folderPath, '\\%_');
|
|
||||||
|
|
||||||
return FileDirectory::query()
|
|
||||||
->where('statusenabled', true)
|
|
||||||
->where('is_akre', true)
|
|
||||||
->where('file', 'ILIKE', $escapedPath . '/%')
|
|
||||||
->exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeNodeByPath(array &$data, string $folderPath): bool
|
|
||||||
{
|
|
||||||
$parts = array_values(array_filter(explode('/', $folderPath), fn ($value) => trim((string) $value) !== ''));
|
|
||||||
$depth = count($parts);
|
|
||||||
if ($depth === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($depth === 1) {
|
|
||||||
foreach ($data as $index => $type) {
|
|
||||||
if (($type['name'] ?? null) !== $parts[0]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$segments = $type['segment'] ?? [];
|
|
||||||
if (is_array($segments) && count($segments) > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
array_splice($data, $index, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($data as $typeIndex => $type) {
|
|
||||||
if (($type['name'] ?? null) !== $parts[0]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$segments = $data[$typeIndex]['segment'] ?? [];
|
|
||||||
if (!is_array($segments)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($segments as $segmentIndex => $segment) {
|
|
||||||
if (($segment['name'] ?? null) !== $parts[1]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($depth === 2) {
|
|
||||||
$children = $segment['turunan'] ?? [];
|
|
||||||
if (is_array($children) && count($children) > 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
array_splice($segments, $segmentIndex, 1);
|
|
||||||
$data[$typeIndex]['segment'] = array_values($segments);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$children = $segments[$segmentIndex]['turunan'] ?? [];
|
|
||||||
if (!is_array($children)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($children as $childIndex => $child) {
|
|
||||||
if (($child['name'] ?? null) !== $parts[2]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
array_splice($children, $childIndex, 1);
|
|
||||||
$segments[$segmentIndex]['turunan'] = array_values($children);
|
|
||||||
$data[$typeIndex]['segment'] = array_values($segments);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read akreditasi.jff (array)
|
* Read akreditasi.jff (array)
|
||||||
*/
|
*/
|
||||||
@ -398,97 +313,4 @@ class AkreditasiInstrumenController extends Controller
|
|||||||
@fclose($lockFp);
|
@fclose($lockFp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Request $request)
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'path' => ['required', 'string', 'max:2000'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$folderPath = trim((string) ($validated['path'] ?? ''));
|
|
||||||
if ($folderPath === '') {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Path folder wajib diisi.',
|
|
||||||
], Response::HTTP_UNPROCESSABLE_ENTITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->hasFilesInPath($folderPath)) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Folder tidak bisa dihapus karena masih memiliki file di dalamnya.',
|
|
||||||
], Response::HTTP_UNPROCESSABLE_ENTITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->ensureJsonDirExists();
|
|
||||||
$path = $this->akreditasiJsonPath();
|
|
||||||
$lockPath = $this->akreditasiLockPath();
|
|
||||||
|
|
||||||
$lockFp = @fopen($lockPath, 'c+');
|
|
||||||
if (!$lockFp) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Gagal membuka file lock untuk akreditasi.jff.',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!flock($lockFp, LOCK_EX)) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Gagal mengunci file akreditasi.jff.',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
$raw = @file_get_contents($path);
|
|
||||||
if ($raw === false) {
|
|
||||||
$data = [];
|
|
||||||
} else {
|
|
||||||
$data = $this->decodeJsonArray($raw, true);
|
|
||||||
if ($data === null) {
|
|
||||||
$repaired = $this->tryRepairJsonArray($raw);
|
|
||||||
if ($repaired === null) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'File public/json/akreditasi.jff tidak valid (JSON rusak).',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
$data = $repaired;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$removed = $this->removeNodeByPath($data, $folderPath);
|
|
||||||
if (!$removed) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Folder tidak ditemukan atau masih memiliki turunan.',
|
|
||||||
], Response::HTTP_UNPROCESSABLE_ENTITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
||||||
if ($json === false) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Gagal mengubah data menjadi JSON.',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
$json .= "\n";
|
|
||||||
|
|
||||||
if (!$this->atomicWrite($path, $json)) {
|
|
||||||
return response()->json([
|
|
||||||
'status' => false,
|
|
||||||
'message' => 'Gagal menulis file akreditasi.jff (atomic write).',
|
|
||||||
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'status' => true,
|
|
||||||
'message' => 'Folder akreditasi berhasil dihapus.',
|
|
||||||
'data' => $data,
|
|
||||||
]);
|
|
||||||
} finally {
|
|
||||||
@flock($lockFp, LOCK_UN);
|
|
||||||
@fclose($lockFp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,6 @@ class EnsureMasterPersetujuan
|
|||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
$hasAccess = $user && $user->masterPersetujuan;
|
$hasAccess = $user && $user->masterPersetujuan;
|
||||||
|
|
||||||
if (!$hasAccess) {
|
if (!$hasAccess) {
|
||||||
abort(403, 'Tidak memiliki akses.');
|
abort(403, 'Tidak memiliki akses.');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,9 @@ class SubUnitKerja extends Model
|
|||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
protected $primaryKey = 'id';
|
protected $primaryKey = 'id';
|
||||||
protected $guarded = ['id'];
|
protected $guarded = ['id'];
|
||||||
protected $with = ['fileDirectory'];
|
// protected $with = ['fileDirectory'];
|
||||||
|
|
||||||
public function fileDirectory(){
|
// public function fileDirectory(){
|
||||||
return $this->hasMany(FileDirectory::class, 'id_sub_unit_kerja', 'id')->where('statusenabled', true);
|
// return $this->hasMany(FileDirectory::class, 'id_sub_unit_kerja', 'id')->where('statusenabled', true);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,10 +11,10 @@ class UnitKerja extends Model
|
|||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
protected $primaryKey = 'id';
|
protected $primaryKey = 'id';
|
||||||
protected $guarded = ['id'];
|
protected $guarded = ['id'];
|
||||||
protected $with = ['subUnitKerja'];
|
// protected $with = ['subUnitKerja'];
|
||||||
|
|
||||||
public function subUnitKerja(){
|
// public function subUnitKerja(){
|
||||||
return $this->hasMany(SubUnitKerja::class, 'objectunitkerjapegawaifk', 'id')->where('statusenabled', true)->select('id', 'objectunitkerjapegawaifk', 'name');
|
// return $this->hasMany(SubUnitKerja::class, 'objectunitkerjapegawaifk', 'id')->where('statusenabled', true)->select('id', 'objectunitkerjapegawaifk', 'name');
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class User extends Authenticatable
|
|||||||
'katasandi' => 'hashed',
|
'katasandi' => 'hashed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
protected $with = ['dataUser', 'masterPersetujuan', 'akses'];
|
protected $with = ['dataUser'];
|
||||||
public function dataUser(){
|
public function dataUser(){
|
||||||
return $this->belongsTo(DataUser::class, 'objectpegawaifk', 'id')->select('id', 'namalengkap');
|
return $this->belongsTo(DataUser::class, 'objectpegawaifk', 'id')->select('id', 'namalengkap');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Foundation\Configuration\Exceptions;
|
use Illuminate\Foundation\Configuration\Exceptions;
|
||||||
use Illuminate\Foundation\Configuration\Middleware;
|
use Illuminate\Foundation\Configuration\Middleware;
|
||||||
|
use Illuminate\Session\TokenMismatchException;
|
||||||
|
|
||||||
return Application::configure(basePath: dirname(__DIR__))
|
return Application::configure(basePath: dirname(__DIR__))
|
||||||
->withRouting(
|
->withRouting(
|
||||||
@ -17,5 +19,24 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
//
|
$exceptions->render(function (TokenMismatchException $exception, Request $request) {
|
||||||
|
$message = 'Sesi halaman sudah habis. Silakan login ulang.';
|
||||||
|
|
||||||
|
if ($request->is('login')) {
|
||||||
|
return redirect()
|
||||||
|
->route('login')
|
||||||
|
->withInput($request->only('namauser'))
|
||||||
|
->with('alertError', 'expired');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$request->expectsJson()) {
|
||||||
|
return redirect()
|
||||||
|
->guest(route('login'))
|
||||||
|
->with('alertError', 'expired-session');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => $message,
|
||||||
|
], 419);
|
||||||
|
});
|
||||||
})->create();
|
})->create();
|
||||||
|
|||||||
78
composer.lock
generated
78
composer.lock
generated
@ -62,16 +62,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.384.2",
|
"version": "3.383.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "a6c85b3a1f5f8327076291040329214b3a2caf2b"
|
"reference": "11c2de39e4511dc99e44f049c7dfc8087e051867"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a6c85b3a1f5f8327076291040329214b3a2caf2b",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/11c2de39e4511dc99e44f049c7dfc8087e051867",
|
||||||
"reference": "a6c85b3a1f5f8327076291040329214b3a2caf2b",
|
"reference": "11c2de39e4511dc99e44f049c7dfc8087e051867",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -153,9 +153,9 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.384.2"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.383.2"
|
||||||
},
|
},
|
||||||
"time": "2026-06-03T18:07:33+00:00"
|
"time": "2026-06-01T18:08:21+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@ -873,26 +873,25 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/guzzle",
|
"name": "guzzlehttp/guzzle",
|
||||||
"version": "7.11.0",
|
"version": "7.10.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/guzzle.git",
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
"reference": "c987f8ce84b8434fa430795eca0f3430663da72b"
|
"reference": "e7412b3180912c01650cc66647f18c1d1cbe9b94"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/c987f8ce84b8434fa430795eca0f3430663da72b",
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/e7412b3180912c01650cc66647f18c1d1cbe9b94",
|
||||||
"reference": "c987f8ce84b8434fa430795eca0f3430663da72b",
|
"reference": "e7412b3180912c01650cc66647f18c1d1cbe9b94",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"guzzlehttp/promises": "^2.5",
|
"guzzlehttp/promises": "^2.3",
|
||||||
"guzzlehttp/psr7": "^2.11",
|
"guzzlehttp/psr7": "^2.8",
|
||||||
"php": "^7.2.5 || ^8.0",
|
"php": "^7.2.5 || ^8.0",
|
||||||
"psr/http-client": "^1.0",
|
"psr/http-client": "^1.0",
|
||||||
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||||
"symfony/polyfill-php80": "^1.24"
|
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/http-client-implementation": "1.0"
|
"psr/http-client-implementation": "1.0"
|
||||||
@ -981,7 +980,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||||
"source": "https://github.com/guzzle/guzzle/tree/7.11.0"
|
"source": "https://github.com/guzzle/guzzle/tree/7.10.6"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -997,25 +996,24 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-06-02T12:40:51+00:00"
|
"time": "2026-06-01T13:06:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/promises",
|
"name": "guzzlehttp/promises",
|
||||||
"version": "2.5.0",
|
"version": "2.4.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/promises.git",
|
"url": "https://github.com/guzzle/promises.git",
|
||||||
"reference": "4360e982f87f5f258bf872d094647791db2f4c8e"
|
"reference": "09e8a212562fb1fb6a512c4156ed71525969d6c2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e",
|
"url": "https://api.github.com/repos/guzzle/promises/zipball/09e8a212562fb1fb6a512c4156ed71525969d6c2",
|
||||||
"reference": "4360e982f87f5f258bf872d094647791db2f4c8e",
|
"reference": "09e8a212562fb1fb6a512c4156ed71525969d6c2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2.5 || ^8.0",
|
"php": "^7.2.5 || ^8.0"
|
||||||
"symfony/deprecation-contracts": "^2.5 || ^3.0"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
@ -1065,7 +1063,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/promises/issues",
|
"issues": "https://github.com/guzzle/promises/issues",
|
||||||
"source": "https://github.com/guzzle/promises/tree/2.5.0"
|
"source": "https://github.com/guzzle/promises/tree/2.4.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1081,29 +1079,27 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-06-02T12:23:43+00:00"
|
"time": "2026-05-20T22:57:30+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/psr7",
|
"name": "guzzlehttp/psr7",
|
||||||
"version": "2.11.0",
|
"version": "2.10.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/psr7.git",
|
"url": "https://github.com/guzzle/psr7.git",
|
||||||
"reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f"
|
"reference": "d2a1a094e396da8957e797489fddaf860c340cfc"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/bbb5e61349fa5cb822b3e87842b951088b76b81f",
|
"url": "https://api.github.com/repos/guzzle/psr7/zipball/d2a1a094e396da8957e797489fddaf860c340cfc",
|
||||||
"reference": "bbb5e61349fa5cb822b3e87842b951088b76b81f",
|
"reference": "d2a1a094e396da8957e797489fddaf860c340cfc",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2.5 || ^8.0",
|
"php": "^7.2.5 || ^8.0",
|
||||||
"psr/http-factory": "^1.0",
|
"psr/http-factory": "^1.0",
|
||||||
"psr/http-message": "^1.1 || ^2.0",
|
"psr/http-message": "^1.1 || ^2.0",
|
||||||
"ralouphie/getallheaders": "^3.0",
|
"ralouphie/getallheaders": "^3.0"
|
||||||
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
|
||||||
"symfony/polyfill-php80": "^1.24"
|
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/http-factory-implementation": "1.0",
|
"psr/http-factory-implementation": "1.0",
|
||||||
@ -1184,7 +1180,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/psr7/issues",
|
"issues": "https://github.com/guzzle/psr7/issues",
|
||||||
"source": "https://github.com/guzzle/psr7/tree/2.11.0"
|
"source": "https://github.com/guzzle/psr7/tree/2.10.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1200,7 +1196,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-06-02T12:30:48+00:00"
|
"time": "2026-05-29T12:59:07+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/uri-template",
|
"name": "guzzlehttp/uri-template",
|
||||||
@ -7193,16 +7189,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/sail",
|
"name": "laravel/sail",
|
||||||
"version": "v1.62.0",
|
"version": "v1.61.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laravel/sail.git",
|
"url": "https://github.com/laravel/sail.git",
|
||||||
"reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e"
|
"reference": "68ef35015630fe510432e63e11e21749006df688"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laravel/sail/zipball/3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e",
|
"url": "https://api.github.com/repos/laravel/sail/zipball/68ef35015630fe510432e63e11e21749006df688",
|
||||||
"reference": "3aaeefc979f8ba6586fbc5b6e0b1b3638058f98e",
|
"reference": "68ef35015630fe510432e63e11e21749006df688",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -7252,7 +7248,7 @@
|
|||||||
"issues": "https://github.com/laravel/sail/issues",
|
"issues": "https://github.com/laravel/sail/issues",
|
||||||
"source": "https://github.com/laravel/sail"
|
"source": "https://github.com/laravel/sail"
|
||||||
},
|
},
|
||||||
"time": "2026-05-27T04:02:01+00:00"
|
"time": "2026-05-23T23:33:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mockery/mockery",
|
"name": "mockery/mockery",
|
||||||
@ -9235,12 +9231,12 @@
|
|||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"stability-flags": {},
|
"stability-flags": [],
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.2"
|
"php": "^8.2"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.9.0"
|
"plugin-api-version": "2.2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
8
public/assets/css/styles.min.css
vendored
8
public/assets/css/styles.min.css
vendored
@ -15904,14 +15904,14 @@ body {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
.body-wrapper .body-wrapper-inner {
|
.body-wrapper .body-wrapper-inner {
|
||||||
min-height: calc(100vh - 110px);
|
min-height: calc(130vh - 180px);
|
||||||
}
|
}
|
||||||
.body-wrapper .container-fluid, .body-wrapper .container-sm, .body-wrapper .container-md, .body-wrapper .container-lg, .body-wrapper .container-xl, .body-wrapper .container-xxl {
|
.body-wrapper .container-fluid, .body-wrapper .container-sm, .body-wrapper .container-md, .body-wrapper .container-lg, .body-wrapper .container-xl, .body-wrapper .container-xxl {
|
||||||
max-width: 1300px;
|
max-width: 1600px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 24px;
|
padding: 10px;
|
||||||
transition: 0.2s ease-in;
|
transition: 0.2s ease-in;
|
||||||
padding-top: 120px;
|
padding-top: 90px;
|
||||||
}
|
}
|
||||||
@media (max-width: 991.98px) {
|
@media (max-width: 991.98px) {
|
||||||
.body-wrapper .container-fluid, .body-wrapper .container-sm, .body-wrapper .container-md, .body-wrapper .container-lg, .body-wrapper .container-xl, .body-wrapper .container-xxl {
|
.body-wrapper .container-fluid, .body-wrapper .container-sm, .body-wrapper .container-md, .body-wrapper .container-lg, .body-wrapper .container-xl, .body-wrapper .container-xxl {
|
||||||
|
|||||||
@ -19,6 +19,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const titleEl = document.getElementById('pendingTitle');
|
const titleEl = document.getElementById('pendingTitle');
|
||||||
const tabPendingEl = document.getElementById('tabPengajuan');
|
const tabPendingEl = document.getElementById('tabPengajuan');
|
||||||
const tabHistoryEl = document.getElementById('tabHistory');
|
const tabHistoryEl = document.getElementById('tabHistory');
|
||||||
|
const isKomiteMutu = !!window.isKomiteMutu;
|
||||||
|
const isTurt = !!window.isTurt;
|
||||||
|
|
||||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||||
const selectedIds = new Set();
|
const selectedIds = new Set();
|
||||||
@ -63,10 +65,54 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusBadge(status){
|
function approvalBadge(status, label){
|
||||||
if (status === 'rejected') return '<span class="badge bg-danger">Rejected</span>';
|
const normalized = String(status || '').toLowerCase();
|
||||||
if (status === 'revised') return '<span class="badge bg-info">Revised</span>';
|
if (normalized === 'approved') return `<span class="badge bg-success">Approved ${label}</span>`;
|
||||||
return '<span class="badge bg-warning text-dark">Pending</span>';
|
if (normalized.startsWith('rejected')) return `<span class="badge bg-danger">Rejected ${label}</span>`;
|
||||||
|
if (normalized.startsWith('revised')) return `<span class="badge bg-info">Revised ${label}</span>`;
|
||||||
|
return `<span class="badge bg-warning text-dark">Pending ${label}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWorkflowStatusBadges(item){
|
||||||
|
const actionStatus = String(item?.status_action || '').toLowerCase();
|
||||||
|
const mutuStatus = String(item?.status_mutu || '').toLowerCase();
|
||||||
|
const turtStatus = String(item?.status_turt || '').toLowerCase();
|
||||||
|
|
||||||
|
if (actionStatus.startsWith('revised')) {
|
||||||
|
return [`<span class="badge bg-info">Revised</span>`];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionStatus.startsWith('rejected')) {
|
||||||
|
return [`<span class="badge bg-danger">Rejected Atasan</span>`];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionStatus !== 'approved') {
|
||||||
|
return [`<span class="badge bg-warning text-dark">Pending Atasan</span>`];
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses = [];
|
||||||
|
|
||||||
|
if (item?.is_akre) {
|
||||||
|
if (mutuStatus.startsWith('rejected')) {
|
||||||
|
statuses.push(`<span class="badge bg-danger">Rejected Mutu</span>`);
|
||||||
|
} else if (mutuStatus !== 'approved') {
|
||||||
|
statuses.push(`<span class="badge bg-warning text-dark">Pending Mutu</span>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item?.master_kategori_directory_id) {
|
||||||
|
if (turtStatus.startsWith('rejected')) {
|
||||||
|
statuses.push(`<span class="badge bg-danger">Rejected TURT</span>`);
|
||||||
|
} else if (turtStatus !== 'approved') {
|
||||||
|
statuses.push(`<span class="badge bg-warning text-dark">Pending TURT</span>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statuses.length > 0) {
|
||||||
|
return statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [`<span class="badge bg-success">Approved</span>`];
|
||||||
}
|
}
|
||||||
|
|
||||||
function aksesBadge(akses){
|
function aksesBadge(akses){
|
||||||
@ -81,12 +127,87 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isRejected(item){
|
function isRejected(item){
|
||||||
return item?.status_action === 'rejected';
|
return String(item?.status_action || '').toLowerCase().startsWith('rejected')
|
||||||
|
|| String(item?.status_mutu || '').toLowerCase().startsWith('rejected')
|
||||||
|
|| String(item?.status_turt || '').toLowerCase().startsWith('rejected');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAtasanApproved(item){
|
||||||
|
return String(item?.status_action || '').toLowerCase() === 'approved';
|
||||||
|
}
|
||||||
|
|
||||||
|
function canProcessRoleStatus(status){
|
||||||
|
const normalized = String(status || '').toLowerCase();
|
||||||
|
return normalized === '' || normalized.startsWith('revised');
|
||||||
|
}
|
||||||
|
|
||||||
|
function canApproveAsAtasan(item){
|
||||||
|
return item?.can_approve_atasan === true || item?.can_approve_atasan === 1 || item?.can_approve_atasan === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
function canEditSubmission(item){
|
||||||
|
return item?.can_edit_submission === true || item?.can_edit_submission === 1 || item?.can_edit_submission === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasRevisionInfo(item){
|
||||||
|
return !!(item?.revision || item?.mutu_revision || item?.turt_revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectRevisionNotes(item){
|
||||||
|
const notes = [];
|
||||||
|
const genericRevision = String(item?.revision || '').trim();
|
||||||
|
const mutuRevision = String(item?.mutu_revision || '').trim();
|
||||||
|
const turtRevision = String(item?.turt_revision || '').trim();
|
||||||
|
|
||||||
|
if (genericRevision) {
|
||||||
|
const lowerGeneric = genericRevision.toLowerCase();
|
||||||
|
if (!lowerGeneric.startsWith('komite mutu:') && !lowerGeneric.startsWith('turt:')) {
|
||||||
|
notes.push({ label: 'Atasan', text: genericRevision });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mutuRevision) {
|
||||||
|
notes.push({ label: 'Komite Mutu', text: mutuRevision });
|
||||||
|
}
|
||||||
|
if (turtRevision) {
|
||||||
|
notes.push({ label: 'TURT', text: turtRevision });
|
||||||
|
}
|
||||||
|
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value){
|
||||||
|
return String(value ?? '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRevisionNotesHtml(notes){
|
||||||
|
if (!notes.length) {
|
||||||
|
return '<div class="text-muted">Tidak ada catatan revisi.</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="text-start" style="line-height:1.6;">
|
||||||
|
${notes.map(note => `
|
||||||
|
<div class="mb-2">
|
||||||
|
<div><strong>${escapeHtml(note.label)}</strong></div>
|
||||||
|
<div>${escapeHtml(note.text)}</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemById(id){
|
||||||
|
return (tableState.data || []).find((row) => String(row.file_directory_id) === String(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectableIdsOnPage(){
|
function getSelectableIdsOnPage(){
|
||||||
return (tableState.data || [])
|
return (tableState.data || [])
|
||||||
.filter((item) => !isRejected(item))
|
.filter((item) => !isRejected(item) && !isAtasanApproved(item) && canApproveAsAtasan(item))
|
||||||
.map((item) => String(item.file_directory_id));
|
.map((item) => String(item.file_directory_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +235,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
function updateSelectionUI(){
|
function updateSelectionUI(){
|
||||||
const count = selectedIds.size;
|
const count = selectedIds.size;
|
||||||
|
const selectableIds = getSelectableIdsOnPage();
|
||||||
|
const canBulkApprove = tableState.mode !== 'history' && selectableIds.length > 0;
|
||||||
if (selectedCountEl) selectedCountEl.textContent = String(count);
|
if (selectedCountEl) selectedCountEl.textContent = String(count);
|
||||||
if (bulkApproveBtn) bulkApproveBtn.disabled = count === 0 || tableState.mode === 'history';
|
if (bulkApproveBtn) bulkApproveBtn.disabled = count === 0 || !canBulkApprove;
|
||||||
if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || tableState.mode === 'history';
|
if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || !canBulkApprove;
|
||||||
updateSelectAllState();
|
updateSelectAllState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,26 +249,60 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-';
|
const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-';
|
||||||
const id = String(item.file_directory_id);
|
const id = String(item.file_directory_id);
|
||||||
const rejected = isRejected(item);
|
const rejected = isRejected(item);
|
||||||
|
const atasanApproved = isAtasanApproved(item);
|
||||||
|
const canAtasanApprove = canApproveAsAtasan(item);
|
||||||
|
const atasanRejected = String(item?.status_action || '').toLowerCase().startsWith('rejected');
|
||||||
|
const canMutuAct = atasanApproved && isKomiteMutu && item.is_akre && canProcessRoleStatus(item.status_mutu);
|
||||||
|
const canTurtAct = atasanApproved && isTurt && item.master_kategori_directory_id && canProcessRoleStatus(item.status_turt);
|
||||||
const checked = selectedIds.has(id);
|
const checked = selectedIds.has(id);
|
||||||
const aksi = `
|
const actions = [
|
||||||
<div class="d-flex gap-1">
|
`<button class="btn btn-sm btn-primary" onclick="infoDok(this)"
|
||||||
<button class="btn btn-sm btn-primary" onclick="infoDok(this)"
|
data-file="${item.file}"
|
||||||
data-file="${item.file}"
|
data-fileName="${item.nama_dokumen}"
|
||||||
data-fileName="${item.nama_dokumen}"
|
data-id="${item.file_directory_id}"
|
||||||
data-id="${item.file_directory_id}"
|
data-no_dokumen="${item.no_dokumen || '-'}"
|
||||||
data-no_dokumen="${item.no_dokumen || '-'}"
|
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
||||||
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
data-permission_file="${item.permission_file || '-'}">
|
||||||
data-permission_file="${item.permission_file || '-'}">
|
<i class="fa-solid fa-eye"></i>
|
||||||
<i class="fa-solid fa-eye"></i>
|
</button>`
|
||||||
</button>
|
];
|
||||||
<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')">
|
|
||||||
<i class="fa-solid fa-check"></i>
|
if (isKomiteMutu || isTurt) {
|
||||||
</button>
|
if (canAtasanApprove && !atasanApproved) {
|
||||||
<button class="btn btn-sm btn-danger" onclick="rejectPending('${item.file_directory_id}', '${item.nama_dokumen || ''}')">
|
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
|
||||||
<i class="fa-solid fa-xmark"></i>
|
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
|
||||||
</button>
|
}
|
||||||
</div>
|
if (canMutuAct) {
|
||||||
`;
|
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
|
||||||
|
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPendingRole('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
|
||||||
|
}
|
||||||
|
if (canTurtAct) {
|
||||||
|
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
|
||||||
|
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-ban"></i></button>`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!atasanApproved) {
|
||||||
|
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
|
||||||
|
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rejected && atasanRejected && !canMutuAct && !canTurtAct) {
|
||||||
|
actions.length = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rejected) {
|
||||||
|
if (hasRevisionInfo(item)) {
|
||||||
|
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectPending('${item.file_directory_id}')"><i class="fa-solid fa-circle-info"></i></button>`);
|
||||||
|
}
|
||||||
|
if (canEditSubmission(item)) {
|
||||||
|
actions.push(`<button class="btn btn-sm btn-primary" onclick="editRejectedFromPending('${item.file_directory_id}')"><i class="fa-solid fa-pen-to-square"></i></button>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusContent = (isKomiteMutu || isTurt)
|
||||||
|
? getWorkflowStatusBadges(item).join(' ')
|
||||||
|
: getWorkflowStatusBadges(item).join(' ');
|
||||||
return `
|
return `
|
||||||
<tr class="${checked ? 'table-active' : ''}">
|
<tr class="${checked ? 'table-active' : ''}">
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
@ -153,11 +310,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
class="form-check-input row-select"
|
class="form-check-input row-select"
|
||||||
data-id="${id}"
|
data-id="${id}"
|
||||||
${checked ? 'checked' : ''}
|
${checked ? 'checked' : ''}
|
||||||
${rejected ? 'disabled' : ''}>
|
${(rejected || atasanApproved || !canAtasanApprove) ? 'disabled' : ''}>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">${rejected ? '' : aksi}</td>
|
<td class="text-center text-nowrap"><div class="d-flex justify-content-center align-items-center gap-1 flex-nowrap">${actions.join('')}</div></td>
|
||||||
<td>${safeText(item.no_dokumen)}</td>
|
<td>${safeText(item.no_dokumen)}</td>
|
||||||
<td>${statusBadge(item?.status_action)}</td>
|
<td>${statusContent}</td>
|
||||||
<td>${aksesBadge(item?.permission_file)}</td>
|
<td>${aksesBadge(item?.permission_file)}</td>
|
||||||
<td><a href="#" class="file-link"
|
<td><a href="#" class="file-link"
|
||||||
data-file="${item.file}"
|
data-file="${item.file}"
|
||||||
@ -166,8 +323,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
data-no_dokumen="${item.no_dokumen || '-'}"
|
data-no_dokumen="${item.no_dokumen || '-'}"
|
||||||
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
||||||
data-permission_file="${item.permission_file || '-'}">${item.nama_dokumen}</a></td>
|
data-permission_file="${item.permission_file || '-'}">${item.nama_dokumen}</a></td>
|
||||||
<td class="col-kategori"><div class="cell-wrap">${safeText(item.folder)}</div></td>
|
<td class="col-kategori"><div class="cell-wrap">${safeText(item.kategori)}</div></td>
|
||||||
<td class="col-unit"><div class="cell-wrap">${safeText(item.part)}</div></td>
|
<td class="col-unit"><div class="cell-wrap">${safeText(item.nama_unit)}</div></td>
|
||||||
<td class="text-nowrap">${tanggalTerbit}</td>
|
<td class="text-nowrap">${tanggalTerbit}</td>
|
||||||
<td class="text-nowrap">${tanggalExp}</td>
|
<td class="text-nowrap">${tanggalExp}</td>
|
||||||
<td class="text-nowrap">${tanggal}</td>
|
<td class="text-nowrap">${tanggal}</td>
|
||||||
@ -177,6 +334,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildHistoryRow(item){
|
function buildHistoryRow(item){
|
||||||
|
|
||||||
const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-';
|
const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-';
|
||||||
const tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-';
|
const tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-';
|
||||||
const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-';
|
const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-';
|
||||||
@ -192,11 +350,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
data-no_dokumen="${item.no_dokumen || '-'}"
|
data-no_dokumen="${item.no_dokumen || '-'}"
|
||||||
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
||||||
data-permission_file="${item.permission_file || '-'}">${safeText(item.nama_dokumen)}</a></td>
|
data-permission_file="${item.permission_file || '-'}">${safeText(item.nama_dokumen)}</a></td>
|
||||||
<td>${safeText(item.folder)}</td>
|
<td>${safeText(item.kategori)}</td>
|
||||||
<td>${safeText(item.part)}</td>
|
<td>${safeText(item.nama_unit)}</td>
|
||||||
<td><span class="badge bg-info">${actionType}</span></td>
|
<td><span class="badge bg-info">${actionType}</span></td>
|
||||||
<td class="text-nowrap">${tanggalTerbit}</td>
|
|
||||||
<td class="text-nowrap">${tanggalExp}</td>
|
|
||||||
<td class="text-nowrap">${tanggal}</td>
|
<td class="text-nowrap">${tanggal}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
@ -323,7 +479,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
tabHistoryEl.classList.toggle('d-none', tableState.mode !== 'history');
|
tabHistoryEl.classList.toggle('d-none', tableState.mode !== 'history');
|
||||||
}
|
}
|
||||||
if (pendingBulkActionsEl) {
|
if (pendingBulkActionsEl) {
|
||||||
pendingBulkActionsEl.classList.toggle('d-none', tableState.mode === 'history');
|
pendingBulkActionsEl.classList.toggle('d-none', tableState.mode === 'history' || isKomiteMutu || isTurt);
|
||||||
}
|
}
|
||||||
updateSelectionUI();
|
updateSelectionUI();
|
||||||
}
|
}
|
||||||
@ -404,7 +560,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
showConfirmButton: false
|
showConfirmButton: false
|
||||||
});
|
});
|
||||||
selectedIds.delete(String(id));
|
selectedIds.delete(String(id));
|
||||||
countData();
|
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
|
||||||
fetchData();
|
fetchData();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
@ -461,7 +617,127 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
showConfirmButton: false
|
showConfirmButton: false
|
||||||
});
|
});
|
||||||
selectedIds.delete(String(id));
|
selectedIds.delete(String(id));
|
||||||
countData();
|
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
|
||||||
|
fetchData();
|
||||||
|
}).catch((err) => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Gagal',
|
||||||
|
text: err.message || 'Terjadi kesalahan.'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.infoRejectPending = function(id){
|
||||||
|
const item = getItemById(id);
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Catatan Revisi',
|
||||||
|
html: buildRevisionNotesHtml(collectRevisionNotes(item)),
|
||||||
|
icon: 'info',
|
||||||
|
confirmButtonText: 'Tutup'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.editRejectedFromPending = function(id){
|
||||||
|
const targetUrl = new URL('/pengajuan-file', window.location.origin);
|
||||||
|
targetUrl.searchParams.set('edit', id);
|
||||||
|
window.location.href = targetUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.approvePendingRole = function(type, id, fileName){
|
||||||
|
const endpoint = type === 'mutu'
|
||||||
|
? `/pending-file/${id}/approve-mutu`
|
||||||
|
: `/pending-file/${id}/approve-turt`;
|
||||||
|
const label = type === 'mutu' ? 'Komite Mutu' : 'TURT';
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: `Approve ${label}?`,
|
||||||
|
text: fileName || 'Dokumen akan disetujui.',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Approve',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
}).then((result) => {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
}).then(async(res) => {
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok || !data?.status) {
|
||||||
|
throw new Error(data?.message || 'Gagal approve.');
|
||||||
|
}
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil',
|
||||||
|
text: data.message || 'Dokumen disetujui.',
|
||||||
|
timer: 1500,
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
|
||||||
|
fetchData();
|
||||||
|
}).catch((err) => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Gagal',
|
||||||
|
text: err.message || 'Terjadi kesalahan.'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.rejectPendingRole = function(type, id, fileName){
|
||||||
|
const endpoint = type === 'mutu'
|
||||||
|
? `/pending-file/${id}/reject-mutu`
|
||||||
|
: `/pending-file/${id}/reject-turt`;
|
||||||
|
const label = type === 'mutu' ? 'Komite Mutu' : 'TURT';
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: `Reject ${label}?`,
|
||||||
|
text: fileName || 'Dokumen akan ditolak.',
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Reject',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
input: 'textarea',
|
||||||
|
inputLabel: 'Catatan',
|
||||||
|
inputPlaceholder: 'Tulis alasan atau catatan revisi...',
|
||||||
|
inputAttributes: {
|
||||||
|
'aria-label': 'Catatan'
|
||||||
|
},
|
||||||
|
preConfirm: (value) => {
|
||||||
|
const revision = (value || '').trim();
|
||||||
|
if (!revision) {
|
||||||
|
Swal.showValidationMessage('Catatan revisi wajib diisi.');
|
||||||
|
}
|
||||||
|
return revision;
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': csrfToken,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ revision: result.value })
|
||||||
|
}).then(async(res) => {
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok || !data?.status) {
|
||||||
|
throw new Error(data?.message || 'Gagal reject.');
|
||||||
|
}
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil',
|
||||||
|
text: data.message || 'Dokumen ditolak.',
|
||||||
|
timer: 1500,
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
|
||||||
fetchData();
|
fetchData();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
@ -572,7 +848,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
timer: 1500,
|
timer: 1500,
|
||||||
showConfirmButton: false
|
showConfirmButton: false
|
||||||
});
|
});
|
||||||
countData();
|
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
|
||||||
fetchData();
|
fetchData();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -103,188 +103,6 @@
|
|||||||
"name": "TKRS 6.f"
|
"name": "TKRS 6.f"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.f"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.d"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 8",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 8.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 8.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 8.c"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.e"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.d"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 11",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 11.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 11.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 11.c"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.d"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.f"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 14",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 14.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 14.b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.g"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -103,188 +103,6 @@
|
|||||||
"name": "TKRS 6.f"
|
"name": "TKRS 6.f"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.f"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 7.1.d"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 8",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 8.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 8.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 8.c"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 9.e"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 10.d"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 11",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 11.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 11.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 11.c"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 12.d"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 13.f"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 14",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 14.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 14.b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15",
|
|
||||||
"turunan": [
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TKRS 15.g"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -1480,7 +1298,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PPI 8.c"
|
"name": "PPI 8.c"
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -3360,8 +3178,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "tst",
|
|
||||||
"segment": []
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|||||||
@ -159,7 +159,11 @@
|
|||||||
@csrf
|
@csrf
|
||||||
@if (session()->has('alertError'))
|
@if (session()->has('alertError'))
|
||||||
<div class="alert alert-danger fw-bold" role="alert">
|
<div class="alert alert-danger fw-bold" role="alert">
|
||||||
@if(session('alertError') === 'captcha')
|
@if(session('alertError') === 'expired')
|
||||||
|
Sesi login sudah habis karena halaman terlalu lama terbuka. Silakan coba lagi.
|
||||||
|
@elseif(session('alertError') === 'expired-session')
|
||||||
|
Sesi Anda sudah habis. Silakan login kembali.
|
||||||
|
@elseif(session('alertError') === 'captcha')
|
||||||
Captcha salah!
|
Captcha salah!
|
||||||
@elseif(session('alertError') === 'rate')
|
@elseif(session('alertError') === 'rate')
|
||||||
Terlalu banyak percobaan login. Coba lagi dalam 1 menit.
|
Terlalu banyak percobaan login. Coba lagi dalam 1 menit.
|
||||||
@ -177,7 +181,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="exampleInputEmail1" class="form-label">Username</label>
|
<label for="exampleInputEmail1" class="form-label">Username</label>
|
||||||
<input type="text" name="namauser" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" required>
|
<input type="text" name="namauser" value="{{ old('namauser') }}" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="exampleInputPassword1" class="form-label">Password</label>
|
<label for="exampleInputPassword1" class="form-label">Password</label>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -200,8 +200,8 @@
|
|||||||
<span class="small text-muted">Tampilkan</span>
|
<span class="small text-muted">Tampilkan</span>
|
||||||
<select id="tablePageSize" class="form-select form-select-sm" style="width: 80px;">
|
<select id="tablePageSize" class="form-select form-select-sm" style="width: 80px;">
|
||||||
<option value="5">5</option>
|
<option value="5">5</option>
|
||||||
<option value="10" selected>10</option>
|
<option value="10" >10</option>
|
||||||
<option value="20">20</option>
|
<option value="20" selected>20</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
</select>
|
</select>
|
||||||
@ -226,7 +226,8 @@
|
|||||||
<script>
|
<script>
|
||||||
const katDok = @json($katDok);
|
const katDok = @json($katDok);
|
||||||
const authUnitKerja = @json(auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]?->unitKerja);
|
const authUnitKerja = @json(auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]?->unitKerja);
|
||||||
const authSubUnitKerja = @json(auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]->sub_unit_kerja);
|
|
||||||
|
// const authSubUnitKerja = @json(auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]->sub_unit_kerja);
|
||||||
const mappingUnitKerjaPegawai = @json(auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]);
|
const mappingUnitKerjaPegawai = @json(auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]);
|
||||||
const authPegawai = @json(auth()->user()->objectpegawaifk);
|
const authPegawai = @json(auth()->user()->objectpegawaifk);
|
||||||
const formCreate = $("#formFile")
|
const formCreate = $("#formFile")
|
||||||
@ -407,6 +408,30 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value){
|
||||||
|
return String(value ?? '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDeleteRecommendation(item){
|
||||||
|
const recommendation = item?.delete_recommendation;
|
||||||
|
if (!recommendation?.visible || !recommendation?.note) return '';
|
||||||
|
|
||||||
|
const actorRole = recommendation.actor_role || 'TURT';
|
||||||
|
const actorName = recommendation.actor_name ? ` (${escapeHtml(recommendation.actor_name)})` : '';
|
||||||
|
const note = escapeHtml(recommendation.note);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="mt-1 px-2 py-1 rounded border border-warning-subtle bg-warning-subtle text-warning-emphasis" style="font-size:12px; line-height:1.35;">
|
||||||
|
Dokumen ini direkomendasikan dihapus oleh ${escapeHtml(actorRole)}${actorName}. Catatan: ${note}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function buildRow(item){
|
function buildRow(item){
|
||||||
const parts = (item.file || '').split('/');
|
const parts = (item.file || '').split('/');
|
||||||
const fileName = parts.pop() || '-';
|
const fileName = parts.pop() || '-';
|
||||||
@ -472,6 +497,22 @@
|
|||||||
>
|
>
|
||||||
<i class="fa-solid fa-download"></i>
|
<i class="fa-solid fa-download"></i>
|
||||||
</a>
|
</a>
|
||||||
|
${authUnitKerja?.id === 22 ? `
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning btn-sm"
|
||||||
|
onclick="rekomendasiDelete(this)"
|
||||||
|
title="Rekomendasi Hapus"
|
||||||
|
data-filename="${item.nama_dokumen || '-'}"
|
||||||
|
data-id="${item.file_directory_id}"
|
||||||
|
data-no_dokumen="${item.no_dokumen || '-'}"
|
||||||
|
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
||||||
|
data-permission_file="${item.permission_file || '-'}"
|
||||||
|
data-pegawai_id_entry="${item.pegawai_id_entry || '-'}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-trash-can"></i>
|
||||||
|
</button>
|
||||||
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -503,6 +544,7 @@
|
|||||||
${expiryBadge}
|
${expiryBadge}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
${renderDeleteRecommendation(item)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -1176,5 +1218,67 @@
|
|||||||
}
|
}
|
||||||
$("#previewModal").modal('show')
|
$("#previewModal").modal('show')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rekomendasiDelete(button){
|
||||||
|
const id = $(button).data('id');
|
||||||
|
const fileName = $(button).data('filename') || 'dokumen';
|
||||||
|
const noDokumen = $(button).data('no_dokumen') || '-';
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Rekomendasi hapus dokumen?',
|
||||||
|
text: `${fileName} (${noDokumen})`,
|
||||||
|
icon: 'warning',
|
||||||
|
input: 'textarea',
|
||||||
|
inputLabel: 'Catatan rekomendasi',
|
||||||
|
inputPlaceholder: 'Tulis alasan kenapa dokumen ini direkomendasikan untuk dihapus...',
|
||||||
|
inputAttributes: {
|
||||||
|
'aria-label': 'Catatan rekomendasi'
|
||||||
|
},
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Kirim rekomendasi',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
preConfirm: (value) => {
|
||||||
|
const note = (value || '').trim();
|
||||||
|
if (!note) {
|
||||||
|
Swal.showValidationMessage('Catatan rekomendasi wajib diisi.');
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
|
fetch(`/recommend-delete-file/${id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
note: result.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil',
|
||||||
|
text: data.message || 'Rekomendasi hapus berhasil dikirim.',
|
||||||
|
timer: 1800,
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(data.message || 'Gagal mengirim rekomendasi hapus.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Gagal',
|
||||||
|
text: err.message || 'Terjadi kesalahan.'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@ -198,8 +198,8 @@
|
|||||||
<span class="small text-muted">Tampilkan</span>
|
<span class="small text-muted">Tampilkan</span>
|
||||||
<select id="tablePageSize" class="form-select form-select-sm" style="width: 80px;">
|
<select id="tablePageSize" class="form-select form-select-sm" style="width: 80px;">
|
||||||
<option value="5">5</option>
|
<option value="5">5</option>
|
||||||
<option value="10" selected>10</option>
|
<option value="10">10</option>
|
||||||
<option value="20">20</option>
|
<option value="20" selected>20</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
</select>
|
</select>
|
||||||
@ -262,13 +262,117 @@
|
|||||||
return [value];
|
return [value];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDateForInput(date) {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateDisplay(dateValue) {
|
||||||
|
if (!dateValue) return '';
|
||||||
|
const date = new Date(`${dateValue}T00:00:00`);
|
||||||
|
if (Number.isNaN(date.getTime())) return '';
|
||||||
|
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const year = date.getFullYear();
|
||||||
|
return `${day}/${month}/${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addYearsToDate(dateValue, years) {
|
||||||
|
if (!dateValue || !years) return '';
|
||||||
|
const baseDate = new Date(`${dateValue}T00:00:00`);
|
||||||
|
if (Number.isNaN(baseDate.getTime())) return '';
|
||||||
|
|
||||||
|
const originalDay = baseDate.getDate();
|
||||||
|
baseDate.setFullYear(baseDate.getFullYear() + Number(years));
|
||||||
|
if (baseDate.getDate() !== originalDay) {
|
||||||
|
baseDate.setDate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatDateForInput(baseDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncMasaBerlakuField(selectEl) {
|
||||||
|
if (!selectEl) return;
|
||||||
|
const fieldWrap = document.getElementById(selectEl.dataset.dateTarget);
|
||||||
|
const input = document.getElementById(selectEl.dataset.inputTarget);
|
||||||
|
const preview = document.getElementById(selectEl.dataset.previewTarget);
|
||||||
|
const baseInput = document.getElementById(selectEl.dataset.baseTarget);
|
||||||
|
if (!fieldWrap || !input) return;
|
||||||
|
|
||||||
|
if (selectEl.value === 'custom') {
|
||||||
|
fieldWrap.classList.remove('d-none');
|
||||||
|
input.disabled = false;
|
||||||
|
if (preview) preview.textContent = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldWrap.classList.add('d-none');
|
||||||
|
input.disabled = true;
|
||||||
|
input.value = '';
|
||||||
|
|
||||||
|
if (!preview) return;
|
||||||
|
if (!selectEl.value) {
|
||||||
|
preview.textContent = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseInput || !baseInput.value) {
|
||||||
|
preview.textContent = 'Pilih Tanggal Terbit untuk melihat hasil masa berlaku.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiredDate = addYearsToDate(baseInput.value, selectEl.value);
|
||||||
|
preview.textContent = expiredDate
|
||||||
|
? `Tanggal kedaluwarsa: ${formatDateDisplay(expiredDate)}`
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareExpiryInputs(formEl) {
|
||||||
|
const selects = formEl.querySelectorAll('.masa-berlaku-select');
|
||||||
|
for (const selectEl of selects) {
|
||||||
|
const option = selectEl.value;
|
||||||
|
const dateInput = document.getElementById(selectEl.dataset.inputTarget);
|
||||||
|
const baseInput = document.getElementById(selectEl.dataset.baseTarget);
|
||||||
|
if (!dateInput) continue;
|
||||||
|
|
||||||
|
if (!option) {
|
||||||
|
dateInput.value = '';
|
||||||
|
dateInput.disabled = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option === 'custom') {
|
||||||
|
if (!dateInput.value) {
|
||||||
|
return 'Tanggal kedaluwarsa wajib diisi saat memilih opsi Lainnya.';
|
||||||
|
}
|
||||||
|
dateInput.disabled = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseInput || !baseInput.value) {
|
||||||
|
return 'Tanggal Terbit wajib diisi saat memilih masa berlaku 1, 2, atau 3 tahun.';
|
||||||
|
}
|
||||||
|
|
||||||
|
dateInput.value = addYearsToDate(baseInput.value, option);
|
||||||
|
dateInput.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('change', function(e){
|
document.addEventListener('change', function(e){
|
||||||
if(!e.target.classList.contains('toggle-expired')) return;
|
if(!e.target.classList.contains('masa-berlaku-select')) return;
|
||||||
const targetId = e.target.getAttribute('data-target');
|
syncMasaBerlakuField(e.target);
|
||||||
if(!targetId) return;
|
});
|
||||||
const fieldWrap = document.getElementById(targetId);
|
|
||||||
const input = fieldWrap.querySelector('input');
|
document.addEventListener('change', function(e){
|
||||||
input.disabled = !e.target.checked;
|
if (!e.target.matches('input[type="date"][id^="dateActive_"]')) return;
|
||||||
|
const index = e.target.id.replace('dateActive_', '');
|
||||||
|
const selectEl = document.querySelector(`.masa-berlaku-select[data-index="${index}"]`);
|
||||||
|
if (selectEl) syncMasaBerlakuField(selectEl);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(pageSizeSelect){
|
if(pageSizeSelect){
|
||||||
@ -349,6 +453,7 @@
|
|||||||
formCreate.find('select').val(null).trigger('change');
|
formCreate.find('select').val(null).trigger('change');
|
||||||
formCreate.find('input[type="file"]').val('');
|
formCreate.find('input[type="file"]').val('');
|
||||||
formCreate.find('.file-name').addClass('d-none').text('');
|
formCreate.find('.file-name').addClass('d-none').text('');
|
||||||
|
document.querySelectorAll('.masa-berlaku-select').forEach(syncMasaBerlakuField);
|
||||||
resetAkreFields(0);
|
resetAkreFields(0);
|
||||||
enableAkreFields(0);
|
enableAkreFields(0);
|
||||||
}
|
}
|
||||||
@ -544,6 +649,31 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value){
|
||||||
|
return String(value ?? '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDeleteRecommendation(item){
|
||||||
|
const recommendation = item?.delete_recommendation;
|
||||||
|
|
||||||
|
if (!recommendation?.visible || !recommendation?.note) return '';
|
||||||
|
|
||||||
|
const actorRole = recommendation.actor_role || 'TURT';
|
||||||
|
const actorName = recommendation.actor_name ? ` (${escapeHtml(recommendation.actor_name)})` : '';
|
||||||
|
const note = escapeHtml(recommendation.note);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="mt-1 px-2 py-1 rounded border border-warning-subtle bg-warning-subtle text-warning-emphasis" style="font-size:12px; line-height:1.35;">
|
||||||
|
Dokumen ini direkomendasikan dihapus oleh ${escapeHtml(actorRole)}${actorName}. Catatan: ${note}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function buildRow(item){
|
function buildRow(item){
|
||||||
const parts = (item.file || '').split('/');
|
const parts = (item.file || '').split('/');
|
||||||
const fileName = parts.pop() || '-';
|
const fileName = parts.pop() || '-';
|
||||||
@ -607,6 +737,23 @@
|
|||||||
>
|
>
|
||||||
<i class="fa-solid fa-download"></i>
|
<i class="fa-solid fa-download"></i>
|
||||||
</a>
|
</a>
|
||||||
|
${authUnitKerja?.id === 22 ? `
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning btn-sm"
|
||||||
|
onclick="rekomendasiDelete(this)"
|
||||||
|
title="Rekomendasi Hapus"
|
||||||
|
data-filename="${item.nama_dokumen || '-'}"
|
||||||
|
data-id="${item.file_directory_id}"
|
||||||
|
data-no_dokumen="${item.no_dokumen || '-'}"
|
||||||
|
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
|
||||||
|
data-permission_file="${item.permission_file || '-'}"
|
||||||
|
data-pegawai_id_entry="${item.pegawai_id_entry || '-'}"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-trash-can"></i>
|
||||||
|
</button>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-nowrap">${item.no_dokumen || '-'}</td>
|
<td class="text-nowrap">${item.no_dokumen || '-'}</td>
|
||||||
@ -626,6 +773,7 @@
|
|||||||
</a>
|
</a>
|
||||||
${akreBadge}${expiryBadge}
|
${akreBadge}${expiryBadge}
|
||||||
</div>
|
</div>
|
||||||
|
${renderDeleteRecommendation(item)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
${item.nama_kategori || '-'}
|
${item.nama_kategori || '-'}
|
||||||
@ -1011,6 +1159,7 @@
|
|||||||
selectOptionUnitKerjaV1(0);
|
selectOptionUnitKerjaV1(0);
|
||||||
initKategoriSelect2(0);
|
initKategoriSelect2(0);
|
||||||
enableAkreFields(0);
|
enableAkreFields(0);
|
||||||
|
document.querySelectorAll('.masa-berlaku-select').forEach(syncMasaBerlakuField);
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadSubUnitKerja(unitId){
|
function loadSubUnitKerja(unitId){
|
||||||
@ -1020,6 +1169,8 @@
|
|||||||
url: `/select-sub-unit-kerja-mapping/${unitId}`,
|
url: `/select-sub-unit-kerja-mapping/${unitId}`,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
if (response?.data) {
|
if (response?.data) {
|
||||||
response.data.forEach(unit => {
|
response.data.forEach(unit => {
|
||||||
let selected = (authSubUnitKerja && unit.id === authSubUnitKerja.objectsubunitkerjapegawaifk);
|
let selected = (authSubUnitKerja && unit.id === authSubUnitKerja.objectsubunitkerjapegawaifk);
|
||||||
@ -1089,28 +1240,39 @@
|
|||||||
placeholder="Contoh: Panduan Mencuci Tangan" required>
|
placeholder="Contoh: Panduan Mencuci Tangan" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label fw-semibold">Tanggal Terbit</label>
|
<label class="form-label fw-semibold">Tanggal Terbit</label>
|
||||||
<input class="form-control"
|
<input class="form-control"
|
||||||
type="date"
|
type="date"
|
||||||
name="data[${colCount}][date_active]">
|
name="data[${colCount}][date_active]"
|
||||||
</div>
|
id="dateActive_${colCount}">
|
||||||
<div class="col-md-3">
|
</div>
|
||||||
<div class="form-check">
|
<div class="col-md-4">
|
||||||
<input class="form-check-input toggle-expired"
|
<label class="form-label fw-semibold">Masa Berlaku Dokumen</label>
|
||||||
type="checkbox"
|
<select class="form-select masa-berlaku-select"
|
||||||
id="hasExpired_${colCount}"
|
name="data[${colCount}][masa_berlaku_option]"
|
||||||
data-target="expiredField_${colCount}">
|
data-index="${colCount}"
|
||||||
<label class="form-check-label" for="hasExpired_${colCount}">Masa Berlaku Dokumen??</label>
|
data-date-target="expiredField_${colCount}"
|
||||||
</div>
|
data-input-target="expiredInput_${colCount}"
|
||||||
</div>
|
data-base-target="dateActive_${colCount}"
|
||||||
|
data-preview-target="expiredPreview_${colCount}">
|
||||||
|
<option value="">Selamanya</option>
|
||||||
|
<option value="1">1 Tahun</option>
|
||||||
|
<option value="2">2 Tahun</option>
|
||||||
|
<option value="3">3 Tahun</option>
|
||||||
|
<option value="custom">Lainnya</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text text-muted">Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.</div>
|
||||||
|
<div class="form-text text-primary" id="expiredPreview_${colCount}"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-md-5" id="expiredField_${colCount}">
|
<div class="col-md-4 d-none" id="expiredField_${colCount}">
|
||||||
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
|
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
|
||||||
<input class="form-control"
|
<input class="form-control"
|
||||||
type="date"
|
type="date"
|
||||||
name="data[${colCount}][tgl_expired]" disabled>
|
id="expiredInput_${colCount}"
|
||||||
</div>
|
name="data[${colCount}][tgl_expired]" disabled>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label fw-semibold">Boleh dilihat unit lain? <span class="text-danger">*</span></label>
|
<label class="form-label fw-semibold">Boleh dilihat unit lain? <span class="text-danger">*</span></label>
|
||||||
@ -1228,7 +1390,7 @@
|
|||||||
processResults: function(data){
|
processResults: function(data){
|
||||||
return {
|
return {
|
||||||
results : data?.data.map(item => ({
|
results : data?.data.map(item => ({
|
||||||
id: item.id+'/'+item.name,
|
id: item.id,
|
||||||
text: item.name,
|
text: item.name,
|
||||||
sub_units: item.sub_unit_kerja // kirim ke front
|
sub_units: item.sub_unit_kerja // kirim ke front
|
||||||
}))
|
}))
|
||||||
@ -1378,6 +1540,18 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const submitBtn = $(this).find('button[type="submit"]');
|
const submitBtn = $(this).find('button[type="submit"]');
|
||||||
submitBtn.prop('disabled', true).text('menyimpan...')
|
submitBtn.prop('disabled', true).text('menyimpan...')
|
||||||
|
|
||||||
|
const expiryError = prepareExpiryInputs(this);
|
||||||
|
if (expiryError) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'warning',
|
||||||
|
title: 'Validasi form',
|
||||||
|
text: expiryError
|
||||||
|
});
|
||||||
|
submitBtn.prop('disabled', false).text('Simpan');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const formData = new FormData(this);
|
const formData = new FormData(this);
|
||||||
console.log(formData);
|
console.log(formData);
|
||||||
|
|
||||||
@ -1482,5 +1656,67 @@
|
|||||||
}
|
}
|
||||||
$("#previewModal").modal('show')
|
$("#previewModal").modal('show')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rekomendasiDelete(button){
|
||||||
|
const id = $(button).data('id');
|
||||||
|
const fileName = $(button).data('filename') || 'dokumen';
|
||||||
|
const noDokumen = $(button).data('no_dokumen') || '-';
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Rekomendasi hapus dokumen?',
|
||||||
|
text: `${fileName} (${noDokumen})`,
|
||||||
|
icon: 'warning',
|
||||||
|
input: 'textarea',
|
||||||
|
inputLabel: 'Catatan rekomendasi',
|
||||||
|
inputPlaceholder: 'Tulis alasan kenapa dokumen ini direkomendasikan untuk dihapus...',
|
||||||
|
inputAttributes: {
|
||||||
|
'aria-label': 'Catatan rekomendasi'
|
||||||
|
},
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Kirim rekomendasi',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
preConfirm: (value) => {
|
||||||
|
const note = (value || '').trim();
|
||||||
|
if (!note) {
|
||||||
|
Swal.showValidationMessage('Catatan rekomendasi wajib diisi.');
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
|
fetch(`/recommend-delete-file/${id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
note: result.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Berhasil',
|
||||||
|
text: data.message || 'Rekomendasi hapus berhasil dikirim.',
|
||||||
|
timer: 1800,
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(data.message || 'Gagal mengirim rekomendasi hapus.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Gagal',
|
||||||
|
text: err.message || 'Terjadi kesalahan.'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@ -46,17 +46,29 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label fw-semibold">Tanggal Terbit</label>
|
<label class="form-label fw-semibold">Tanggal Terbit</label>
|
||||||
<input class="form-control" type="date" name="data[0][date_active]">
|
<input class="form-control" type="date" name="data[0][date_active]" id="dateActive_0">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="form-check">
|
<label class="form-label fw-semibold">Masa Berlaku Dokumen</label>
|
||||||
<input class="form-check-input toggle-expired" type="checkbox" id="hasExpired0" data-target="expiredField_0">
|
<select class="form-select masa-berlaku-select"
|
||||||
<label class="form-check-label" for="hasExpired0">Masa Berlaku Dokumen?</label>
|
name="data[0][masa_berlaku_option]"
|
||||||
</div>
|
data-index="0"
|
||||||
|
data-date-target="expiredField_0"
|
||||||
|
data-input-target="expiredInput_0"
|
||||||
|
data-base-target="dateActive_0"
|
||||||
|
data-preview-target="expiredPreview_0">
|
||||||
|
<option value="">Selamanya</option>
|
||||||
|
<option value="1">1 Tahun</option>
|
||||||
|
<option value="2">2 Tahun</option>
|
||||||
|
<option value="3">3 Tahun</option>
|
||||||
|
<option value="custom">Lainnya</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text text-muted">Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.</div>
|
||||||
|
<div class="form-text text-primary" id="expiredPreview_0"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4" id="expiredField_0">
|
<div class="col-md-4 d-none" id="expiredField_0">
|
||||||
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
|
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
|
||||||
<input class="form-control" type="date" name="data[0][tgl_expired]" disabled>
|
<input class="form-control" type="date" name="data[0][tgl_expired]" id="expiredInput_0" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label fw-semibold">Boleh dilihat unit lain? <span class="text-danger">*</span></label>
|
<label class="form-label fw-semibold">Boleh dilihat unit lain? <span class="text-danger">*</span></label>
|
||||||
|
|||||||
@ -21,6 +21,55 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
||||||
<style>
|
<style>
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
.body-wrapper,
|
||||||
|
.body-wrapper-inner,
|
||||||
|
.container-fluid {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(148, 163, 184, 0.5) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
html::-webkit-scrollbar,
|
||||||
|
body::-webkit-scrollbar,
|
||||||
|
.body-wrapper::-webkit-scrollbar,
|
||||||
|
.body-wrapper-inner::-webkit-scrollbar,
|
||||||
|
.container-fluid::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html::-webkit-scrollbar-track,
|
||||||
|
body::-webkit-scrollbar-track,
|
||||||
|
.body-wrapper::-webkit-scrollbar-track,
|
||||||
|
.body-wrapper-inner::-webkit-scrollbar-track,
|
||||||
|
.container-fluid::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html::-webkit-scrollbar-thumb,
|
||||||
|
body::-webkit-scrollbar-thumb,
|
||||||
|
.body-wrapper::-webkit-scrollbar-thumb,
|
||||||
|
.body-wrapper-inner::-webkit-scrollbar-thumb,
|
||||||
|
.container-fluid::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(148, 163, 184, 0.5);
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html::-webkit-scrollbar-thumb:hover,
|
||||||
|
body::-webkit-scrollbar-thumb:hover,
|
||||||
|
.body-wrapper::-webkit-scrollbar-thumb:hover,
|
||||||
|
.body-wrapper-inner::-webkit-scrollbar-thumb:hover,
|
||||||
|
.container-fluid::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.68);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
.modal,
|
.modal,
|
||||||
.modal-content,
|
.modal-content,
|
||||||
.modal-body,
|
.modal-body,
|
||||||
|
|||||||
@ -2,5 +2,5 @@
|
|||||||
<p class="mb-0 fs-4"> ©
|
<p class="mb-0 fs-4"> ©
|
||||||
<script>
|
<script>
|
||||||
document.write(new Date().getFullYear());
|
document.write(new Date().getFullYear());
|
||||||
</script> made with by <a href="/" target="_blank" >TIM SIRS</a></p>
|
</script> made with by <a href="/" target="_blank" >TIM SIMRS</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -52,7 +52,25 @@
|
|||||||
{{-- AKTIVITAS --}}
|
{{-- AKTIVITAS --}}
|
||||||
<li class="nav-small-cap"><span class="hide-menu">Aktivitas</span></li>
|
<li class="nav-small-cap"><span class="hide-menu">Aktivitas</span></li>
|
||||||
@php
|
@php
|
||||||
$isAtasan = \App\Models\MappingUnitKerjaPegawai::where('statusenabled', true)->where('objectatasanlangsungfk', auth()->user()->objectpegawaifk)->exists();
|
$pegawaiId = auth()->user()->objectpegawaifk;
|
||||||
|
$userUnitIds = auth()->user()->dataUser?->mappingUnitKerjaPegawai()
|
||||||
|
->where('statusenabled', true)
|
||||||
|
->pluck('objectunitkerjapegawaifk')
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
if($userUnitIds){
|
||||||
|
$isAtasan = \App\Models\MappingUnitKerjaPegawai::where('statusenabled', true)
|
||||||
|
->where(function ($q) use ($pegawaiId) {
|
||||||
|
$q->where('objectatasanlangsungfk', $pegawaiId)
|
||||||
|
->orWhere('objectpejabatpenilaifk', $pegawaiId);
|
||||||
|
})->exists();
|
||||||
|
$isRoleApprover = in_array(51, $userUnitIds, true) || in_array(22, $userUnitIds, true);
|
||||||
|
}else{
|
||||||
|
$isAtasan = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@endphp
|
@endphp
|
||||||
@if($isAtasan)
|
@if($isAtasan)
|
||||||
@if(!Auth::guard('admin')->check())
|
@if(!Auth::guard('admin')->check())
|
||||||
@ -79,6 +97,10 @@
|
|||||||
<i class="ti ti-clock"></i>
|
<i class="ti ti-clock"></i>
|
||||||
<span class="hide-menu">Pengajuan</span>
|
<span class="hide-menu">Pengajuan</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if($isRoleApprover)
|
||||||
|
<span class="badge bg-danger rounded-pill d-none" id="pengajuanPendingCountBadge">0</span>
|
||||||
|
@endif
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
@ -114,7 +136,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@if(!Auth::guard('admin')->check())
|
@if(!Auth::guard('admin')->check())
|
||||||
@if(auth()->user()->dataUser->mappingUnitKerjaPegawai()->where('objectunitkerjapegawaifk', 43)->exists())
|
@if(auth()->user()->dataUser->mappingUnitKerjaPegawai()->whereIn('objectunitkerjapegawaifk', [43, 22])->exists())
|
||||||
<li class="nav-small-cap"><span class="hide-menu">Master</span></li>
|
<li class="nav-small-cap"><span class="hide-menu">Master</span></li>
|
||||||
|
|
||||||
<li class="sidebar-item has-sub {{ $openMaster ? 'open' : '' }}">
|
<li class="sidebar-item has-sub {{ $openMaster ? 'open' : '' }}">
|
||||||
@ -192,10 +214,11 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const badge = document.getElementById('pendingCountBadge');
|
const pendingBadge = document.getElementById('pendingCountBadge');
|
||||||
|
const pengajuanBadge = document.getElementById('pengajuanPendingCountBadge');
|
||||||
|
|
||||||
async function countData() {
|
async function countData() {
|
||||||
if (!badge) return;
|
if (!pendingBadge && !pengajuanBadge) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/data/count-pending', {
|
const res = await fetch('/data/count-pending', {
|
||||||
@ -207,14 +230,24 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const count = Number(data?.count ?? 0);
|
const count = Number(data?.count ?? 0);
|
||||||
|
|
||||||
badge.textContent = count;
|
if (pendingBadge) {
|
||||||
badge.classList.toggle('d-none', count <= 0);
|
pendingBadge.textContent = count;
|
||||||
|
pendingBadge.classList.toggle('d-none', count <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pengajuanBadge) {
|
||||||
|
pengajuanBadge.textContent = count;
|
||||||
|
pengajuanBadge.classList.toggle('d-none', count <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
badge.classList.add('d-none');
|
if (pendingBadge) pendingBadge.classList.add('d-none');
|
||||||
|
if (pengajuanBadge) pengajuanBadge.classList.add('d-none');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.refreshPendingCount = countData;
|
||||||
|
|
||||||
countData();
|
countData();
|
||||||
|
|
||||||
// setInterval(countData, 60000);
|
// setInterval(countData, 60000);
|
||||||
|
|||||||
@ -1,3 +1,22 @@
|
|||||||
|
{{-- @php
|
||||||
|
$globalUpdateItems = [
|
||||||
|
[
|
||||||
|
'date' => '29 Juni 2026',
|
||||||
|
'title' => 'Metadata preview dokumen diperluas',
|
||||||
|
'description' => 'Modal preview sekarang menampilkan nomor dokumen, tanggal terbit, tanggal kedaluwarsa, tanggal upload, akses dokumen, jenis dokumen akreditasi/non-akreditasi, dan kategori.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'date' => '29 Juni 2026',
|
||||||
|
'title' => 'Nama unit ditampilkan lebih jelas',
|
||||||
|
'description' => 'Data persetujuan dan pengajuan file sekarang memakai nama unit langsung dari tabel unit agar lebih mudah dibaca.',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'date' => '29 Juni 2026',
|
||||||
|
'title' => 'Perbaikan query persetujuan',
|
||||||
|
'description' => 'Query halaman persetujuan dan pengajuan file dirapikan supaya aman saat join tabel dan tidak gagal karena kolom ambigu.',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
@endphp --}}
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.message-body {
|
.message-body {
|
||||||
@ -58,6 +77,11 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div class="navbar-collapse justify-content-end px-0" id="navbarNav">
|
<div class="navbar-collapse justify-content-end px-0" id="navbarNav">
|
||||||
<ul class="navbar-nav flex-row ms-auto align-items-center justify-content-end">
|
<ul class="navbar-nav flex-row ms-auto align-items-center justify-content-end">
|
||||||
|
{{-- <li class="nav-item me-2">
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#updatesInfoModal">
|
||||||
|
<i class="fa-solid fa-bullhorn me-1"></i> Info Update
|
||||||
|
</button>
|
||||||
|
</li> --}}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link position-relative" href="javascript:void(0)" id="dropExpired" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link position-relative" href="javascript:void(0)" id="dropExpired" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<span class="d-inline-flex align-items-center justify-content-center rounded-circle bg-light" style="width:46px;height:46px;">
|
<span class="d-inline-flex align-items-center justify-content-center rounded-circle bg-light" style="width:46px;height:46px;">
|
||||||
@ -177,6 +201,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- <div class="modal fade" id="updatesInfoModal" tabindex="-1" aria-labelledby="updatesInfoModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="updatesInfoModalLabel">Info Update Terbaru</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="d-flex flex-column gap-3">
|
||||||
|
@foreach ($globalUpdateItems as $update)
|
||||||
|
<div class="border rounded p-3">
|
||||||
|
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2 mb-2">
|
||||||
|
<h6 class="mb-0">{{ $update['title'] }}</h6>
|
||||||
|
<span class="badge bg-light text-dark border">{{ $update['date'] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted small">{{ $update['description'] }}</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Tutup</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> --}}
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
@extends('layout.main')
|
@extends('layout.main')
|
||||||
@section('body_main')
|
@section('body_main')
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -58,7 +57,7 @@
|
|||||||
<div class="small text-muted ms-md-auto" id="tableSummary"></div>
|
<div class="small text-muted ms-md-auto" id="tableSummary"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="tabPengajuan">
|
<div id="tabPengajuan">
|
||||||
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto; overflow-x:auto;">
|
<div class="table-responsive" style="max-height: 75vh; overflow-y:auto; overflow-x:auto;">
|
||||||
<table class="table table-sm table-hover table-striped align-middle mb-0 pending-table" id="lastUpdatedTable">
|
<table class="table table-sm table-hover table-striped align-middle mb-0 pending-table" id="lastUpdatedTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -96,9 +95,7 @@
|
|||||||
<th>Kategori</th>
|
<th>Kategori</th>
|
||||||
<th>Unit</th>
|
<th>Unit</th>
|
||||||
<th>Aksi</th>
|
<th>Aksi</th>
|
||||||
<th>Tanggal Terbit</th>
|
<th>Tanggal Aksi</th>
|
||||||
<th>Tanggal Kedaluwarsa Dokumen</th>
|
|
||||||
<th>Tanggal Upload</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tableHistoryFile">
|
<tbody id="tableHistoryFile">
|
||||||
@ -114,5 +111,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@include('pendingFile.modal.view')
|
@include('pendingFile.modal.view')
|
||||||
|
<script>
|
||||||
|
window.isKomiteMutu = @json($isKomiteMutu);
|
||||||
|
window.isTurt = @json($isTurt);
|
||||||
|
</script>
|
||||||
<script src="{{ ver('/js/pendingFile/index.js') }}"></script>
|
<script src="{{ ver('/js/pendingFile/index.js') }}"></script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@ -13,6 +13,9 @@
|
|||||||
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
<div class="modal-body p-2" style="min-height:250px; max-height:70vh; overflow:auto;">
|
<div class="modal-body p-2" style="min-height:250px; max-height:70vh; overflow:auto;">
|
||||||
|
<div class="d-flex justify-content-end align-items-center sticky-top bg-white z-10 d-none" id="deleteData">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger mb-2 me-2" id="delete-file">Hapus</button>
|
||||||
|
</div>
|
||||||
<div id="file-preview"
|
<div id="file-preview"
|
||||||
class="text-center text-muted d-flex justify-content-center align-items-center"
|
class="text-center text-muted d-flex justify-content-center align-items-center"
|
||||||
style="height:100%;">
|
style="height:100%;">
|
||||||
|
|||||||
@ -46,10 +46,26 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link active" type="button" data-mode="pengajuan">Data Pengajuan</button>
|
<button class="nav-link active" type="button" data-mode="pengajuan">Data Pengajuan</button>
|
||||||
</li>
|
</li>
|
||||||
|
@if($isKomiteMutu || $isTurt || $isAtasan)
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="nav-link d-flex align-items-center gap-2" type="button" data-mode="persetujuan">
|
||||||
|
<span>Data Persetujuan</span>
|
||||||
|
<span class="badge bg-danger rounded-pill d-none" id="pendingPersetujuanTabBadge">0</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link" type="button" data-mode="history">Log History</button>
|
<button class="nav-link" type="button" data-mode="history">Log History</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@if(($isKomiteMutu || $isTurt) && !$isAtasan)
|
||||||
|
<div class="alert alert-warning d-none align-items-center justify-content-between gap-3 py-2" id="pendingPersetujuanNotice" role="alert">
|
||||||
|
<div>
|
||||||
|
Ada <strong id="pendingPersetujuanNoticeCount">0</strong> data yang menunggu persetujuan Anda.
|
||||||
|
</div>
|
||||||
|
<a href="{{ url('/pending-file') }}" class="btn btn-sm btn-outline-warning">Lihat Persetujuan</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3 flex-wrap">
|
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3 flex-wrap">
|
||||||
<div class="input-group input-group-sm flex-grow-1" style="max-width:320px;">
|
<div class="input-group input-group-sm flex-grow-1" style="max-width:320px;">
|
||||||
<span class="input-group-text bg-white border-end-0">
|
<span class="input-group-text bg-white border-end-0">
|
||||||
@ -78,6 +94,14 @@
|
|||||||
<option value="100">100</option>
|
<option value="100">100</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2" id="pengajuanBulkActions">
|
||||||
|
<button class="btn btn-danger btn-sm" id="bulkDeleteBtn" disabled>
|
||||||
|
Hapus dipilih (<span id="selectedCount">0</span>)
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary btn-sm" id="clearSelectionBtn" disabled>
|
||||||
|
Reset pilihan
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button class="btn btn-outline-secondary btn-sm" onclick="refreshLog()">
|
<button class="btn btn-outline-secondary btn-sm" onclick="refreshLog()">
|
||||||
<i class="fa fa-rotate"></i> Refresh
|
<i class="fa fa-rotate"></i> Refresh
|
||||||
</button>
|
</button>
|
||||||
@ -88,6 +112,9 @@
|
|||||||
<table class="table table-sm table-hover align-middle mb-0" id="tablePengajuan">
|
<table class="table table-sm table-hover align-middle mb-0" id="tablePengajuan">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" class="form-check-input" id="selectAllPengajuan" title="Pilih semua di halaman">
|
||||||
|
</th>
|
||||||
<th>Aksi</th>
|
<th>Aksi</th>
|
||||||
<th>No Dokumen</th>
|
<th>No Dokumen</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
@ -95,8 +122,6 @@
|
|||||||
<th>Nama</th>
|
<th>Nama</th>
|
||||||
<th>Kategori</th>
|
<th>Kategori</th>
|
||||||
<th>Unit</th>
|
<th>Unit</th>
|
||||||
<th>Tanggal Terbit</th>
|
|
||||||
<th>Tanggal Kedaluwarsa Dokumen</th>
|
|
||||||
<th>Tanggal Upload</th>
|
<th>Tanggal Upload</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -108,7 +133,7 @@
|
|||||||
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2 mt-3" id="paginationPengajuan"></div>
|
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2 mt-3" id="paginationPengajuan"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="tabHistory" class="d-none">
|
<div id="tabHistory" class="d-none">
|
||||||
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto;">
|
<div class="table-responsive" style="max-height: 75vh; overflow-y:auto;">
|
||||||
<table class="table table-sm table-hover align-middle mb-0" id="tableHistory">
|
<table class="table table-sm table-hover align-middle mb-0" id="tableHistory">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -118,9 +143,7 @@
|
|||||||
<th>Kategori</th>
|
<th>Kategori</th>
|
||||||
<th>Unit</th>
|
<th>Unit</th>
|
||||||
<th>Aksi</th>
|
<th>Aksi</th>
|
||||||
<th>Tanggal Terbit</th>
|
<th>Tanggal Aksi</th>
|
||||||
<th>Tanggal Kedaluwarsa Dokumen</th>
|
|
||||||
<th>Tanggal Upload</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tableHistoryFile">
|
<tbody id="tableHistoryFile">
|
||||||
@ -140,6 +163,9 @@
|
|||||||
@include('dataUnit.modal.create')
|
@include('dataUnit.modal.create')
|
||||||
<script>
|
<script>
|
||||||
window.katDok = @json($katDok);
|
window.katDok = @json($katDok);
|
||||||
|
window.isKomiteMutu = @json($isKomiteMutu);
|
||||||
|
window.isTurt = @json($isTurt);
|
||||||
|
window.isAtasan = @json($isAtasan);
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ ver('/js/pengajuanFile/index.js') }}"></script>
|
<script src="{{ ver('/js/pengajuanFile/index.js') }}"></script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@ -47,12 +47,25 @@
|
|||||||
<input class="form-control" type="date" name="tanggal_terbit" id="edit_tanggal_terbit">
|
<input class="form-control" type="date" name="tanggal_terbit" id="edit_tanggal_terbit">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="form-check">
|
<label class="form-label fw-semibold">Masa Berlaku Dokumen</label>
|
||||||
<input class="form-check-input toggle-expired" type="checkbox" id="edit_has_expired" data-target="edit_expired_field">
|
<select class="form-select masa-berlaku-select"
|
||||||
<label class="form-check-label" for="edit_has_expired">Masa Berlaku Dokumen?</label>
|
name="masa_berlaku_option"
|
||||||
</div>
|
id="edit_masa_berlaku_option"
|
||||||
|
data-index="edit"
|
||||||
|
data-date-target="edit_expired_field"
|
||||||
|
data-input-target="edit_tgl_expired"
|
||||||
|
data-base-target="edit_tanggal_terbit"
|
||||||
|
data-preview-target="edit_expired_preview">
|
||||||
|
<option value="">Selamanya</option>
|
||||||
|
<option value="1">1 Tahun</option>
|
||||||
|
<option value="2">2 Tahun</option>
|
||||||
|
<option value="3">3 Tahun</option>
|
||||||
|
<option value="custom">Lainnya</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text text-muted">Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.</div>
|
||||||
|
<div class="form-text text-primary" id="edit_expired_preview"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4" id="edit_expired_field">
|
<div class="col-md-4 d-none" id="edit_expired_field">
|
||||||
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
|
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
|
||||||
<input class="form-control" type="date" name="tgl_expired" id="edit_tgl_expired" disabled>
|
<input class="form-control" type="date" name="tgl_expired" id="edit_tgl_expired" disabled>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -19,12 +19,10 @@ Route::middleware(['auth:admin,web'])->group(function(){
|
|||||||
Route::get('/datatable-umum', [DashboardController::class, 'datatableDataUmum']);
|
Route::get('/datatable-umum', [DashboardController::class, 'datatableDataUmum']);
|
||||||
Route::get('/data-akreditasi', [DashboardController::class, 'dataAkreditasi']);
|
Route::get('/data-akreditasi', [DashboardController::class, 'dataAkreditasi']);
|
||||||
Route::get('/datatable-akreditasi', [DashboardController::class, 'dataTableAkreditasi']);
|
Route::get('/datatable-akreditasi', [DashboardController::class, 'dataTableAkreditasi']);
|
||||||
Route::get('/data-akreditasi/recap', [DashboardController::class, 'dataAkreditasiRecap']);
|
|
||||||
|
|
||||||
// Kelola "folder/instrumen" akreditasi via file: public/json/akreditasi.jff (tanpa database)
|
// Kelola "folder/instrumen" akreditasi via file: public/json/akreditasi.jff (tanpa database)
|
||||||
Route::get('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'index']);
|
Route::get('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'index']);
|
||||||
Route::post('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'store']);
|
Route::post('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'store']);
|
||||||
Route::delete('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'destroy']);
|
|
||||||
Route::get('/download-excel/data-umum', [DashboardController::class, 'downloadDataUmumExcel']);
|
Route::get('/download-excel/data-umum', [DashboardController::class, 'downloadDataUmumExcel']);
|
||||||
Route::post('/uploadv2', [DashboardController::class, 'storeVersion2']);
|
Route::post('/uploadv2', [DashboardController::class, 'storeVersion2']);
|
||||||
Route::get('/file-preview/{id}', [DashboardController::class, 'dataPdf']);
|
Route::get('/file-preview/{id}', [DashboardController::class, 'dataPdf']);
|
||||||
@ -53,7 +51,9 @@ Route::middleware(['auth:admin,web'])->group(function(){
|
|||||||
Route::get('/select-sub-unit-kerja-mapping/{id}', [DashboardController::class, 'optionSubUnitKerjaByMapping']);
|
Route::get('/select-sub-unit-kerja-mapping/{id}', [DashboardController::class, 'optionSubUnitKerjaByMapping']);
|
||||||
|
|
||||||
|
|
||||||
|
Route::delete('/delete-file/bulk', [DashboardController::class, 'deleteFiles']);
|
||||||
Route::delete('/delete-file/{id}', [DashboardController::class, 'deleteFile']);
|
Route::delete('/delete-file/{id}', [DashboardController::class, 'deleteFile']);
|
||||||
|
Route::post('/recommend-delete-file/{id}', [DashboardController::class, 'recommendDeleteFile']);
|
||||||
// Route::get('/getFile/{id_unit_kerja}/{id_sub_unit_kerja}/{master_kategori_directory_id}', [DashboardController::class, 'getFile']);
|
// Route::get('/getFile/{id_unit_kerja}/{id_sub_unit_kerja}/{master_kategori_directory_id}', [DashboardController::class, 'getFile']);
|
||||||
|
|
||||||
Route::post('/download-multiple', [DashboardController::class, 'downloadDataMultiple']);
|
Route::post('/download-multiple', [DashboardController::class, 'downloadDataMultiple']);
|
||||||
@ -71,6 +71,10 @@ Route::middleware(['auth:admin,web'])->group(function(){
|
|||||||
Route::get('/pengajuan-file', [DashboardController::class, 'pengajuanFile']);
|
Route::get('/pengajuan-file', [DashboardController::class, 'pengajuanFile']);
|
||||||
Route::get('/datatable/pengajuan-file', [DashboardController::class, 'dataPengajuanFile']);
|
Route::get('/datatable/pengajuan-file', [DashboardController::class, 'dataPengajuanFile']);
|
||||||
Route::post('/pengajuan-file/{id}/update', [DashboardController::class, 'updatePengajuanFile']);
|
Route::post('/pengajuan-file/{id}/update', [DashboardController::class, 'updatePengajuanFile']);
|
||||||
|
Route::post('/pengajuan-file/{id}/approve-mutu', [DashboardController::class, 'approvePendingFileMutu']);
|
||||||
|
Route::post('/pengajuan-file/{id}/approve-turt', [DashboardController::class, 'approvePendingFileTurt']);
|
||||||
|
Route::post('/pengajuan-file/{id}/reject-mutu', [DashboardController::class, 'rejectPendingFileMutu']);
|
||||||
|
Route::post('/pengajuan-file/{id}/reject-turt', [DashboardController::class, 'rejectPendingFileTurt']);
|
||||||
|
|
||||||
// Route::middleware(['master.persetujuan'])->group(function () {
|
// Route::middleware(['master.persetujuan'])->group(function () {
|
||||||
Route::get('/pending-file', [DashboardController::class, 'pendingFile']);
|
Route::get('/pending-file', [DashboardController::class, 'pendingFile']);
|
||||||
@ -78,6 +82,10 @@ Route::middleware(['auth:admin,web'])->group(function(){
|
|||||||
Route::post('/pending-file/{id}/approve', [DashboardController::class, 'approvePendingFile']);
|
Route::post('/pending-file/{id}/approve', [DashboardController::class, 'approvePendingFile']);
|
||||||
Route::post('/pending-file/approve-multiple', [DashboardController::class, 'approvePendingFileMultiple']);
|
Route::post('/pending-file/approve-multiple', [DashboardController::class, 'approvePendingFileMultiple']);
|
||||||
Route::post('/pending-file/{id}/reject', [DashboardController::class, 'rejectPendingFile']);
|
Route::post('/pending-file/{id}/reject', [DashboardController::class, 'rejectPendingFile']);
|
||||||
|
Route::post('/pending-file/{id}/approve-mutu', [DashboardController::class, 'approvePendingFileMutu']);
|
||||||
|
Route::post('/pending-file/{id}/approve-turt', [DashboardController::class, 'approvePendingFileTurt']);
|
||||||
|
Route::post('/pending-file/{id}/reject-mutu', [DashboardController::class, 'rejectPendingFileMutu']);
|
||||||
|
Route::post('/pending-file/{id}/reject-turt', [DashboardController::class, 'rejectPendingFileTurt']);
|
||||||
Route::get('/data/count-pending', [DashboardController::class, 'countDataPending']);
|
Route::get('/data/count-pending', [DashboardController::class, 'countDataPending']);
|
||||||
Route::get('/data/count-rejected', [DashboardController::class, 'countRejectedPengajuan']);
|
Route::get('/data/count-rejected', [DashboardController::class, 'countRejectedPengajuan']);
|
||||||
// });
|
// });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user