This commit is contained in:
Naz 2026-05-15 11:25:36 +08:00
parent 7ab4706524
commit ffac32640a
2 changed files with 163 additions and 62 deletions

View File

@ -115,17 +115,38 @@
</option> </option>
</select> </select>
</div> </div>
<div class="card-body"> <div class="card-body mt-3 border-top pt-4">
<h5>Holidays for Selected State</h5> <h6 class="text-uppercase fw-bold text-secondary mb-3 small">Holiday Schedule</h6>
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center" <div v-if="filteredHolidays.length > 0" class="table-responsive bg-white rounded shadow-sm">
v-for="holiday in filteredHolidays" :key="holiday.holidayId"> <table class="table table-hover table-borderless align-middle mb-0">
<span>{{ formatDate(holiday.holidayDate) }} - {{ holiday.holidayName }}</span> <thead class="table-light text-muted small text-uppercase">
<button class="btn btn-danger btn-sm" v-on:click="deleteHoliday(holiday.holidayId)"> <tr>
<i class="mdi mdi-delete"></i> <th class="ps-4 py-3 rounded-start">Date</th>
<th class="py-3">Holiday Name</th>
<th class="text-end pe-4 py-3 rounded-end">Action</th>
</tr>
</thead>
<tbody>
<tr v-for="holiday in filteredHolidays" :key="holiday.holidayId" class="border-bottom">
<td class="ps-4 fw-semibold text-primary">
<i class="mdi mdi-calendar-check me-2"></i>{{ formatDate(holiday.holidayDate) }}
</td>
<td class="fw-bold text-dark">{{ holiday.holidayName }}</td>
<td class="text-end pe-4">
<button class="btn btn-sm btn-outline-danger px-3 rounded-pill" v-on:click="deleteHoliday(holiday.holidayId)">
<i class="mdi mdi-trash-can-outline me-1"></i> Remove
</button> </button>
</li> </td>
</ul> </tr>
</tbody>
</table>
</div>
<div v-else class="text-center py-5 bg-light rounded shadow-sm border border-light">
<i class="mdi mdi-folder-open text-muted opacity-25 display-4"></i>
<h6 class="text-muted mt-3 fw-bold">No Data Available</h6>
</div>
</div> </div>
</div> </div>
@ -166,21 +187,52 @@
</div> </div>
</div> </div>
<div v-if="activeTab === 'weekend'" class="card shadow-sm"> <div v-if="activeTab === 'weekend'" class="card shadow-sm border-0 mt-3">
<div class="card-body"> <div class="card-body bg-white rounded border pt-4 pb-5">
<div class="row"> <h6 class="text-uppercase fw-bold text-secondary mb-4 text-center">
<i class="mdi mdi-calendar-weekend text-purple me-1"></i> Assigned Weekends
</h6>
<div v-if="Object.keys(groupedWeekends).length === 0" class="text-center py-5 bg-light rounded">
<i class="mdi mdi-folder-open text-muted opacity-25 display-4"></i>
<h6 class="text-muted mt-3 fw-bold">No Data Available</h6>
<p class="text-secondary small mb-0">Assign states to a weekend above.</p>
</div>
<div class="row g-4 justify-content-center">
<div class="col-md-6" v-for="(states, day) in groupedWeekends" :key="day"> <div class="col-md-6" v-for="(states, day) in groupedWeekends" :key="day">
<div class="card p-2 mb-2 shadow-sm"> <div class="card border border-light-subtle shadow-sm h-100">
<strong>{{ day }}</strong>
<ul class="list-unstyled ps-3"> <div class="card-header bg-light border-bottom-0 py-3 d-flex justify-content-between align-items-center">
<li v-for="state in states" :key="state">{{ state }}</li> <h6 class="fw-bold mb-0 text-dark">
</ul> <i class="mdi mdi-clock-outline me-2 text-primary"></i>{{ day }}
</div> </h6>
</div> <span class="badge bg-primary rounded-pill">{{ states.length }}</span>
</div> </div>
<div class="card-body pt-3 pb-4">
<div class="d-flex flex-wrap gap-2">
<span class="badge border text-secondary bg-white px-3 py-2 shadow-sm" style="font-size: 0.85rem;" v-for="state in states" :key="state">
<i class="mdi mdi-map-marker-outline me-1"></i> {{ state }}
</span>
</div> </div>
</div> </div>
@* <div class="card-body pt-3 pb-4">
<div class="d-flex flex-wrap gap-2">
<span class="badge border text-secondary bg-white px-3 py-2 shadow-sm d-flex align-items-center" style="font-size: 0.85rem;" v-for="state in states" :key="state">
<img :src="getStateFlag(state)" alt="Flag" class="me-2" style="width: 20px; height: 14px; object-fit: cover; border-radius: 2px; border: 1px solid #ddd;">
{{ state }}
</span>
</div>
</div> *@
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -202,7 +254,6 @@
selectedDay: "", selectedDay: "",
weekendList: [], weekendList: [],
stateWeekends: [], stateWeekends: [],
selectAllChecked: false
}; };
}, },
@ -217,7 +268,9 @@
computed: { computed: {
filteredHolidays() { filteredHolidays() {
return this.selectedState return this.selectedState
? this.holidayList.filter(h => h.stateId === this.selectedState) ? this.holidayList
.filter(h => h.stateId === this.selectedState)
.sort((a, b) => new Date(a.holidayDate) - new Date(b.holidayDate))
: []; : [];
}, },
groupedWeekends() { groupedWeekends() {
@ -230,24 +283,22 @@
}); });
return grouped; return grouped;
}, },
filteredHolidays() {
return this.selectedState selectAllChecked: {
? this.holidayList
.filter(h => h.stateId === this.selectedState) get() {
.sort((a, b) => new Date(a.holidayDate) - new Date(b.holidayDate)) return this.stateList.length > 0 && this.selectedStates.length === this.stateList.length;
: [];
}
}, },
watch: { set(value) {
selectAllChecked(ckeckedState) { if (value) {
if (ckeckedState) { // If checked, select all states
this.selectedStates = this.stateList.map(state => state.stateId); this.selectedStates = this.stateList.map(state => state.stateId);
} else {
// If unchecked, clear all states
this.selectedStates = [];
}
} }
},
selectedStates(ckeckedState) {
this.selectAllChecked = ckeckedState.length === this.stateList.length;
} }
}, },
@ -289,7 +340,14 @@
}, },
formatDate(date) { formatDate(date) {
return new Date(date).toLocaleDateString(); if (!date) return '';
const d = new Date(date);
const day = String(d.getDate()).padStart(2, '0');
const month = String(d.getMonth() + 1).padStart(2, '0');
const year = d.getFullYear();
return `${day}/${month}/${year}`;
}, },
async updateHoliday() { async updateHoliday() {
@ -414,6 +472,42 @@
} }
}, },
// getStateFlag(stateName) {
// if (!stateName) return '';
// const name = stateName.toLowerCase();
// Dictionary mapping state names to Wikimedia Commons flag URLs
// const flags = {
// 'johor': 'https:upload.wikimedia.org/wikipedia/commons/5/5a/Flag_of_Johor.svg',
// 'kedah': 'https:upload.wikimedia.org/wikipedia/commons/c/cc/Flag_of_Kedah.svg',
// 'kelantan': 'https:upload.wikimedia.org/wikipedia/commons/6/61/Flag_of_Kelantan.svg',
// 'melaka': 'https:upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_Malacca.svg',
// 'malacca': 'https:upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_Malacca.svg',
// 'negeri sembilan': 'https:upload.wikimedia.org/wikipedia/commons/d/db/Flag_of_Negeri_Sembilan.svg',
// 'pahang': 'https:upload.wikimedia.org/wikipedia/commons/a/aa/Flag_of_Pahang.svg',
// 'penang': 'https:upload.wikimedia.org/wikipedia/commons/d/d4/Flag_of_Penang_%28Malaysia%29.svg',
// 'pulau pinang': 'https:upload.wikimedia.org/wikipedia/commons/d/d4/Flag_of_Penang_%28Malaysia%29.svg',
// 'perak': 'https:upload.wikimedia.org/wikipedia/commons/8/87/Flag_of_Perak.svg',
// 'perlis': 'https:upload.wikimedia.org/wikipedia/commons/a/aa/Flag_of_Perlis.svg',
// 'sabah': 'https:upload.wikimedia.org/wikipedia/commons/b/b5/Flag_of_Sabah.svg',
// 'sarawak': 'https:upload.wikimedia.org/wikipedia/commons/7/7e/Flag_of_Sarawak.svg',
// 'selangor': 'https:upload.wikimedia.org/wikipedia/commons/0/0c/Flag_of_Selangor.svg',
// 'terengganu': 'https:upload.wikimedia.org/wikipedia/commons/6/6b/Flag_of_Terengganu.svg',
// 'kuala lumpur': 'https:upload.wikimedia.org/wikipedia/commons/6/64/Flag_of_Kuala_Lumpur%2C_Malaysia.svg',
// 'labuan': 'https:upload.wikimedia.org/wikipedia/commons/6/69/Flag_of_Labuan.svg',
// 'putrajaya': 'https:upload.wikimedia.org/wikipedia/commons/9/9f/Flag_of_Putrajaya.svg'
// };
// for (let key in flags) {
// if (name.includes(key)) {
// return flags[key];
// }
// }
// return 'https:flagcdn.com/w40/my.png';
// },
clearForm() { clearForm() {
this.selectedDate = ''; this.selectedDate = '';
this.selectedStates = []; this.selectedStates = [];

View File

@ -6,12 +6,10 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<style> <style>
#ApprovalFlowTable { #ApprovalFlowTable {
background-color: white; background-color: white;
} }
#ApprovalFlowTable th, #ApprovalFlowTable th,
#ApprovalFlowTable td { #ApprovalFlowTable td {
background-color: white !important; background-color: white !important;
@ -223,37 +221,46 @@
</div> </div>
</form> </form>
</div> </div>
<div class="d-flex justify-content-center">
<div class="table-container table-responsive w-100" style="margin-top: 20px; max-width: 900px;"> @* Table Approval Name *@
<table id="ApprovalFlowTable" class="table table-bordered table-striped align-middle"> <div class="d-flex justify-content-center mt-5">
<thead class="table-light"> <div class="table-responsive bg-white rounded shadow-sm border w-75">
<table id="ApprovalFlowTable" class="table table-hover table-borderless align-middle mb-0">
<thead class="table-light text-muted small text-uppercase border-bottom">
<tr> <tr>
<th style="width: 50px;">No.</th> <th class="ps-4 py-3" style="width: 80px;">No.</th>
<th>Approval Name</th> <th class="py-3">Approval Name</th>
<th style="width: 120px;">Action</th> <th class="text-end pe-4 py-3" style="width: 220px;">Action</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-if="approvalFlowList.length === 0"> <tr v-if="approvalFlowList.length === 0">
<td colspan="3" class="text-center text-muted">No approval flows found.</td> <td colspan="3" class="text-center py-5 bg-light">
<i class="mdi mdi-folder-open text-muted opacity-25 display-4 d-block mb-3"></i>
<span class="text-muted fw-bold">No Approval Flows Found</span>
<p class="text-secondary small mt-1 mb-0">Create a new flow above to get started.</p>
</td>
</tr> </tr>
<tr v-for="(flow, index) in approvalFlowList" :key="flow.approvalFlowId">
<td>{{ index + 1 }}</td> <tr v-for="(flow, index) in approvalFlowList" :key="flow.approvalFlowId" class="border-bottom">
<td>{{ flow.approvalName }}</td> <td class="ps-4 fw-semibold text-secondary">{{ index + 1 }}</td>
<td> <td class="fw-bold text-dark">{{ flow.approvalName }}</td>
<button class="btn btn-sm btn-primary me-2" v-on:click="openEditModal(flow)" title="Edit"> <td class="pe-4">
<i class="bi bi-pencil"></i> <div class="d-flex justify-content-end gap-2">
<button class="btn btn-sm btn-primary px-3 rounded-pill fw-bold shadow-sm d-inline-flex align-items-center" v-on:click="openEditModal(flow)" title="Edit">
<i class="bi bi-pencil-square me-2"></i> Edit
</button> </button>
<button class="btn btn-sm btn-danger" v-on:click="deleteApprovalFlow(flow.approvalFlowId)" title="Delete">
<i class="bi bi-trash"></i> <button class="btn btn-sm btn-danger px-3 rounded-pill fw-bold shadow-sm d-inline-flex align-items-center" v-on:click="deleteApprovalFlow(flow.approvalFlowId)" title="Delete">
<i class="bi bi-trash-fill me-2"></i> Remove
</button> </button>
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>