Update & Fix Eerror
This commit is contained in:
parent
7a08ac374a
commit
d6d151a3c7
@ -4,7 +4,6 @@
|
||||
ViewData["Title"] = "Forgot your password?";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h2>Enter your email.</h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
ViewData["Title"] = "Forgot password confirmation";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
|
||||
@ -44,10 +44,57 @@
|
||||
color: purple;
|
||||
}
|
||||
|
||||
|
||||
.ms-auto {
|
||||
margin-left: auto !important; /* Push Complete/Incomplete to right */
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-toggle-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dropdown-toggle-box input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.dropdown-btn {
|
||||
border: none;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.dropdown-content option {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-content option:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
</style>
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml")
|
||||
<div id="registerItem" class="row">
|
||||
@ -73,14 +120,31 @@
|
||||
<div class="row mb-3" v-if="sortBy === 'station'">
|
||||
<h4 class="col-sm-1 col-form-h2" style="min-width:150px;">Search Station:</h4>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control" v-model="searchStation" placeholder="Search by station name...">
|
||||
<div class="dropdown" v-click-outside="closeDropdown">
|
||||
<!-- Button + Input dalam satu box -->
|
||||
<div class="dropdown-toggle-box" v-on:click="dropdownOpen = !dropdownOpen">
|
||||
<input type="text" class="form-control" v-model="searchQueryStation"
|
||||
placeholder="Search Station..." v-on:focus="dropdownOpen = true" v-on:click.stop />
|
||||
<button type="button" class="btn btn-primary dropdown-btn" v-on:click.stop="dropdownOpen = !dropdownOpen">
|
||||
▼
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown list -->
|
||||
<div v-if="dropdownOpen" class="dropdown-content" v-on:click.stop>
|
||||
<div v-for="(item, index) in stations"
|
||||
:key="index" class="dropdown-item" v-on:mousedown.prevent="selectStation(item)">
|
||||
{{ item.stationName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="sortBy === 'all'">
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<h2>Pending Item Movement</h2>
|
||||
<h2>Pending Item Transit</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="loading">
|
||||
@ -96,6 +160,9 @@
|
||||
<div class="card-header">
|
||||
<h2>Complete Item Movement</h2>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<h4>All Item Movement</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="loading">
|
||||
<div class="spinner-border text-info" role="status">
|
||||
@ -104,6 +171,18 @@
|
||||
</div>
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementCompleteDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<h4>Assign Station</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="loading">
|
||||
<div class="spinner-border text-info" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="assignStationDatatable"
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -130,7 +209,7 @@
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(group, itemId) in filteredItems" :key="itemId" class="row card">
|
||||
<div v-for="(group, itemId) in paginatedItems" :key="itemId" class="row card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
@* <h2>Item : {{ group.uniqueID }}</h2> *@
|
||||
<h2 v-if="group.uniqueID">Item : {{ group.uniqueID }}</h2>
|
||||
@ -159,13 +238,13 @@
|
||||
</h3>
|
||||
|
||||
<!-- Send Date -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:310px; min-width:310px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : movement.action === 'Register' ? 'Register Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{movement.action !== 'Register' ? movement.sendDate : movement.date }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Receive Date -->
|
||||
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<div v-if="movement.action !== 'Assign' && movement.action !== 'Register'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
|
||||
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
|
||||
</div>
|
||||
@ -260,13 +339,13 @@
|
||||
</h3>
|
||||
|
||||
<!-- Send Date -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:310px; min-width:310px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : movement.action === 'Register' ? 'Register Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{movement.action !== 'Register' ? movement.sendDate : movement.date }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Receive Date -->
|
||||
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<div v-if="movement.action !== 'Assign' && movement.action !== 'Register'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
|
||||
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
|
||||
</div>
|
||||
@ -347,8 +426,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--------------------------------------------STATION CATEGORY---------------------------------------------------------------------->
|
||||
<div v-if="sortBy === 'station'">
|
||||
<div v-for="(items, station) in filteredStation " :key="stationName"
|
||||
@ -363,7 +440,7 @@
|
||||
|
||||
<!-- Show Items Under Each Station -->
|
||||
<div v-show="categoryVisible[station]" class="card-body">
|
||||
<div v-for="(group, itemId) in items" :key="itemId" class="row card">
|
||||
<div v-for="(group, itemId) in paginatedItemsStation(items)" :key="itemId" class="row card">
|
||||
<!-- Item Header -->
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
@* <h2>Item : {{ group.uniqueID }}</h2> *@
|
||||
@ -391,13 +468,13 @@
|
||||
</h3>
|
||||
|
||||
<!-- Send Date -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:310px; min-width:310px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : movement.action === 'Register' ? 'Register Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{movement.action !== 'Register' ? movement.sendDate : movement.date }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Receive Date -->
|
||||
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<div v-if="movement.action !== 'Assign' && movement.action !== 'Register'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
|
||||
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
|
||||
</div>
|
||||
@ -485,13 +562,13 @@
|
||||
</h3>
|
||||
|
||||
<!-- Send Date -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:285px; min-width:285px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{ movement.sendDate }}</span>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:310px; min-width:310px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">{{movement.action === 'Assign' ? 'Assign Date' : movement.action === 'Register' ? 'Register Date' : 'Send Date'}}</h4>
|
||||
<span class="fixed-value text-truncate">{{movement.action !== 'Register' ? movement.sendDate : movement.date }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Receive Date -->
|
||||
<div v-if="movement.action !== 'Assign'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<div v-if="movement.action !== 'Assign' && movement.action !== 'Register'" class="d-flex flex-wrap align-items-center gap-2 flex-grow-1" style="max-width:290px; min-width:290px;">
|
||||
<h4 class="fixed-label m-0 text-nowrap">Receive Date:</h4>
|
||||
<span class="fixed-value text-truncate" style="max-width:160px;">{{ movement.receiveDate || 'Not arrive' }}</span>
|
||||
</div>
|
||||
@ -602,6 +679,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center mt-3" v-if="sortBy === 'item' && totalPages > 0">
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPage(currentPage - 1)" :disabled="currentPage === 1">
|
||||
Previous
|
||||
</button>
|
||||
|
||||
<span class="mx-2">Page {{ currentPage }} of {{ totalPages }}</span>
|
||||
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPage(currentPage + 1)" :disabled="currentPage === totalPages">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center mt-3" v-if="sortBy === 'station' && totalPagesStation > 0">
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPageStation(currentPageStation - 1)" :disabled="currentPageStation === 1">
|
||||
Previous
|
||||
</button>
|
||||
|
||||
<span class="mx-2">Page {{ currentPageStation }} of {{ itemsPerPageStation }}</span>
|
||||
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPageStation(currentPageStation + 1)" :disabled="currentPageStation === totalPages">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -644,6 +743,7 @@
|
||||
selectedTeamType: "",
|
||||
selectedtoStation: "",
|
||||
consignmentNoteUrl: "",
|
||||
assignStationDatatable: null,
|
||||
showItemModal: false,
|
||||
loading: false,
|
||||
items: [],
|
||||
@ -651,20 +751,39 @@
|
||||
currentUserCompanyDept: null,
|
||||
sortBy: "all",
|
||||
searchQuery: "",
|
||||
searchQueryStation: "",
|
||||
categoryVisible: {},
|
||||
historyVisible: {},
|
||||
detailsVisible: {},
|
||||
currentRole:"",
|
||||
stationName: "",
|
||||
searchStation: "",
|
||||
|
||||
currentPage: 1,
|
||||
itemsPerPage: 10,
|
||||
currentPageStation : 1,
|
||||
itemsPerPageStation: 10,
|
||||
dropdownOpen: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchItem();
|
||||
|
||||
this.fetchStation();
|
||||
},
|
||||
computed: {
|
||||
paginatedItems() {
|
||||
const start = (this.currentPage - 1) * this.itemsPerPage;
|
||||
const end = start + this.itemsPerPage;
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.filteredItems).slice(start, end)
|
||||
);
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(Object.keys(this.filteredItems).length / this.itemsPerPage);
|
||||
},
|
||||
|
||||
totalPagesStation() {
|
||||
return Math.ceil(Object.keys(this.filteredStation).length / this.itemsPerPage);
|
||||
},
|
||||
groupedItems() {
|
||||
let grouped = this.items.reduce((acc, movement) => {
|
||||
if (!acc[movement.itemId]) {
|
||||
@ -761,16 +880,16 @@
|
||||
|
||||
},
|
||||
filteredStation() {
|
||||
if (!this.searchStation) {
|
||||
if (!this.searchQueryStation) {
|
||||
return this.groupedByStation;
|
||||
}
|
||||
|
||||
let searchQuery = this.searchStation.toLowerCase();
|
||||
let searchQueryStation = this.toLowerCase();
|
||||
let grouped = this.groupedByStation;
|
||||
let filtered = {};
|
||||
|
||||
Object.keys(grouped).forEach(station => {
|
||||
if (station.toLowerCase().includes(searchQuery)) {
|
||||
if (station.toLowerCase().includes(searchQueryStation)) {
|
||||
filtered[station] = grouped[station];
|
||||
}
|
||||
});
|
||||
@ -780,6 +899,30 @@
|
||||
},
|
||||
methods: {
|
||||
|
||||
paginatedItemsStation(item) {
|
||||
const start = (this.currentPageStation - 1) * this.itemsPerPageStation;
|
||||
const end = start + this.itemsPerPageStation;
|
||||
return Object.fromEntries(
|
||||
Object.entries(item).slice(start, end)
|
||||
);
|
||||
},
|
||||
selectStation(item) {
|
||||
this.searchQueryStation = item.stationName;
|
||||
this.dropdownOpen = false;
|
||||
},
|
||||
closeDropdown() {
|
||||
this.dropdownOpen = false; // Tutup dropdown
|
||||
},
|
||||
goToPage(page) {
|
||||
if (page >= 1 && page <= this.totalPages) {
|
||||
this.currentPage = page;
|
||||
}
|
||||
},
|
||||
goToPageStation(page) {
|
||||
if (page >= 1 && page <= this.itemsPerPageStation) {
|
||||
this.currentPageStation = page;
|
||||
}
|
||||
},
|
||||
remark(remark) {
|
||||
document.getElementById("remarkContent").innerText = remark || "No remark message provide.";
|
||||
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
||||
@ -963,6 +1106,25 @@
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
async fetchStation() {
|
||||
try {
|
||||
const response = await fetch('/InvMainAPI/StationList', {
|
||||
method: 'POST', // Specify the HTTP method
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch Station');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
this.stations = data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching Station:', error);
|
||||
}
|
||||
},
|
||||
handleSorting() {
|
||||
this.renderTables();
|
||||
},
|
||||
@ -979,11 +1141,15 @@
|
||||
if (this.itemMovementCompleteDatatable) {
|
||||
this.itemMovementCompleteDatatable.destroy();
|
||||
}
|
||||
if (this.assignStationDatatable) {
|
||||
this.assignStationDatatable.destroy();
|
||||
}
|
||||
|
||||
this.itemMovementNotCompleteDatatable = $("#itemMovementNotCompleteDatatable").DataTable({
|
||||
data: this.items.filter((m) => m.movementComplete == 0),
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ title: "Action", data: "action" },
|
||||
{ title: "Send Date", data: "sendDate" },
|
||||
@ -994,35 +1160,7 @@
|
||||
{ title: "Start Status", data: "toOther" },
|
||||
{ title: "Product Category", data: "productCategory" },
|
||||
{ title: "Qty", data: "quantity" },
|
||||
{
|
||||
title: "Note",
|
||||
data: "consignmentNote",
|
||||
render: function (data, type, full, meta) {
|
||||
if (!data) {
|
||||
return "No Document";
|
||||
}
|
||||
|
||||
// Check if the document is an image based on file extension
|
||||
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(data);
|
||||
var isPdf = /\.pdf$/i.test(data);
|
||||
|
||||
if (isImage) {
|
||||
return `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
}
|
||||
else if (isPdf) {
|
||||
return `<a href="${data}" target="_blank">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/87/PDF_file_icon.svg"
|
||||
alt="PDF Document" class="img-thumbnail"
|
||||
style="width: 50px; height: 50px;" />
|
||||
<br>View PDF
|
||||
</a>`;
|
||||
} else {
|
||||
return `<a href="${data}" target="_blank">Download File</a>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{ title: "Note", data: "consignmentNote", render: renderFile },
|
||||
{ title: "Remark", data: "remark" },
|
||||
],
|
||||
order: [[0, "desc"]],
|
||||
@ -1030,9 +1168,10 @@
|
||||
});
|
||||
|
||||
this.itemMovementCompleteDatatable = $("#itemMovementCompleteDatatable").DataTable({
|
||||
data: this.items.filter((m) => m.movementComplete == 1),
|
||||
data: this.items.filter((m) => m.movementComplete == 1 && m.action !== "Assign"),
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ title: "Send Date", data: "sendDate" },
|
||||
{ title: "Receive Date", data: "receiveDate" },
|
||||
@ -1046,55 +1185,32 @@
|
||||
{ title: "Start Status", data: "toOther" },
|
||||
{ title: "Latest Status", data: "latestStatus" },
|
||||
{ title: "Product Category", data: "productCategory" },
|
||||
{ title: "Qty", data: "quantity" },
|
||||
{ title: "Note",
|
||||
data: "consignmentNote",
|
||||
render: function (data, type, full, meta) {
|
||||
if (!data) {
|
||||
return "No Document";
|
||||
}
|
||||
|
||||
// Check if the document is an image based on file extension
|
||||
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(data);
|
||||
var isPdf = /\.pdf$/i.test(data);
|
||||
|
||||
if (isImage) {
|
||||
return `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
}
|
||||
else if (isPdf) {
|
||||
return `<a href="${data}" target="_blank">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/87/PDF_file_icon.svg"
|
||||
alt="PDF Document" class="img-thumbnail"
|
||||
style="width: 50px; height: 50px;" />
|
||||
<br>View PDF
|
||||
</a>`;
|
||||
} else {
|
||||
return `<a href="${data}" target="_blank">Download File</a>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{ title: "Note", data: "consignmentNote", render: renderFile },
|
||||
{ title: "Remark", data: "remark" },
|
||||
],
|
||||
order: [[0, "desc"]],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
this.stationDatatable = $("#stationDatatable").DataTable({
|
||||
this.assignStationDatatable = $("#assignStationDatatable").DataTable({
|
||||
data: this.items.filter((m) => m.action === "Assign" ),
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ title: "Assign Date", data: "sendDate" },
|
||||
{ title: "From User", data: "toUserName" },
|
||||
{ title: "Action", data: "action" },
|
||||
{ title: "Station User PIC", data: "toUserName" },
|
||||
{ title: "From Station", data: "toStationName" },
|
||||
{ title: "Last Station", data: "lastStationName" },
|
||||
{ title: "Qty", data: "quantity" },
|
||||
{
|
||||
title: "Note",
|
||||
data: "consignmentNote",
|
||||
render: function (data, type, full, meta) {
|
||||
{ title: "Note", data: "consignmentNote", render: renderFile },
|
||||
{ title: "Remark", data: "remark" },
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
function renderFile(data, type, full, meta) {
|
||||
if (!data) {
|
||||
return "No Document";
|
||||
}
|
||||
@ -1118,12 +1234,7 @@
|
||||
} else {
|
||||
return `<a href="${data}" target="_blank">Download File</a>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{ title: "Remark", data: "remark" },
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
toggleCategory(itemId) {
|
||||
this.categoryVisible[itemId] = !this.categoryVisible[itemId];
|
||||
@ -1148,6 +1259,21 @@
|
||||
},
|
||||
|
||||
},
|
||||
directives: {
|
||||
clickOutside: {
|
||||
beforeMount(el, binding) {
|
||||
el.clickOutsideEvent = (event) => {
|
||||
if (!(el.contains(event.target))) {
|
||||
binding.value?.(); // Guna optional chaining untuk elak error
|
||||
}
|
||||
};
|
||||
document.body.addEventListener("click", el.clickOutsideEvent);
|
||||
},
|
||||
unmounted(el) {
|
||||
document.body.removeEventListener("click", el.clickOutsideEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -28,6 +28,52 @@
|
||||
margin-left: auto !important; /* Push Complete/Incomplete to right */
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-toggle-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dropdown-toggle-box input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.dropdown-btn {
|
||||
border: none;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.dropdown-content option {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-content option:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
</style>
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml")
|
||||
@ -54,14 +100,32 @@
|
||||
<div class="row mb-3" v-if="sortBy === 'station'">
|
||||
<h4 class="col-sm-1 col-form-h2" style="min-width:150px;">Search Station:</h4>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" class="form-control" v-model="searchStation" placeholder="Search by station name...">
|
||||
<div class="dropdown" v-click-outside="closeDropdown">
|
||||
<!-- Button + Input dalam satu box -->
|
||||
<div class="dropdown-toggle-box" v-on:click="dropdownOpen = !dropdownOpen">
|
||||
<input type="text" class="form-control" v-model="searchQueryStation"
|
||||
placeholder="Search Station..." v-on:focus="dropdownOpen = true" v-on:click.stop />
|
||||
<button type="button" class="btn btn-primary dropdown-btn" v-on:click.stop="dropdownOpen = !dropdownOpen">
|
||||
▼
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown list -->
|
||||
<div v-if="dropdownOpen" class="dropdown-content" v-on:click.stop>
|
||||
<div v-for="(item, index) in stations"
|
||||
:key="index" class="dropdown-item" v-on:mousedown.prevent="selectStation(item)">
|
||||
{{ item.stationName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="sortBy === 'all'">
|
||||
<h5 style="color:cadetblue">*Each Items will display one record only*</h5>
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
<h2>Pending Item Movement</h2>
|
||||
<h2>Pending Item Transit</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementNotCompleteDatatable"
|
||||
@ -73,16 +137,26 @@
|
||||
<div class="card-header">
|
||||
<h2>Complete Item Movement</h2>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<h4>All Item Movement</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementCompleteDatatable"
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
<div class="card-header">
|
||||
<h4>Assign Station</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="assignStationDatatable"
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--------------------------------------------ITEM CATEGORY---------------------------------------------------------------------->
|
||||
<div v-if="sortBy === 'item'">
|
||||
<div v-for="(group, itemId) in filteredItems" :key="itemId" class="row card">
|
||||
<div v-for="(group, itemId) in paginatedItems" :key="itemId" class="row card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>Item : {{ group.uniqueID }}</h2>
|
||||
<button class="btn btn-light" v-on:click="toggleCategory(itemId)">
|
||||
@ -532,6 +606,29 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center mt-3" v-if="sortBy === 'item' && totalPages > 0">
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPage(currentPage - 1)" :disabled="currentPage === 1">
|
||||
Previous
|
||||
</button>
|
||||
|
||||
<span class="mx-2">Page {{ currentPage }} of {{ totalPages }}</span>
|
||||
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPage(currentPage + 1)" :disabled="currentPage === totalPages">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center mt-3" v-if="sortBy === 'station' && totalPagesStation > 0">
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPageStation(currentPageStation - 1)" :disabled="currentPageStation === 1">
|
||||
Previous
|
||||
</button>
|
||||
|
||||
<span class="mx-2">Page {{ currentPageStation }} of {{ itemsPerPageStation }}</span>
|
||||
|
||||
<button class="btn btn-secondary mx-1" v-on:click="goToPageStation(currentPageStation + 1)" :disabled="currentPageStation === totalPages">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@ -545,11 +642,13 @@
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
userId: null,
|
||||
itemMovements: [],
|
||||
assignStationDatatable: null,
|
||||
itemMovementCompleteDatatable: null,
|
||||
stationDatatable: null,
|
||||
itemMovementNotCompleteDatatable: null,
|
||||
searchQuery: "",
|
||||
searchQueryStation: "",
|
||||
searchStation: "",
|
||||
sortBy: "all",
|
||||
historyVisible: {},
|
||||
@ -557,9 +656,29 @@
|
||||
categoryVisible: {},
|
||||
consignmentNoteUrl: "",
|
||||
stationName: "",
|
||||
currentPage: 1,
|
||||
itemsPerPage: 10,
|
||||
currentPageStation: 1,
|
||||
itemsPerPageStation: 10,
|
||||
dropdownOpen: false,
|
||||
currentUser: null,
|
||||
stations:[],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
paginatedItems() {
|
||||
const start = (this.currentPage - 1) * this.itemsPerPage;
|
||||
const end = start + this.itemsPerPage;
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.filteredItems).slice(start, end)
|
||||
);
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(Object.keys(this.filteredItems).length / this.itemsPerPage);
|
||||
},
|
||||
totalPagesStation() {
|
||||
return Math.ceil(Object.keys(this.filteredStation).length / this.itemsPerPage);
|
||||
},
|
||||
processedGroupedItems() {
|
||||
let grouped = this.itemMovements.reduce((acc, movement) => {
|
||||
if (!acc[movement.itemId]) {
|
||||
@ -686,16 +805,16 @@
|
||||
},
|
||||
|
||||
filteredStation() {
|
||||
if (!this.searchStation) {
|
||||
if (!this.searchQueryStation) {
|
||||
return this.groupedByStation;
|
||||
}
|
||||
|
||||
let searchQuery = this.searchStation.toLowerCase();
|
||||
let searchQueryStation = this.searchQueryStation.toLowerCase();
|
||||
let grouped = this.groupedByStation;
|
||||
let filtered = {};
|
||||
|
||||
Object.keys(grouped).forEach(station => {
|
||||
if (station.toLowerCase().includes(searchQuery)) {
|
||||
if (station.toLowerCase().includes(searchQueryStation)) {
|
||||
filtered[station] = grouped[station];
|
||||
}
|
||||
});
|
||||
@ -703,10 +822,44 @@
|
||||
return filtered;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
this.fetchItemMovement();
|
||||
await this.fetchUser();
|
||||
await Promise.all([
|
||||
this.fetchStation(),
|
||||
]);
|
||||
},
|
||||
methods: {
|
||||
paginatedItemsStation(item) {
|
||||
const start = (this.currentPageStation - 1) * this.itemsPerPageStation;
|
||||
const end = start + this.itemsPerPageStation;
|
||||
return Object.fromEntries(
|
||||
Object.entries(item).slice(start, end)
|
||||
);
|
||||
},
|
||||
selectStation(item) {
|
||||
this.searchQueryStation = item.stationName;
|
||||
this.dropdownOpen = false;
|
||||
},
|
||||
closeDropdown() {
|
||||
this.dropdownOpen = false; // Tutup dropdown
|
||||
},
|
||||
goToPage(page) {
|
||||
if (page >= 1 && page <= this.totalPages) {
|
||||
this.currentPage = page;
|
||||
}
|
||||
},
|
||||
goToPageStation(page) {
|
||||
if (page >= 1 && page <= this.itemsPerPageStation) {
|
||||
this.currentPageStation = page;
|
||||
}
|
||||
},
|
||||
remark(remark) {
|
||||
document.getElementById("remarkContent").innerText = remark || "No remark message provide.";
|
||||
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
||||
modal.show();
|
||||
},
|
||||
|
||||
remark(remark) {
|
||||
document.getElementById("remarkContent").innerText = remark || "No remark message provide.";
|
||||
let modal = new bootstrap.Modal(document.getElementById("remarkModal"));
|
||||
@ -750,6 +903,45 @@
|
||||
}
|
||||
},
|
||||
|
||||
async fetchStation() {
|
||||
try {
|
||||
const response = await fetch('/InvMainAPI/StationList', {
|
||||
method: 'POST', // Specify the HTTP method
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch Station');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
this.stations = data.filter(station => station.stationPicID === this.userId);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching Station:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async fetchUser() {
|
||||
try {
|
||||
const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
|
||||
method: 'POST',
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.currentUser = data?.userInfo || null;
|
||||
this.userId = await this.currentUser.id;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch user: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
}
|
||||
},
|
||||
|
||||
handleSorting() {
|
||||
this.renderTables();
|
||||
},
|
||||
@ -767,8 +959,8 @@
|
||||
if (this.itemMovementCompleteDatatable) {
|
||||
this.itemMovementCompleteDatatable.destroy();
|
||||
}
|
||||
if (this.stationDatatable) {
|
||||
this.stationDatatable.destroy();
|
||||
if (this.assignStationDatatable) {
|
||||
this.assignStationDatatable.destroy();
|
||||
}
|
||||
|
||||
// Get latest movement per uniqueID after filtering
|
||||
@ -808,6 +1000,7 @@
|
||||
|
||||
let notCompleteData = [];
|
||||
let completeData = [];
|
||||
let completeStationData = [];
|
||||
|
||||
latestMovements.forEach(movement => {
|
||||
let filteredMovements = filterMovements([movement]);
|
||||
@ -815,8 +1008,10 @@
|
||||
if (filteredMovements.length > 0) {
|
||||
if (movement.movementComplete == 0) {
|
||||
notCompleteData.push(movement);
|
||||
} else if (movement.movementComplete == 1) {
|
||||
} else if (movement.movementComplete == 1 && movement.action !== "Assign") {
|
||||
completeData.push(movement);
|
||||
} else {
|
||||
completeStationData.push(movement);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -826,7 +1021,7 @@
|
||||
data: notCompleteData,
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ title: "Action", data: "action" },
|
||||
{ title: "Send Date", data: "sendDate" },
|
||||
@ -847,7 +1042,7 @@
|
||||
data: completeData,
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; } },
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ title: "Send Date", data: "sendDate" },
|
||||
{ title: "Receive Date", data: "receiveDate" },
|
||||
@ -867,6 +1062,25 @@
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
// Table 3: Station Movements
|
||||
this.assignStationDatatable = $("#assignStationDatatable").DataTable({
|
||||
data: completeStationData,
|
||||
columns: [
|
||||
{ title: "Unique Id", data: "id" },
|
||||
{ title: "Product Name", data: "productName", render: (data, type, full) => { return `${data} <br> ${renderFile(full.productImage)}`; }},
|
||||
{ title: "Product Code", data: "uniqueID" },
|
||||
{ title: "Assign Date", data: "sendDate" },
|
||||
{ title: "Action", data: "action" },
|
||||
{ title: "Station User PIC", data: "toUserName" },
|
||||
{ title: "From Station", data: "toStationName" },
|
||||
{ title: "Last Station", data: "lastStationName" },
|
||||
{ title: "Qty", data: "quantity" },
|
||||
{ title: "Note", data: "consignmentNote", render: renderFile },
|
||||
{ title: "Remark", data: "remark" },
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
// Function to render file (image/PDF)
|
||||
function renderFile(data, type, full, meta) {
|
||||
if (!data) {
|
||||
@ -917,6 +1131,22 @@
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
directives: {
|
||||
clickOutside: {
|
||||
beforeMount(el, binding) {
|
||||
el.clickOutsideEvent = (event) => {
|
||||
if (!(el.contains(event.target))) {
|
||||
binding.value?.(); // Guna optional chaining untuk elak error
|
||||
}
|
||||
};
|
||||
document.body.addEventListener("click", el.clickOutsideEvent);
|
||||
},
|
||||
unmounted(el) {
|
||||
document.body.removeEventListener("click", el.clickOutsideEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@ -573,14 +573,14 @@
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch suppliers');
|
||||
throw new Error('Failed to fetch Station');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
this.stations = data.filter(station => station.stationPicID === this.userId);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching suppliers:', error);
|
||||
console.error('Error fetching Station:', error);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<h1 class="font-light text-white">
|
||||
<i class="mdi mdi-factory"></i>
|
||||
</h1>
|
||||
<h6 class="text-white">Manifacturer</h6>
|
||||
<h6 class="text-white">Manufacturer</h6>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -69,45 +69,6 @@ namespace PSTW_CentralSystem.Controllers.API
|
||||
return StatusCode(500, $"An error occurred: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
//[HttpPost("GetTechnicianUserInformation")]
|
||||
//public async Task<IActionResult> GetTechnicianUserInformation()
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// var users = await _identityDbContext.Users
|
||||
// .Include(u => u.Department)
|
||||
// .ToListAsync(); // Retrieve all users with department info
|
||||
|
||||
// var technicianUsers = new List<object>();
|
||||
|
||||
// foreach (var user in users)
|
||||
// {
|
||||
// var roles = await _userManager.GetRolesAsync(user);
|
||||
// if (roles.Contains("Technician"))
|
||||
// {
|
||||
// technicianUsers.Add(new
|
||||
// {
|
||||
// id = user.Id,
|
||||
// fullname = user.FullName,
|
||||
// department = user.Department
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!technicianUsers.Any())
|
||||
// {
|
||||
// return NotFound(new { message = "No technicians found" });
|
||||
// }
|
||||
|
||||
// return Ok(new { technicianUsers = technicianUsers });
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return StatusCode(500, new { message = $"An error occurred: {ex.Message}" });
|
||||
// }
|
||||
|
||||
//}
|
||||
#endregion User
|
||||
|
||||
#region LDAP Login
|
||||
|
||||
@ -777,6 +777,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
ToUserName = i.NextUser?.FullName,
|
||||
ToStoreName = i.NextStore?.StoreName,
|
||||
ToStationName = i.NextStation?.StationName,
|
||||
ProductImage = i.Item?.Product?.ImageProduct,
|
||||
i.ToOther,
|
||||
i.sendDate,
|
||||
i.Action,
|
||||
@ -1503,6 +1504,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
i.Id,
|
||||
i.FullName,
|
||||
i.Department,
|
||||
i.Department?.DepartmentName,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -552,7 +552,7 @@
|
||||
<div class="ms-auto text-end">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="#">Home</a></li>
|
||||
<li class="breadcrumb-item"><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">
|
||||
Library
|
||||
</li>
|
||||
|
||||
@ -123,7 +123,7 @@
|
||||
<!-- ============================================================== -->
|
||||
<!-- Logo -->
|
||||
<!-- ============================================================== -->
|
||||
<a class="navbar-brand" href="index.html">
|
||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">
|
||||
<!-- Logo icon -->
|
||||
<b class="logo-icon ps-2">
|
||||
<!--You can put here icon as well // <i class="wi wi-sunset"></i> //-->
|
||||
@ -747,7 +747,7 @@
|
||||
<!-- footer -->
|
||||
<!-- ============================================================== -->
|
||||
<footer class="footer text-center">
|
||||
All Rights Reserved by Matrix-admin. Designed and Developed by
|
||||
All Rights Reserved by PSTW. Designed and Developed by
|
||||
<a href="https://www.wrappixel.com">WrapPixel</a>.
|
||||
</footer>
|
||||
<!-- ============================================================== -->
|
||||
|
||||
Loading…
Reference in New Issue
Block a user