Commit ac889b9d by Hannah Zahra

Fourth Update

parent 5961a3ce
......@@ -2,7 +2,8 @@
@extends('ui::layouts.app')
@push('style')
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.3/semantic.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/components/calendar.min.css">
<style type="text/css">
body { background-color: #f0f2f5; }
......@@ -71,10 +72,29 @@
color: white; border-radius: 10px;
padding: 10px 20px; font-weight: 600;
}
.ui.calendar .disabled.day {
color: #ccc !important;
text-decoration: line-through;
}
</style>
@endpush
@section('content')
@if (session('error'))
<div class="ui negative message">
<i class="close icon"></i>
<div class="header">{{ session('error') }}</div>
</div>
@endif
@if (session('success'))
<div class="ui positive message">
<i class="close icon"></i>
<div class="header">{{ session('success') }}</div>
</div>
@endif
<div class="ui container">
<div class="ui segments">
<div class="ui segment">
......@@ -179,9 +199,10 @@
<small>(Jumlah keseluruhan sebenar akan dikira di Senarai Tuntutan)</small>
</td>
<td>
<strong id="total_claim">RM 0.00</strong>
<strong id="total_claim_amount">RM 0.00</strong>
</td>
</tr> </tbody>
</tr>
</tbody>
</table>
</div>
</div>
......@@ -199,11 +220,49 @@
@push('script')
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.3/semantic.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/components/calendar.min.js"></script>
<script>
$(document).ready(function() {
// --- Modal on load ---
// === Auto Calculation for Ringkasan Pengiraan ===
function updateSummary() {
const totalMileage = parseFloat($('#total_mileage').val()) || 0;
const toll = parseFloat($('#toll_amount').val()) || 0;
const others = parseFloat($('#others_amount').val()) || 0;
// Apply 40km daily deduction rule
let claimableMileage = 0;
if (totalMileage > 40) {
claimableMileage = totalMileage - 40;
}
// Mileage rate logic (RM0.55/km up to 500km, RM0.50 after that)
let rate = 0.55;
if (claimableMileage > 500) rate = 0.50;
// Total mileage claim
const mileageAmount = claimableMileage * rate;
// Total claim = mileage + toll + others
const totalClaim = mileageAmount + toll + others;
// Update display fields
$('#claimable_mileage_formula').text(`${totalMileage} - 40 = ${claimableMileage} KM`);
$('#toll_claim').text(`RM ${toll.toFixed(2)}`);
$('#others_claim').text(`RM ${others.toFixed(2)}`);
$('#total_claim_amount').text(`RM ${totalClaim.toFixed(2)}`);
// Store hidden claimable mileage input
$('#claimable_mileage_input').val(claimableMileage);
}
// Watch for input changes
$('#total_mileage, #toll_amount, #others_amount').on('input', updateSummary);
updateSummary(); // run once on page load
// === Modal Setup ===
const modalHtml = `
<div class="ui basic modal" id="supportingDocsModal">
<div class="ui icon header">
......@@ -215,131 +274,50 @@
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.</p>
</div>
<div class="actions" style="text-align:center;">
<div class="ui green ok inverted button"><i class="checkmark icon"></i> Saya Faham</div>
<div class="ui green ok inverted button">
<i class="checkmark icon"></i> Saya Faham
</div>
</div>
</div>`;
$('body').append(modalHtml);
// Disable form until user clicks "Saya Faham"
$('#claimForm :input').prop('disabled', true);
// Show modal
$('#supportingDocsModal').modal({
closable: false,
onApprove: function() {
$('#claimForm :input').prop('disabled', false);
$('.ui.dropdown').dropdown();
// Wait for modal to close before initializing calendar
setTimeout(() => {
console.log('🟢 Initializing Fomantic calendar...');
$('#claim_date').calendar({
type: 'date',
startMode: 'day',
touchReadonly: true,
monthFirst: false,
maxDate: new Date(),
disabledDates: [],
formatter: {
date: function(date) {
if (!date) return '';
let d = date.getDate();
let m = date.getMonth() + 1;
let y = date.getFullYear();
return `${y}-${m<10?'0':''}${m}-${d<10?'0':''}${d}`;
const y = date.getFullYear();
const m = ('0' + (date.getMonth() + 1)).slice(-2);
const d = ('0' + date.getDate()).slice(-2);
return `${y}-${m}-${d}`;
}
},
onChange: function(date, text) {
console.log('📅 Date selected:', text);
}
});
}, 400);
}
}).modal('show');
// --- Calculation ---
function calculateTotals() {
const mileage = parseFloat($('#total_mileage').val()) || 0;
const claimable = Math.max(0, mileage - 40);
const toll = parseFloat($('#toll_amount').val()) || 0;
const others = parseFloat($('#others_amount').val()) || 0;
const total = toll + others;
$('#claimable_mileage_formula').text(`${mileage.toFixed(2)} - 40 = ${claimable.toFixed(2)} KM`);
$('#toll_claim').text(`RM ${toll.toFixed(2)}`);
$('#others_claim').text(`RM ${others.toFixed(2)}`);
$('#total_claim').text(`RM ${total.toFixed(2)}`);
$('#claimable_mileage_input').val(claimable.toFixed(2));
}
$('#total_mileage, #toll_amount, #others_amount').on('input', function() {
calculateTotals();
const othersVal = parseFloat($('#others_amount').val()) || 0;
if (othersVal > 0) {
$('#others_description_wrapper').slideDown();
$('#others_description').prop('required', true);
} else {
$('#others_description_wrapper').slideUp();
$('#others_description').prop('required', false).val('');
}
});
// --- summary Modal ---
$('#claimForm').on('submit', function(e) {
e.preventDefault();
// ✅ Project validation check
const projectId = $('#project_id').val();
if (!projectId || projectId === "") {
$('body').toast({
class: 'error',
message: 'Sila pilih projek sebelum menghantar tuntutan.',
showProgress: 'bottom'
});
$('#project_id').closest('.field').addClass('error');
return false;
} else {
$('#project_id').closest('.field').removeClass('error');
}
const project = $('#project_id option:selected').text();
const claimDate = $('#date').val();
const vehicle = $('#jenis_kenderaan_id option:selected').text();
const desc = $('input[name="description"]').val();
const from = $('select[name="distance_from"] option:selected').text();
const to = $('input[name="distance_to"]').val();
const mileage = $('#total_mileage').val();
const claimable = $('#claimable_mileage_input').val();
const toll = $('#toll_amount').val() || 0;
const others = $('#others_amount').val() || 0;
const othersDesc = $('#others_description').val() || '-';
// Remove previous modal if any
$('#claimSummaryModal').remove();
const summaryModal = `
<div class="ui modal" id="claimSummaryModal">
<div class="header">Ringkasan Tuntutan Anda</div>
<div class="content">
<p><strong>Tarikh:</strong> ${claimDate}</p>
<p><strong>Jenis Kenderaan:</strong> ${vehicle}</p>
<p><strong>Nama Projek:</strong> ${project}</p>
<p><strong>Butiran:</strong> ${desc}</p>
<p><strong>Dari:</strong> ${from}</p>
<p><strong>Ke:</strong> ${to}</p>
<p><strong>Jumlah Perbatuan:</strong> ${mileage} KM</p>
<p><strong>Layak Dituntut:</strong> ${mileage} - 40 = ${claimable} KM</p>
<p><strong>Toll:</strong> RM ${parseFloat(toll).toFixed(2)}</p>
<p><strong>Lain-lain:</strong> RM ${parseFloat(others).toFixed(2)}</p>
<p><strong>Butiran Lain-lain:</strong> ${othersDesc}</p>
</div>
<div class="actions">
<div class="ui grey button" id="editClaim"><i class="arrow left icon"></i> Edit</div>
<div class="ui green approve button"><i class="check icon"></i> Hantar</div>
</div>
</div>`;
$('body').append(summaryModal);
$('#claimSummaryModal').modal({
closable: false,
onApprove: function() {
$('#claimForm')[0].submit();
return false;
}
}).modal('show');
$(document).off('click', '#editClaim').on('click', '#editClaim', function() {
$('#claimSummaryModal').modal('hide');
});
});
});
</script>
@endpush
......@@ -2,7 +2,8 @@
@extends('ui::layouts.app')
@push('style')
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.3/semantic.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/components/calendar.min.css">
<style type="text/css">
body { background-color: #f0f2f5; }
......@@ -71,10 +72,28 @@
color: white; border-radius: 10px;
padding: 10px 20px; font-weight: 600;
}
.ui.calendar .disabled.day {
color: #ccc !important;
text-decoration: line-through;
}
</style>
@endpush
@section('content')
@if (session('error'))
<div class="ui negative message">
<i class="close icon"></i>
<div class="header">{{ session('error') }}</div>
</div>
@endif
@if (session('success'))
<div class="ui positive message">
<i class="close icon"></i>
<div class="header">{{ session('success') }}</div>
</div>
@endif
<div class="ui container">
<div class="ui segments">
<div class="ui segment">
......@@ -90,6 +109,7 @@
<p><strong>Maklumat Tuntutan</strong></p>
<div class="ui segment">
<div class="ui form">
<div class="inline field required">
<label class="three-wide field">Tarikh</label>
<div class="eight wide field ui calendar" id="claim_date">
......@@ -111,13 +131,13 @@
</div>
<div class="inline field required">
<label class="three-wide field">Butiran Tuntutan</label>
<label class="three-wide field">Butiran</label>
<input class="eight wide field" name="description" type="text" placeholder="Cth: Mesyuarat Dalaman" required>
</div>
<div class="inline field required">
<label class="three-wide field">Jarak Dari</label>
<select class="ui search dropdown eight wide field" name="distance_from">
<select class="ui search dropdown eight wide field" name="distance_from" required>
<option value="">Sila Pilih Lokasi</option>
<option value="Pejabat">Pejabat</option>
</select>
......@@ -166,7 +186,7 @@
<small>(Jumlah keseluruhan sebenar akan dikira di Senarai Tuntutan)</small>
</td>
<td>
<strong id="total_claim">RM 0.00</strong>
<strong id="total_claim_amount">RM 0.00</strong>
</td>
</tr>
</tbody>
......@@ -187,11 +207,38 @@
@push('script')
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.3/semantic.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/semantic.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.3/dist/components/calendar.min.js"></script>
<script>
$(document).ready(function() {
// --- Warning Modal on load ---
// === Auto Calculation for Ringkasan Pengiraan ===
function updateSummary() {
const totalMileage = parseFloat($('#total_mileage').val()) || 0;
const toll = parseFloat($('#toll_amount').val()) || 0;
const others = parseFloat($('#others_amount').val()) || 0;
let claimableMileage = 0;
if (totalMileage > 40) claimableMileage = totalMileage - 40;
let rate = 0.55;
if (claimableMileage > 500) rate = 0.50;
const mileageAmount = claimableMileage * rate;
const totalClaim = mileageAmount + toll + others;
$('#claimable_mileage_formula').text(`${totalMileage} - 40 = ${claimableMileage} KM`);
$('#toll_claim').text(`RM ${toll.toFixed(2)}`);
$('#others_claim').text(`RM ${others.toFixed(2)}`);
$('#total_claim_amount').text(`RM ${totalClaim.toFixed(2)}`);
$('#claimable_mileage_input').val(claimableMileage);
}
$('#total_mileage, #toll_amount, #others_amount').on('input', updateSummary);
updateSummary();
// === Modal Setup ===
const modalHtml = `
<div class="ui basic modal" id="supportingDocsModal">
<div class="ui icon header">
......@@ -199,10 +246,8 @@
Peringatan Penting
</div>
<div class="content" style="text-align:center;">
<p style="font-size:16px; line-height:1.5;">
Dokumen sokongan seperti resit mesti dihantar kepada pihak Finance sebagai bukti.<br>
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.
</p>
<p>Dokumen sokongan seperti resit mesti dihantar kepada pihak Finance.<br>
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.</p>
</div>
<div class="actions" style="text-align:center;">
<div class="ui green ok inverted button">
......@@ -212,7 +257,6 @@
</div>`;
$('body').append(modalHtml);
// Disable inputs until user acknowledges
$('#claimForm :input').prop('disabled', true);
$('#supportingDocsModal').modal({
......@@ -220,103 +264,29 @@
onApprove: function() {
$('#claimForm :input').prop('disabled', false);
$('.ui.dropdown').dropdown();
setTimeout(() => {
$('#claim_date').calendar({
type: 'date',
startMode: 'day',
touchReadonly: true,
monthFirst: false,
maxDate: new Date(),
disabledDates: [],
formatter: {
date: function(date) {
if (!date) return '';
let d = date.getDate();
let m = date.getMonth() + 1;
let y = date.getFullYear();
return `${y}-${m<10?'0':''}${m}-${d<10?'0':''}${d}`;
}
}
});
const y = date.getFullYear();
const m = ('0' + (date.getMonth() + 1)).slice(-2);
const d = ('0' + date.getDate()).slice(-2);
return `${y}-${m}-${d}`;
}
}).modal('show');
// --- Calculation ---
function calculateTotals() {
const mileage = parseFloat($('#total_mileage').val()) || 0;
const claimable = Math.max(0, mileage - 40);
const toll = parseFloat($('#toll_amount').val()) || 0;
const others = parseFloat($('#others_amount').val()) || 0;
const total = toll + others;
$('#claimable_mileage_formula').text(`${mileage.toFixed(2)} - 40 = ${claimable.toFixed(2)} KM`);
$('#toll_claim').text(`RM ${toll.toFixed(2)}`);
$('#others_claim').text(`RM ${others.toFixed(2)}`);
$('#total_claim').text(`RM ${total.toFixed(2)}`);
$('#claimable_mileage_input').val(claimable.toFixed(2));
}
$('#total_mileage, #toll_amount, #others_amount').on('input', function() {
calculateTotals();
const othersVal = parseFloat($('#others_amount').val()) || 0;
if (othersVal > 0) {
$('#others_description_wrapper').slideDown();
$('#others_description').prop('required', true);
} else {
$('#others_description_wrapper').slideUp();
$('#others_description').prop('required', false).val('');
}
});
// --- Summary Modal ---
$('#claimForm').on('submit', function(e) {
e.preventDefault();
const claimDate = $('#date').val();
const vehicle = $('#jenis_kenderaan_id option:selected').text();
const desc = $('input[name="description"]').val();
const from = $('select[name="distance_from"] option:selected').text();
const to = $('input[name="distance_to"]').val();
const mileage = $('#total_mileage').val();
const claimable = $('#claimable_mileage_input').val();
const toll = $('#toll_amount').val() || 0;
const others = $('#others_amount').val() || 0;
const othersDesc = $('#others_description').val() || '-';
const subtotal = $('#total_claim').text() || '-';
$('#claimSummaryModal').remove();
const summaryModal = `
<div class="ui modal" id="claimSummaryModal">
<div class="header">Ringkasan Tuntutan Anda</div>
<div class="content">
<p><strong>Tarikh:</strong> ${claimDate}</p>
<p><strong>Jenis Kenderaan:</strong> ${vehicle}</p>
<p><strong>Butiran:</strong> ${desc}</p>
<p><strong>Dari:</strong> ${from}</p>
<p><strong>Ke:</strong> ${to}</p>
<p><strong>Jumlah Perbatuan:</strong> ${mileage} KM</p>
<p><strong>Layak Dituntut:</strong> ${mileage} - 40 = ${claimable} KM</p>
<p><strong>Toll:</strong> RM ${parseFloat(toll).toFixed(2)}</p>
<p><strong>Lain-lain:</strong> RM ${parseFloat(others).toFixed(2)}</p>
<p><strong>Butiran Lain-lain:</strong> ${othersDesc}</p>
<p><strong>Jumlah Tol dan Lain-lain (Jumlah Keseluruhan akan Dikira di Senarai Tuntutan):</strong> ${subtotal}</p>
</div>
<div class="actions">
<div class="ui grey button" id="editClaim"><i class="arrow left icon"></i> Edit</div>
<div class="ui green approve button"><i class="check icon"></i> Hantar</div>
</div>
</div>`;
$('body').append(summaryModal);
$('#claimSummaryModal').modal({
closable: false,
onApprove: function() {
$('#claimForm')[0].submit();
return false;
}, 400);
}
}).modal('show');
$(document).off('click', '#editClaim').on('click', '#editClaim', function() {
$('#claimSummaryModal').modal('hide');
});
});
});
</script>
@endpush
......@@ -138,6 +138,7 @@
<br>
@else
<div class="ui message">Tiada tuntutan untuk projek ini bulan ini.</div>
<br>
@endif
</div>
@empty
......
......@@ -21,8 +21,8 @@ function () {
Route::prefix('applyform')->name('applyform')->group(function () {
Route::get('/', [ApplyformController::class, 'index'])->name('.index');
Route::get('/noproject', [ApplyformController::class, 'noproject'])->name('.noproject');
Route::get('/applyform/getDates', [ApplyformController::class, 'getDates'])->name('.getDates');
});
// Route::resource('applyform', ApplyformController::class);
// Applications
......@@ -32,8 +32,6 @@ function () {
Route::get('applications/view/claim/{id}', [ApplicationsController::class, 'viewEach'])->name('applications.viewEach');
Route::put('applications/{id}', [ApplicationsController::class, 'update'])
->name('applications.update');
Route::get('/applications/dates', [\Portal\Mileage\Http\Controllers\ApplicationsController::class, 'getClaimDates'])
->name('mileage::applications.getDates');
// Project Manager
Route::prefix('projectManager')->name('projectManager')->group(function () {
......
......@@ -9,6 +9,7 @@
use Illuminate\Routing\Controller;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Carbon\Carbon;
......@@ -68,53 +69,29 @@ public function index(Request $request)
public function store(Request $request)
{
// Validate request data
$validated = $request->validate([
'claim_date' => 'required|date',
'jenis_kenderaan_id' => 'required|exists:lkp_jenis_kenderaans,id',
'project_id' => 'nullable|exists:lkp_project,id',
'description' => 'required',
'distance_from' => 'nullable',
'distance_to' => 'nullable',
'total_mileage' => 'required|numeric',
'toll_amount' => 'nullable|numeric',
'others_amount' => 'nullable|numeric',
'others_description' => 'nullable|string|max:255',
]);
$validated['user_id'] = auth()->id(); // Get the current authenticated user
// Calculate the total claim amount and mileage
$calculatedAmount = $this->calculateClaimAmount($validated);
$validated['calculated_amount'] = $calculatedAmount;
// Handle the status based on actions (e.g., project manager, director, finance)
$validated['status'] = 'Diproses'; // Initial status
// Store the claim in the database
$claim = Claim::create($validated);
// Now, we need to manage the role and verify the project
// Example: If project_id is set, link the claim with the project and handle roles
if ($claim->project) {
$project = $claim->project;
// Here, you can check if the claim should be verified by a Project Manager (HOD)
// For now, we’re assuming you use the role directly to set verified_by
if ($project->project_manager_id) {
$claim->verified_by = $project->project_manager_id;
}
// Similarly, you can handle who approves and reviews (e.g., Project Director or BOD)
if ($project->project_director_id) {
$claim->approved_by = $project->project_director_id;
}
// your existing store logic
$calculation = $this->calculateClaimAmount($request);
$claim->save();
}
Claim::create([
'user_id' => Auth::id(),
'project_id' => $request->project_id,
'claim_date' => $request->claim_date,
'jenis_kenderaan_id' => $request->jenis_kenderaan_id,
'description' => $request->description,
'distance_from' => $request->distance_from,
'distance_to' => $request->distance_to,
'total_mileage' => $request->total_mileage,
'toll_amount' => $request->toll_amount ?? 0,
'others_amount' => $request->others_amount ?? 0,
'claimable_mileage' => $calculation['claimable_mileage'],
'rate' => $calculation['rate'],
'mileage_amount' => $calculation['mileage_amount'],
'total_claim_amount' => $calculation['total_claim'],
'status' => 'Diproses',
]);
return redirect()->route('mileage::applications.index')->with('success', 'Claim submitted successfully!');
return redirect()->route('mileage::applications.index')
->with('success', 'Tuntutan berjaya dihantar!');
}
public function edit($id)
......@@ -265,5 +242,26 @@ public function updateStatus(Request $request, $id)
return back()->with('success', 'Claim status updated to ' . $claim->status);
}
private function calculateClaimAmount($request)
{
$totalMileage = (float) $request->total_mileage;
$toll = (float) $request->toll_amount;
$others = (float) $request->others_amount;
// Deduct first 40km
$claimableMileage = $totalMileage > 40 ? $totalMileage - 40 : 0;
// Apply rate logic
$rate = $claimableMileage > 500 ? 0.50 : 0.55;
$mileageAmount = $claimableMileage * $rate;
$totalClaim = $mileageAmount + $toll + $others;
return [
'claimable_mileage' => $claimableMileage,
'rate' => $rate,
'mileage_amount' => $mileageAmount,
'total_claim' => $totalClaim,
];
}
}
......@@ -130,4 +130,15 @@ public function noproject()
return view('mileage::applyform.noproject', compact('jenisKenderaan'));
}
public function getDates()
{
$userId = auth()->id();
$dates = Claim::where('user_id', $userId)
->pluck('claim_date') // get all submitted dates
->map(fn($d) => \Carbon\Carbon::parse($d)->format('Y-m-d')); // format to yyyy-mm-dd
return response()->json($dates);
}
}
......@@ -6,7 +6,7 @@
class AclRole extends Model
{
protected $table = 'acl_roles'; // ⚠️ must be your roles definition table
protected $table = 'acl_roles'; // must be your roles definition table
protected $primaryKey = 'id';
public $timestamps = false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment