2024-12-19 10:51:27 +07:00
|
|
|
@extends('layouts.app')
|
|
|
|
|
|
|
|
|
|
@section('title')
|
2026-06-01 15:01:03 +07:00
|
|
|
Dashboard
|
2024-12-19 10:51:27 +07:00
|
|
|
@endsection
|
|
|
|
|
|
|
|
|
|
@section('admin-content')
|
2025-10-13 00:14:28 +07:00
|
|
|
<style>
|
2026-06-01 15:01:03 +07:00
|
|
|
/* Style Awal Anda */
|
2025-10-13 00:14:28 +07:00
|
|
|
#outstandingCard {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
#outstandingCard.active {
|
|
|
|
|
box-shadow: 0 0 0 0.25rem rgba(255, 255, 255, 0.35);
|
|
|
|
|
}
|
2026-06-01 15:01:03 +07:00
|
|
|
|
|
|
|
|
/* Style Baru: Mini Status Cards (Seragam dengan Up Country) */
|
|
|
|
|
.status-card {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s ease-in-out;
|
|
|
|
|
border: 1px solid rgba(0,0,0,0.08);
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
}
|
|
|
|
|
.status-card:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
|
|
|
|
.status-card.active-filter {
|
|
|
|
|
border: 2px solid #343a40;
|
|
|
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
|
|
|
|
|
}
|
|
|
|
|
.mini-stat-box {
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
min-height: 45px;
|
|
|
|
|
}
|
|
|
|
|
.mini-stat-box h6 { margin: 0; font-weight: 800; font-size: 0.95rem; color: #333; }
|
|
|
|
|
.mini-stat-box p { margin: 0; font-size: 0.65rem; text-transform: uppercase; color: #777; font-weight: 600; }
|
|
|
|
|
|
|
|
|
|
/* Indikator Warna Kiri */
|
|
|
|
|
.status-card[data-status="On Progress"] { border-left: 3px solid #ffc107; }
|
|
|
|
|
.status-card[data-status="Approved 1"] { border-left: 3px solid #17a2b8; }
|
|
|
|
|
.status-card[data-status="Approved 2"] { border-left: 3px solid #007bff; }
|
|
|
|
|
.status-card[data-status="Final Approved"] { border-left: 3px solid #28a745; }
|
|
|
|
|
.status-card[data-status="Rejected"] { border-left: 3px solid #dc3545; }
|
|
|
|
|
.status-card[data-status="Closed"] { border-left: 3px solid #6c757d; }
|
2025-10-13 00:14:28 +07:00
|
|
|
</style>
|
2026-06-01 15:01:03 +07:00
|
|
|
|
2025-10-13 00:14:28 +07:00
|
|
|
@php
|
|
|
|
|
$outstandingIdCollection = collect($outstandingIds ?? []);
|
2026-06-01 15:01:03 +07:00
|
|
|
|
|
|
|
|
// ==========================================
|
|
|
|
|
// FILTER PERIODE BERJALAN KHUSUS UNTUK MINI CARDS
|
|
|
|
|
// (Agar Angka sinkron dengan tabel filter JS di bawah)
|
|
|
|
|
// ==========================================
|
|
|
|
|
$startDayIndex = (int) env('STARTING_DATE', 11);
|
|
|
|
|
$closingDayIndex = (int) env('CLOSING_DATE', 13);
|
|
|
|
|
$nowIndex = \Carbon\Carbon::now();
|
|
|
|
|
|
|
|
|
|
if ($nowIndex->day >= $startDayIndex) {
|
|
|
|
|
$startPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $startDayIndex)->startOfDay();
|
|
|
|
|
$endPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->addMonth()->year, $nowIndex->copy()->addMonth()->month, $closingDayIndex)->endOfDay();
|
|
|
|
|
} else {
|
|
|
|
|
$startPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->subMonth()->year, $nowIndex->copy()->subMonth()->month, $startDayIndex)->startOfDay();
|
|
|
|
|
$endPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $closingDayIndex)->endOfDay();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Penampung data pengajuan MURNI HANYA DI PERIODE BERJALAN SAAT INI
|
|
|
|
|
$currentPeriodForms = $forms->filter(function($form) use ($startPeriodIndex, $endPeriodIndex) {
|
|
|
|
|
$tanggalForm = \Carbon\Carbon::parse($form->tanggal);
|
|
|
|
|
return $tanggalForm->between($startPeriodIndex, $endPeriodIndex);
|
|
|
|
|
});
|
2025-10-13 00:14:28 +07:00
|
|
|
@endphp
|
2026-06-01 15:01:03 +07:00
|
|
|
|
2024-12-19 10:51:27 +07:00
|
|
|
<section class="content-header">
|
|
|
|
|
<div class="container-fluid">
|
|
|
|
|
<div class="row mb-2">
|
|
|
|
|
<div class="col-sm-6">
|
|
|
|
|
<h1>Forms Meeting Seminar</h1>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-sm-6">
|
|
|
|
|
<ol class="breadcrumb float-sm-right">
|
|
|
|
|
<li class="breadcrumb-item"><a href="{{ route('dashboard.index') }}">Dashboard</a></li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
2026-06-01 15:01:03 +07:00
|
|
|
|
|
|
|
|
<section class="content">
|
|
|
|
|
<div class="container-fluid">
|
|
|
|
|
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-5 g-3">
|
|
|
|
|
<div class="col d-flex">
|
|
|
|
|
<div class="small-box bg-info flex-fill">
|
|
|
|
|
<div class="inner">
|
|
|
|
|
<h3>Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}</h3>
|
|
|
|
|
<p>Total Expense</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="icon"><i class="fas fa-wallet"></i></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col d-flex">
|
|
|
|
|
<div class="small-box bg-success flex-fill">
|
|
|
|
|
<div class="inner">
|
|
|
|
|
<h3>Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}</h3>
|
|
|
|
|
<p>Approved Expenses</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="icon"><i class="fas fa-check-circle"></i></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col d-flex">
|
|
|
|
|
<div class="small-box bg-warning flex-fill">
|
|
|
|
|
<div class="inner">
|
|
|
|
|
<h3 class="text-white">Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}</h3>
|
|
|
|
|
<p class="text-white">Pending Expenses</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="icon"><i class="fas fa-hourglass-half"></i></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col d-flex">
|
|
|
|
|
<div class="small-box bg-danger flex-fill" id="outstandingCard">
|
|
|
|
|
<div class="inner">
|
|
|
|
|
<h3>{{ $outstandingCount }}</h3>
|
|
|
|
|
<p>Outstanding Expenses</p>
|
|
|
|
|
<p class="mb-0"><small>Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}</small></p>
|
|
|
|
|
<p class="mb-0 filter-hint"><small>Klik untuk menampilkan detail</small></p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="icon"><i class="fas fa-exclamation-triangle"></i></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@if($availableBudget > 0)
|
|
|
|
|
<div class="col d-flex">
|
|
|
|
|
<div class="small-box bg-purple flex-fill">
|
|
|
|
|
<div class="inner">
|
|
|
|
|
<h3 class="text-white">Rp {{ number_format($availableBudget, 0, ',', '.') }}</h3>
|
|
|
|
|
<p class="text-white">Available Budget</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="icon"><i class="fas fa-money-bill"></i></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@endif
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="content mt-2">
|
|
|
|
|
<div class="container-fluid">
|
|
|
|
|
<div class="row row-cols-2 row-cols-sm-3 row-cols-md-4 row-cols-xl-7 g-2 mb-3">
|
|
|
|
|
<div class="col"><div class="status-card active-filter" data-status="all"><div class="mini-stat-box"><div><p>Semua</p><h6>{{ $currentPeriodForms->count() }}</h6></div><i class="fas fa-list-ul text-muted"></i></div></div></div>
|
|
|
|
|
<div class="col"><div class="status-card" data-status="On Progress"><div class="mini-stat-box"><div><p>Progress</p><h6>{{ $currentPeriodForms->where('status', 'On Progress')->count() }}</h6></div><i class="fas fa-sync text-warning fa-xs"></i></div></div></div>
|
|
|
|
|
<div class="col"><div class="status-card" data-status="Approved 1"><div class="mini-stat-box"><div><p>App 1</p><h6>{{ $currentPeriodForms->where('status', 'Approved 1')->count() }}</h6></div><i class="fas fa-user-check text-info fa-xs"></i></div></div></div>
|
|
|
|
|
<div class="col"><div class="status-card" data-status="Approved 2"><div class="mini-stat-box"><div><p>App 2</p><h6>{{ $currentPeriodForms->where('status', 'Approved 2')->count() }}</h6></div><i class="fas fa-users text-primary fa-xs"></i></div></div></div>
|
|
|
|
|
<div class="col"><div class="status-card" data-status="Final Approved"><div class="mini-stat-box"><div><p>Final</p><h6>{{ $currentPeriodForms->where('status', 'Final Approved')->count() }}</h6></div><i class="fas fa-check-double text-success fa-xs"></i></div></div></div>
|
|
|
|
|
<div class="col"><div class="status-card" data-status="Rejected"><div class="mini-stat-box"><div><p>Reject</p><h6>{{ $currentPeriodForms->where('status', 'Rejected')->count() }}</h6></div><i class="fas fa-ban text-danger fa-xs"></i></div></div></div>
|
|
|
|
|
<div class="col"><div class="status-card" data-status="Closed"><div class="mini-stat-box"><div><p>Closed</p><h6>{{ $currentPeriodForms->where('status', 'Closed')->count() }}</h6></div><i class="fas fa-lock text-secondary fa-xs"></i></div></div></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="content">
|
2024-12-19 10:51:27 +07:00
|
|
|
<div class="container-fluid">
|
|
|
|
|
<div class="card card-primary card-outline">
|
|
|
|
|
<div class="card-body">
|
2026-06-01 15:01:03 +07:00
|
|
|
<h4 class="header-title float-left">List Data Forms Meeting Seminar Anda</h4>
|
2024-12-19 10:51:27 +07:00
|
|
|
<p class="float-right mb-2">
|
2026-06-01 15:01:03 +07:00
|
|
|
@if (auth()->user()->can('forms.meeting.create'))
|
|
|
|
|
<a class="btn btn-primary text-white" href="{{ route('forms.meeting.create') }}">Add New Expense</a>
|
|
|
|
|
@endif
|
2024-12-19 10:51:27 +07:00
|
|
|
</p>
|
|
|
|
|
<div class="clearfix"></div>
|
|
|
|
|
<div class="dataTables_wrapper dt-bootstrap4 mt-2">
|
|
|
|
|
@include('backend.layouts.partials.messages')
|
2026-06-01 15:01:03 +07:00
|
|
|
<div class="table-responsive">
|
|
|
|
|
<table id="dataTable" class="table table-bordered table-striped table-head-fixed dataTable dtr-inline" style="width:100%">
|
|
|
|
|
<thead class="bg-light text-capitalize">
|
|
|
|
|
<tr>
|
|
|
|
|
<th>#</th>
|
|
|
|
|
<th>No Expense</th>
|
|
|
|
|
<th>Nama</th>
|
|
|
|
|
<th>Cabang</th>
|
|
|
|
|
<th>Tanggal</th>
|
|
|
|
|
<th>Allowance</th>
|
|
|
|
|
<th>Transport Ankot</th>
|
|
|
|
|
<th>Hotel</th>
|
|
|
|
|
<th>Total</th>
|
|
|
|
|
<th>Total Approved</th>
|
|
|
|
|
<th>Account Number</th>
|
|
|
|
|
<th>Status</th>
|
|
|
|
|
@if (auth()->user()->can('approval.view')) <th>Approval 1</th> @endif
|
|
|
|
|
@if (auth()->user()->can('approval2.view')) <th>Approval 2</th> @endif
|
|
|
|
|
@if (auth()->user()->can('final_approval.view')) <th>Final Approval</th> @endif
|
|
|
|
|
<th>Aksi</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
@php
|
|
|
|
|
use App\Helpers\NextCloudHelper;
|
|
|
|
|
$role = auth()->user()->getRoleNames()[0];
|
|
|
|
|
@endphp
|
|
|
|
|
@foreach ($forms as $item)
|
|
|
|
|
<tr data-id="{{ $item->id }}" data-status="{{ $item->status }}" data-date="{{ \Carbon\Carbon::parse($item->tanggal)->format('Y-m-d') }}" data-outstanding="{{ $outstandingIdCollection->contains($item->id) ? '1' : '0' }}">
|
|
|
|
|
<td>{{ $loop->iteration }}</td>
|
|
|
|
|
<td>{{ $item->expense_number }}</td>
|
|
|
|
|
<td>{{ $item->user->name }}</td>
|
|
|
|
|
<td>{{ $item->user->cabang->cabang->name }}</td>
|
|
|
|
|
<td>{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('dddd, D MMMM Y HH:mm:ss') }}</td>
|
|
|
|
|
<td>{{ number_format($item->allowance, 0, ',', '.') }}</td>
|
|
|
|
|
<td>{{ number_format($item->transport_ankot, 0, ',', '.') }}</td>
|
|
|
|
|
<td>{{ number_format($item->hotel, 0, ',', '.') }}</td>
|
|
|
|
|
<td>{{ number_format($item->total, 0, ',', '.') }}</td>
|
|
|
|
|
<td>{{ number_format($item->approved_total, 0, ',', '.') }}</td>
|
|
|
|
|
<td>{{ $item->account_number }}</td>
|
|
|
|
|
<td>
|
|
|
|
|
@if ($item->status == 'On Progress')
|
|
|
|
|
<span class="badge badge-warning text-white">{{ $item->status }}</span>
|
|
|
|
|
@elseif (in_array($item->status, ['Approved 1', 'Approved 2', 'Final Approved']))
|
|
|
|
|
<span class="badge badge-success text-white">{{ $item->status }}</span>
|
|
|
|
|
@else
|
|
|
|
|
<span class="badge badge-danger text-white">{{ $item->status }}</span>
|
|
|
|
|
@endif
|
|
|
|
|
</td>
|
|
|
|
|
|
|
|
|
|
{{-- SELEKTIF MENYEMBUNYIKAN TOMBOL APPROVE/REJECT DIRECT DI INDEX TANPA MENGHAPUS LOGIKA KODE --}}
|
|
|
|
|
@if (auth()->user()->can('approval.view'))
|
|
|
|
|
<td>
|
|
|
|
|
<div class="d-none">
|
|
|
|
|
@if (auth()->user()->can('approval.approve') && $item->status == 'On Progress')
|
|
|
|
|
<button type="button" class="btn btn-success btn-sm final-approve-modal" data-id="{{ route('forms.meeting.approve', $item->id) }}">Approve 1</button>
|
|
|
|
|
<button type="button" class="btn btn-danger btn-sm open-reject-modal" data-action="{{ route('forms.meeting.reject', $item->id) }}">Reject</button>
|
|
|
|
|
@endif
|
|
|
|
|
</div>
|
|
|
|
|
@if ($item->approved_at) <br><small>{{ \Carbon\Carbon::parse($item->approved_at)->format('d/m/y H:i') }}</small> @endif
|
|
|
|
|
</td>
|
|
|
|
|
@endif
|
|
|
|
|
@if (auth()->user()->can('approval2.view'))
|
|
|
|
|
<td>
|
|
|
|
|
<div class="d-none">
|
|
|
|
|
@if (auth()->user()->can('approval2.approve') && $item->status == 'Approved 1' && $item->approved_at)
|
|
|
|
|
<button type="button" class="btn btn-success btn-sm open-approve-modal" data-id="{{ route('forms.meeting.approve2', $item->id) }}">Approve 2</button>
|
|
|
|
|
<button type="button" class="btn btn-danger btn-sm open-reject-modal" data-action="{{ route('forms.meeting.reject', $item->id) }}">Reject</button>
|
|
|
|
|
@endif
|
|
|
|
|
</div>
|
|
|
|
|
@if ($item->approved2_at) <br><small>{{ \Carbon\Carbon::parse($item->approved2_at)->format('d/m/y H:i') }}</small> @endif
|
|
|
|
|
</td>
|
|
|
|
|
@endif
|
|
|
|
|
@if(auth()->user()->can('final_approval.view'))
|
|
|
|
|
<td>
|
|
|
|
|
<div class="d-none">
|
|
|
|
|
@if(auth()->user()->can('final_approval.approve'))
|
|
|
|
|
@if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at)
|
|
|
|
|
<button type="button" class="btn btn-success btn-sm open-approve-modal" data-id="{{ route('forms.meeting.final-approve', $item->id) }}">Final Approve</button>
|
|
|
|
|
<button type="button" class="btn btn-danger btn-sm open-reject-modal" data-action="{{ route('forms.meeting.reject', $item->id) }}">Reject</button>
|
|
|
|
|
@elseif ($item->final_approved_at && auth()->user()->can('final_approval.open'))
|
|
|
|
|
<form action="{{ route('forms.meeting.open', $item->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Confirm Open?');">
|
|
|
|
|
@csrf @method('PUT') <button type="submit" class="btn btn-success btn-sm">Open</button>
|
|
|
|
|
</form>
|
|
|
|
|
@endif
|
|
|
|
|
@endif
|
|
|
|
|
</div>
|
|
|
|
|
@if ($item->final_approved_at) <br><small>{{ \Carbon\Carbon::parse($item->final_approved_at)->format('d/m/y H:i') }}</small> @endif
|
|
|
|
|
</td>
|
|
|
|
|
@endif
|
|
|
|
|
|
|
|
|
|
<td>
|
|
|
|
|
@if (auth()->user()->can('forms.meeting.view'))
|
|
|
|
|
<a href="{{ route('forms.meeting.view', $item->id) }}" class="btn btn-primary btn-sm"><i class="fas fa-eye text-white"></i></a>
|
|
|
|
|
@endif
|
|
|
|
|
@php
|
|
|
|
|
$canEdit = auth()->user()->can('forms.meeting.edit') && (!in_array($role, ['Medical Representatif', 'Area Manager Cabang']) || $item->status == 'On Progress');
|
|
|
|
|
@endphp
|
|
|
|
|
@if ($canEdit)
|
|
|
|
|
<a href="{{ route('forms.meeting.edit', $item->id) }}" class="btn btn-warning btn-sm"><i class="fas fa-edit text-white"></i></a>
|
|
|
|
|
@endif
|
|
|
|
|
@if (($role != 'Medical Representatif' && auth()->user()->can('forms.meeting.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress'))
|
|
|
|
|
<form action="{{ route('forms.meeting.destroy', $item->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Delete?');">
|
|
|
|
|
@csrf @method('DELETE') <button type="submit" class="btn btn-danger btn-sm"><i class="fas fa-trash"></i></button>
|
|
|
|
|
</form>
|
|
|
|
|
@endif
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
@endforeach
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-12-19 10:51:27 +07:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
2026-06-01 15:01:03 +07:00
|
|
|
|
|
|
|
|
{{-- MODAL CONTAINER DI BAWAH (TETAP UTUH UNTUK KEBUTUHAN ROUTING AJAX) --}}
|
|
|
|
|
<div class="modal fade" id="approveModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
|
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
|
|
|
<div class="modal-content">
|
|
|
|
|
<form id="approveForm" method="POST" action="">
|
|
|
|
|
@csrf @method('PUT')
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
<h5 class="modal-title" id="approveModalLabel">Approve Item</h5>
|
|
|
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
<div id="loadingSpinner" class="d-flex justify-content-center align-items-center" style="height: 200px;"><div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div></div>
|
|
|
|
|
<div id="modalContent" class="d-none">
|
|
|
|
|
<p>Please review the details below:</p>
|
|
|
|
|
<ul class="list-group">
|
|
|
|
|
<li class="list-group-item"><strong>Expense Number:</strong> <span id="expense_number"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>User:</strong> <span id="user"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>Tanggal:</strong> <span id="tanggal"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>Allowance:</strong> <span id="allowance_value"></span> (<span id="bukti_allowance"></span>)</li>
|
|
|
|
|
<li class="list-group-item"><strong>Transport Ankot:</strong> <span id="transport_ankot_value"></span> (<span id="bukti_transport_ankot"></span>)</li>
|
|
|
|
|
<li class="list-group-item"><strong>Hotel:</strong> <span id="hotel_value"></span> (<span id="bukti_hotel"></span>)</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="submit" class="btn btn-primary">Approve</button></div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="modal fade" id="finalApproveModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
|
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
|
|
|
<div class="modal-content">
|
|
|
|
|
<form id="finalApproveForm" method="POST" action="">
|
|
|
|
|
@csrf @method('PUT')
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
<h5 class="modal-title" id="finalApproveModalLabel">Final Approve Item</h5>
|
|
|
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
<div id="finalLoadingSpinner" class="d-flex justify-content-center align-items-center" style="height: 200px;"><div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div></div>
|
|
|
|
|
<div id="finalModalContent" class="d-none">
|
|
|
|
|
<p>Please review the details below:</p>
|
|
|
|
|
<ul class="list-group">
|
|
|
|
|
<li class="list-group-item"><strong>Expense:</strong> <span id="final_expense_number"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>User:</strong> <span id="final_user"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>Tanggal:</strong> <span id="final_tanggal"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>Bukti Allowance:</strong> <span id="final_bukti_allowance"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>Bukti Transport:</strong> <span id="final_bukti_transport_ankot"></span></li>
|
|
|
|
|
<li class="list-group-item"><strong>Bukti Hotel:</strong> <span id="final_bukti_hotel"></span></li>
|
|
|
|
|
</ul>
|
|
|
|
|
<div class="custom-control custom-checkbox mb-2"><input type="checkbox" class="custom-control-input" id="final_allowance" name="allowance"><label class="custom-control-label" for="final_allowance">Allowance (<span id="final_allowance_value"></span>)</label></div>
|
|
|
|
|
<div class="custom-control custom-checkbox mb-2"><input type="checkbox" class="custom-control-input" id="final_transport_ankot" name="transport_ankot"><label class="custom-control-label" for="final_transport_ankot">Transport Ankot (<span id="final_transport_ankot_value"></span>)</label></div>
|
|
|
|
|
<div class="custom-control custom-checkbox mb-3"><input type="checkbox" class="custom-control-input" id="final_hotel" name="hotel"><label class="custom-control-label" for="final_hotel">Hotel (<span id="final_hotel_value"></span>)</label></div>
|
|
|
|
|
<div class="form-group"><label>Total Approved</label><input type="text" class="form-control font-weight-bold" id="total" name="total" value="0" readonly></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="submit" class="btn btn-primary">Approve</button></div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="modal fade" id="rejectModal" tabindex="-1" role="dialog" aria-hidden="true">
|
|
|
|
|
<div class="modal-dialog" role="document">
|
|
|
|
|
<div class="modal-content">
|
|
|
|
|
<form id="rejectForm" method="POST" action="">
|
|
|
|
|
@csrf @method('PUT')
|
|
|
|
|
<div class="modal-header">
|
|
|
|
|
<h5 class="modal-title" id="rejectModalLabel">Reject Expense</h5>
|
|
|
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-body">
|
|
|
|
|
<div class="form-group"><label>Remarks <span class="text-danger">*</span></label><textarea class="form-control" name="remarks" id="rejectRemarks" rows="3" required></textarea></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button><button type="submit" class="btn btn-danger">Reject</button></div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2024-12-19 10:51:27 +07:00
|
|
|
@endsection
|
2024-12-24 11:40:49 +07:00
|
|
|
|
|
|
|
|
@section('scripts')
|
2026-06-01 15:01:03 +07:00
|
|
|
<script>
|
|
|
|
|
$(document).ready(function () {
|
|
|
|
|
// Uncheck all checkboxes on page refresh
|
|
|
|
|
$('.custom-control-input').prop('checked', false);
|
|
|
|
|
|
|
|
|
|
if ($('#dataTable').length) {
|
|
|
|
|
let outstandingFilterActive = false;
|
|
|
|
|
let statusFilterActive = 'all';
|
|
|
|
|
|
|
|
|
|
// Custom Search DataTables (Filter Sinkron: Status + Outstanding + Siklus Periode Berjalan 11 - 13)
|
|
|
|
|
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
|
|
|
|
|
const row = $(settings.aoData[dataIndex].nTr);
|
|
|
|
|
const rowStatus = row.attr('data-status');
|
|
|
|
|
const isOutstanding = row.attr('data-outstanding') === '1' || row.attr('data-outstanding') === 1;
|
|
|
|
|
const rowDateStr = row.attr('data-date'); // Format: YYYY-MM-DD
|
|
|
|
|
|
|
|
|
|
// 1. Prioritas Pertama: Outstanding Filter Murni
|
|
|
|
|
if (outstandingFilterActive) {
|
|
|
|
|
return isOutstanding;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Prioritas Kedua: Aturan Jendela Waktu (Cut-off) Berlaku MUTLAK Untuk Semua Mini Card
|
|
|
|
|
if (rowDateStr) {
|
|
|
|
|
const dateParts = rowDateStr.split('-');
|
|
|
|
|
const rowDate = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 12, 0, 0);
|
|
|
|
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
let startPeriod, endPeriod;
|
|
|
|
|
const startDay = 11;
|
|
|
|
|
const closingDay = 13;
|
|
|
|
|
|
|
|
|
|
if (now.getDate() >= startDay) {
|
|
|
|
|
startPeriod = new Date(now.getFullYear(), now.getMonth(), startDay, 0, 0, 0);
|
|
|
|
|
endPeriod = new Date(now.getFullYear(), now.getMonth() + 1, closingDay, 23, 59, 59);
|
|
|
|
|
} else {
|
|
|
|
|
startPeriod = new Date(now.getFullYear(), now.getMonth() - 1, startDay, 0, 0, 0);
|
|
|
|
|
endPeriod = new Date(now.getFullYear(), now.getMonth(), closingDay, 23, 59, 59);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TENGGELAMKAN ROW jika tidak berada di dalam siklus berjalan
|
|
|
|
|
if (rowDate < startPeriod || rowDate > endPeriod) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Prioritas Ketiga: Filter Status Setelah Melewati Karantina Tanggal
|
|
|
|
|
if (statusFilterActive !== 'all' && rowStatus !== statusFilterActive) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const table = $('#dataTable').DataTable({
|
|
|
|
|
responsive: false, dom: 'Blfrtip',
|
|
|
|
|
lengthMenu: [ [10, 50, 100, -1], [10, 50, 100, "All"] ],
|
|
|
|
|
buttons: [ { extend: 'excel', text: 'Excel' }, { extend: 'pdf', text: 'PDF', orientation: 'landscape' }, 'colvis' ]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('.status-card').on('click', function() {
|
|
|
|
|
outstandingFilterActive = false;
|
|
|
|
|
$('#outstandingCard').removeClass('active');
|
|
|
|
|
$('#outstandingCard').find('.filter-hint small').text('Klik untuk detail');
|
|
|
|
|
|
|
|
|
|
$('.status-card').removeClass('active-filter');
|
|
|
|
|
$(this).addClass('active-filter');
|
|
|
|
|
statusFilterActive = $(this).data('status');
|
|
|
|
|
table.draw();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const $outstandingCard = $('#outstandingCard');
|
|
|
|
|
$outstandingCard.on('click', function () {
|
|
|
|
|
outstandingFilterActive = !outstandingFilterActive;
|
|
|
|
|
$(this).toggleClass('active', outstandingFilterActive);
|
|
|
|
|
|
|
|
|
|
if(outstandingFilterActive) {
|
|
|
|
|
$('.status-card').removeClass('active-filter');
|
|
|
|
|
$('.status-card[data-status="all"]').addClass('active-filter');
|
|
|
|
|
statusFilterActive = 'all';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.draw();
|
|
|
|
|
$(this).find('.filter-hint small').text(outstandingFilterActive ? 'Tampilkan semua data' : 'Klik untuk detail');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$(document).on('click', '.open-reject-modal', function () {
|
|
|
|
|
$('#rejectForm').attr('action', $(this).data('action'));
|
|
|
|
|
$('#rejectRemarks').val('').focus();
|
|
|
|
|
$('#rejectModal').modal('show');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(document).on('click', '.open-approve-modal', function() {
|
|
|
|
|
const url = $(this).data('id');
|
|
|
|
|
$('#loadingSpinner').show(); $('#modalContent').addClass('d-none');
|
|
|
|
|
$('#approveForm').attr('action', url); $('#approveModal').modal('show');
|
|
|
|
|
$.get(url.replace(/approve2|final-approve/g, 'detail'), function (data) {
|
|
|
|
|
const fmt = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 });
|
|
|
|
|
$('#allowance_value').text(fmt.format(data.allowance));
|
|
|
|
|
$('#transport_ankot_value').text(fmt.format(data.transport_ankot));
|
|
|
|
|
$('#hotel_value').text(fmt.format(data.hotel));
|
|
|
|
|
$('#user').text(data.user.name); $('#expense_number').text(data.expense_number);
|
|
|
|
|
$('#tanggal').text(new Date(data.tanggal).toLocaleDateString('id-ID', {year:'numeric', month:'long', day:'numeric'}));
|
|
|
|
|
$('#bukti_allowance').html(data.bukti_allowance ? '<a href="'+data.bukti_allowance+'" target="_blank">View</a>' : '-');
|
|
|
|
|
$('#bukti_transport_ankot').html(data.bukti_transport_ankot ? '<a href="'+data.bukti_transport_ankot+'" target="_blank">View</a>' : '-');
|
|
|
|
|
$('#bukti_hotel').html(data.bukti_hotel ? '<a href="'+data.bukti_hotel+'" target="_blank">View</a>' : '-');
|
|
|
|
|
$('#loadingSpinner').hide(); $('#modalContent').removeClass('d-none');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(document).on('click', '.final-approve-modal', function() {
|
|
|
|
|
const url = $(this).data('id');
|
|
|
|
|
$('#finalLoadingSpinner').show(); $('#finalModalContent').addClass('d-none');
|
|
|
|
|
$('#finalApproveForm').attr('action', url); $('#finalApproveModal').modal('show');
|
|
|
|
|
$.get(url.replace('approve', 'detail'), function (data) {
|
|
|
|
|
const fmt = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 });
|
|
|
|
|
$('#final_allowance_value').text(fmt.format(data.allowance));
|
|
|
|
|
$('#final_transport_ankot_value').text(fmt.format(data.transport_ankot));
|
|
|
|
|
$('#final_hotel_value').text(fmt.format(data.hotel));
|
|
|
|
|
$('#final_allowance').data('value', data.allowance);
|
|
|
|
|
$('#final_transport_ankot').data('value', data.transport_ankot);
|
|
|
|
|
$('#final_hotel').data('value', data.hotel);
|
|
|
|
|
$('#final_user').text(data.user.name); $('#final_expense_number').text(data.expense_number);
|
|
|
|
|
$('#final_tanggal').text(new Date(data.tanggal).toLocaleDateString('id-ID', {year:'numeric', month:'long', day:'numeric'}));
|
|
|
|
|
$('#final_bukti_allowance').html(data.bukti_allowance ? '<a href="'+data.bukti_allowance+'" target="_blank">View</a>' : '-');
|
|
|
|
|
$('#final_bukti_transport_ankot').html(data.bukti_transport_ankot ? '<a href="'+data.bukti_transport_ankot+'" target="_blank">View</a>' : '-');
|
|
|
|
|
$('#final_bukti_hotel').html(data.bukti_hotel ? '<a href="'+data.bukti_hotel+'" target="_blank">View</a>' : '-');
|
|
|
|
|
$('#finalLoadingSpinner').hide(); $('#finalModalContent').removeClass('d-none');
|
|
|
|
|
$('#total').val(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('.custom-control-input').on('change', function () {
|
|
|
|
|
let total = 0;
|
|
|
|
|
$('.custom-control-input:checked').each(function () { total += parseFloat($(this).data('value') || 0); });
|
|
|
|
|
const fmt = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 });
|
|
|
|
|
$('#total').val(fmt.format(total));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('#finalApproveModal').on('hidden.bs.modal', function () {
|
|
|
|
|
$('.custom-control-input').prop('checked', false); $('#total').val('0');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
@endsection
|