Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mileage-claim
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
trainee
mileage-claim
Commits
ac889b9d
Commit
ac889b9d
authored
Oct 30, 2025
by
Hannah Zahra
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fourth Update
parent
5961a3ce
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
208 additions
and
252 deletions
+208
-252
packages/portal/mileage/resources/views/applyform/index.blade.php
+85
-107
packages/portal/mileage/resources/views/applyform/noproject.blade.php
+66
-96
packages/portal/mileage/resources/views/projectManager/index.blade.php
+1
-0
packages/portal/mileage/routes/web.php
+1
-3
packages/portal/mileage/src/Http/Controllers/ApplicationsController.php
+43
-45
packages/portal/mileage/src/Http/Controllers/ApplyformController.php
+11
-0
packages/portal/mileage/src/Model/Aclrole.php
+1
-1
No files found.
packages/portal/mileage/resources/views/applyform/index.blade.php
View file @
ac889b9d
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
@extends('ui::layouts.app')
@extends('ui::layouts.app')
@push('style')
@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"
>
<style
type=
"text/css"
>
body
{
background-color
:
#f0f2f5
;
}
body
{
background-color
:
#f0f2f5
;
}
...
@@ -71,10 +72,29 @@
...
@@ -71,10 +72,29 @@
color
:
white
;
border-radius
:
10px
;
color
:
white
;
border-radius
:
10px
;
padding
:
10px
20px
;
font-weight
:
600
;
padding
:
10px
20px
;
font-weight
:
600
;
}
}
.ui.calendar
.disabled.day
{
color
:
#ccc
!important
;
text-decoration
:
line-through
;
}
</style>
</style>
@endpush
@endpush
@section('content')
@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 container"
>
<div
class=
"ui segments"
>
<div
class=
"ui segments"
>
<div
class=
"ui segment"
>
<div
class=
"ui segment"
>
...
@@ -179,9 +199,10 @@
...
@@ -179,9 +199,10 @@
<small>
(Jumlah keseluruhan sebenar akan dikira di Senarai Tuntutan)
</small>
<small>
(Jumlah keseluruhan sebenar akan dikira di Senarai Tuntutan)
</small>
</td>
</td>
<td>
<td>
<strong
id=
"total_claim"
>
RM 0.00
</strong>
<strong
id=
"total_claim
_amount
"
>
RM 0.00
</strong>
</td>
</td>
</tr>
</tbody>
</tr>
</tbody>
</table>
</table>
</div>
</div>
</div>
</div>
...
@@ -199,11 +220,49 @@
...
@@ -199,11 +220,49 @@
@push('script')
@push('script')
<script
src=
"https://code.jquery.com/jquery-3.7.1.min.js"
></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>
<script>
$
(
document
).
ready
(
function
()
{
$
(
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
=
`
const
modalHtml
=
`
<div class="ui basic modal" id="supportingDocsModal">
<div class="ui basic modal" id="supportingDocsModal">
<div class="ui icon header">
<div class="ui icon header">
...
@@ -215,131 +274,50 @@
...
@@ -215,131 +274,50 @@
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.</p>
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.</p>
</div>
</div>
<div class="actions" style="text-align:center;">
<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>
</div>`
;
</div>`
;
$
(
'body'
).
append
(
modalHtml
);
$
(
'body'
).
append
(
modalHtml
);
// Disable form until user clicks "Saya Faham"
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
true
);
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
true
);
// Show modal
$
(
'#supportingDocsModal'
).
modal
({
$
(
'#supportingDocsModal'
).
modal
({
closable
:
false
,
closable
:
false
,
onApprove
:
function
()
{
onApprove
:
function
()
{
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
false
);
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
false
);
$
(
'.ui.dropdown'
).
dropdown
();
$
(
'.ui.dropdown'
).
dropdown
();
// Wait for modal to close before initializing calendar
setTimeout
(()
=>
{
console
.
log
(
'🟢 Initializing Fomantic calendar...'
);
$
(
'#claim_date'
).
calendar
({
$
(
'#claim_date'
).
calendar
({
type
:
'date'
,
type
:
'date'
,
startMode
:
'day'
,
touchReadonly
:
true
,
monthFirst
:
false
,
maxDate
:
new
Date
(),
maxDate
:
new
Date
(),
disabledDates
:
[],
formatter
:
{
formatter
:
{
date
:
function
(
date
)
{
date
:
function
(
date
)
{
if
(
!
date
)
return
''
;
if
(
!
date
)
return
''
;
let
d
=
date
.
getDate
();
const
y
=
date
.
getFullYear
();
let
m
=
date
.
getMonth
()
+
1
;
const
m
=
(
'0'
+
(
date
.
getMonth
()
+
1
)).
slice
(
-
2
)
;
let
y
=
date
.
getFullYear
(
);
const
d
=
(
'0'
+
date
.
getDate
()).
slice
(
-
2
);
return
`
${
y
}
-
${
m
<
10
?
'0'
:
''
}${
m
}
-
${
d
<
10
?
'0'
:
''
}
${
d
}
`
;
return
`
${
y
}
-
${
m
}
-
${
d
}
`
;
}
}
},
onChange
:
function
(
date
,
text
)
{
console
.
log
(
'📅 Date selected:'
,
text
);
}
}
});
});
},
400
);
}
}
}).
modal
(
'show'
);
}).
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>
</script>
@endpush
@endpush
packages/portal/mileage/resources/views/applyform/noproject.blade.php
View file @
ac889b9d
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
@extends('ui::layouts.app')
@extends('ui::layouts.app')
@push('style')
@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"
>
<style
type=
"text/css"
>
body
{
background-color
:
#f0f2f5
;
}
body
{
background-color
:
#f0f2f5
;
}
...
@@ -71,10 +72,28 @@
...
@@ -71,10 +72,28 @@
color
:
white
;
border-radius
:
10px
;
color
:
white
;
border-radius
:
10px
;
padding
:
10px
20px
;
font-weight
:
600
;
padding
:
10px
20px
;
font-weight
:
600
;
}
}
.ui.calendar
.disabled.day
{
color
:
#ccc
!important
;
text-decoration
:
line-through
;
}
</style>
</style>
@endpush
@endpush
@section('content')
@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 container"
>
<div
class=
"ui segments"
>
<div
class=
"ui segments"
>
<div
class=
"ui segment"
>
<div
class=
"ui segment"
>
...
@@ -90,6 +109,7 @@
...
@@ -90,6 +109,7 @@
<p><strong>
Maklumat Tuntutan
</strong></p>
<p><strong>
Maklumat Tuntutan
</strong></p>
<div
class=
"ui segment"
>
<div
class=
"ui segment"
>
<div
class=
"ui form"
>
<div
class=
"ui form"
>
<div
class=
"inline field required"
>
<div
class=
"inline field required"
>
<label
class=
"three-wide field"
>
Tarikh
</label>
<label
class=
"three-wide field"
>
Tarikh
</label>
<div
class=
"eight wide field ui calendar"
id=
"claim_date"
>
<div
class=
"eight wide field ui calendar"
id=
"claim_date"
>
...
@@ -111,13 +131,13 @@
...
@@ -111,13 +131,13 @@
</div>
</div>
<div
class=
"inline field required"
>
<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
>
<input
class=
"eight wide field"
name=
"description"
type=
"text"
placeholder=
"Cth: Mesyuarat Dalaman"
required
>
</div>
</div>
<div
class=
"inline field required"
>
<div
class=
"inline field required"
>
<label
class=
"three-wide field"
>
Jarak Dari
</label>
<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=
""
>
Sila Pilih Lokasi
</option>
<option
value=
"Pejabat"
>
Pejabat
</option>
<option
value=
"Pejabat"
>
Pejabat
</option>
</select>
</select>
...
@@ -166,7 +186,7 @@
...
@@ -166,7 +186,7 @@
<small>
(Jumlah keseluruhan sebenar akan dikira di Senarai Tuntutan)
</small>
<small>
(Jumlah keseluruhan sebenar akan dikira di Senarai Tuntutan)
</small>
</td>
</td>
<td>
<td>
<strong
id=
"total_claim"
>
RM 0.00
</strong>
<strong
id=
"total_claim
_amount
"
>
RM 0.00
</strong>
</td>
</td>
</tr>
</tr>
</tbody>
</tbody>
...
@@ -187,11 +207,38 @@
...
@@ -187,11 +207,38 @@
@push('script')
@push('script')
<script
src=
"https://code.jquery.com/jquery-3.7.1.min.js"
></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>
<script>
$
(
document
).
ready
(
function
()
{
$
(
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
=
`
const
modalHtml
=
`
<div class="ui basic modal" id="supportingDocsModal">
<div class="ui basic modal" id="supportingDocsModal">
<div class="ui icon header">
<div class="ui icon header">
...
@@ -199,10 +246,8 @@
...
@@ -199,10 +246,8 @@
Peringatan Penting
Peringatan Penting
</div>
</div>
<div class="content" style="text-align:center;">
<div class="content" style="text-align:center;">
<p style="font-size:16px; line-height:1.5;">
<p>Dokumen sokongan seperti resit mesti dihantar kepada pihak Finance.<br>
Dokumen sokongan seperti resit mesti dihantar kepada pihak Finance sebagai bukti.<br>
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.</p>
Jika <b>tidak</b>, tuntutan akan <b>DITOLAK</b>.
</p>
</div>
</div>
<div class="actions" style="text-align:center;">
<div class="actions" style="text-align:center;">
<div class="ui green ok inverted button">
<div class="ui green ok inverted button">
...
@@ -212,7 +257,6 @@
...
@@ -212,7 +257,6 @@
</div>`
;
</div>`
;
$
(
'body'
).
append
(
modalHtml
);
$
(
'body'
).
append
(
modalHtml
);
// Disable inputs until user acknowledges
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
true
);
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
true
);
$
(
'#supportingDocsModal'
).
modal
({
$
(
'#supportingDocsModal'
).
modal
({
...
@@ -220,103 +264,29 @@
...
@@ -220,103 +264,29 @@
onApprove
:
function
()
{
onApprove
:
function
()
{
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
false
);
$
(
'#claimForm :input'
).
prop
(
'disabled'
,
false
);
$
(
'.ui.dropdown'
).
dropdown
();
$
(
'.ui.dropdown'
).
dropdown
();
setTimeout
(()
=>
{
$
(
'#claim_date'
).
calendar
({
$
(
'#claim_date'
).
calendar
({
type
:
'date'
,
type
:
'date'
,
startMode
:
'day'
,
touchReadonly
:
true
,
monthFirst
:
false
,
maxDate
:
new
Date
(),
maxDate
:
new
Date
(),
disabledDates
:
[],
formatter
:
{
formatter
:
{
date
:
function
(
date
)
{
date
:
function
(
date
)
{
if
(
!
date
)
return
''
;
if
(
!
date
)
return
''
;
let
d
=
date
.
getDate
();
const
y
=
date
.
getFullYear
();
let
m
=
date
.
getMonth
()
+
1
;
const
m
=
(
'0'
+
(
date
.
getMonth
()
+
1
)).
slice
(
-
2
);
let
y
=
date
.
getFullYear
();
const
d
=
(
'0'
+
date
.
getDate
()).
slice
(
-
2
);
return
`
${
y
}
-
${
m
<
10
?
'0'
:
''
}${
m
}
-
${
d
<
10
?
'0'
:
''
}${
d
}
`
;
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
(
''
);
}
}
});
});
},
400
);
// --- 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
;
}
}
}).
modal
(
'show'
);
}).
modal
(
'show'
);
$
(
document
).
off
(
'click'
,
'#editClaim'
).
on
(
'click'
,
'#editClaim'
,
function
()
{
$
(
'#claimSummaryModal'
).
modal
(
'hide'
);
});
});
});
});
</script>
</script>
@endpush
@endpush
packages/portal/mileage/resources/views/projectManager/index.blade.php
View file @
ac889b9d
...
@@ -138,6 +138,7 @@
...
@@ -138,6 +138,7 @@
<br>
<br>
@else
@else
<div
class=
"ui message"
>
Tiada tuntutan untuk projek ini bulan ini.
</div>
<div
class=
"ui message"
>
Tiada tuntutan untuk projek ini bulan ini.
</div>
<br>
@endif
@endif
</div>
</div>
@empty
@empty
...
...
packages/portal/mileage/routes/web.php
View file @
ac889b9d
...
@@ -21,8 +21,8 @@ function () {
...
@@ -21,8 +21,8 @@ function () {
Route
::
prefix
(
'applyform'
)
->
name
(
'applyform'
)
->
group
(
function
()
{
Route
::
prefix
(
'applyform'
)
->
name
(
'applyform'
)
->
group
(
function
()
{
Route
::
get
(
'/'
,
[
ApplyformController
::
class
,
'index'
])
->
name
(
'.index'
);
Route
::
get
(
'/'
,
[
ApplyformController
::
class
,
'index'
])
->
name
(
'.index'
);
Route
::
get
(
'/noproject'
,
[
ApplyformController
::
class
,
'noproject'
])
->
name
(
'.noproject'
);
Route
::
get
(
'/noproject'
,
[
ApplyformController
::
class
,
'noproject'
])
->
name
(
'.noproject'
);
Route
::
get
(
'/applyform/getDates'
,
[
ApplyformController
::
class
,
'getDates'
])
->
name
(
'.getDates'
);
});
});
// Route::resource('applyform', ApplyformController::class);
// Route::resource('applyform', ApplyformController::class);
// Applications
// Applications
...
@@ -32,8 +32,6 @@ function () {
...
@@ -32,8 +32,6 @@ function () {
Route
::
get
(
'applications/view/claim/{id}'
,
[
ApplicationsController
::
class
,
'viewEach'
])
->
name
(
'applications.viewEach'
);
Route
::
get
(
'applications/view/claim/{id}'
,
[
ApplicationsController
::
class
,
'viewEach'
])
->
name
(
'applications.viewEach'
);
Route
::
put
(
'applications/{id}'
,
[
ApplicationsController
::
class
,
'update'
])
Route
::
put
(
'applications/{id}'
,
[
ApplicationsController
::
class
,
'update'
])
->
name
(
'applications.update'
);
->
name
(
'applications.update'
);
Route
::
get
(
'/applications/dates'
,
[
\Portal\Mileage\Http\Controllers\ApplicationsController
::
class
,
'getClaimDates'
])
->
name
(
'mileage::applications.getDates'
);
// Project Manager
// Project Manager
Route
::
prefix
(
'projectManager'
)
->
name
(
'projectManager'
)
->
group
(
function
()
{
Route
::
prefix
(
'projectManager'
)
->
name
(
'projectManager'
)
->
group
(
function
()
{
...
...
packages/portal/mileage/src/Http/Controllers/ApplicationsController.php
View file @
ac889b9d
...
@@ -9,6 +9,7 @@
...
@@ -9,6 +9,7 @@
use
Illuminate\Routing\Controller
;
use
Illuminate\Routing\Controller
;
use
Illuminate\Support\Collection
;
use
Illuminate\Support\Collection
;
use
Illuminate\Support\Facades\Auth
;
use
Illuminate\Http\Request
;
use
Illuminate\Http\Request
;
use
Carbon\Carbon
;
use
Carbon\Carbon
;
...
@@ -68,53 +69,29 @@ public function index(Request $request)
...
@@ -68,53 +69,29 @@ public function index(Request $request)
public
function
store
(
Request
$request
)
public
function
store
(
Request
$request
)
{
{
// Validate request data
// your existing store logic
$validated
=
$request
->
validate
([
$calculation
=
$this
->
calculateClaimAmount
(
$request
);
'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
;
}
$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
)
public
function
edit
(
$id
)
...
@@ -265,5 +242,26 @@ public function updateStatus(Request $request, $id)
...
@@ -265,5 +242,26 @@ public function updateStatus(Request $request, $id)
return
back
()
->
with
(
'success'
,
'Claim status updated to '
.
$claim
->
status
);
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
,
];
}
}
}
packages/portal/mileage/src/Http/Controllers/ApplyformController.php
View file @
ac889b9d
...
@@ -130,4 +130,15 @@ public function noproject()
...
@@ -130,4 +130,15 @@ public function noproject()
return
view
(
'mileage::applyform.noproject'
,
compact
(
'jenisKenderaan'
));
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
);
}
}
}
packages/portal/mileage/src/Model/Aclrole.php
View file @
ac889b9d
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
class
AclRole
extends
Model
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'
;
protected
$primaryKey
=
'id'
;
public
$timestamps
=
false
;
public
$timestamps
=
false
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment