Item Movement & request
This commit is contained in:
parent
37ea90be65
commit
1542750302
@ -577,8 +577,8 @@
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
groupedItems() {
|
||||
return this.itemMovements.reduce((acc, movement) => {
|
||||
processedGroupedItems() {
|
||||
let grouped = this.itemMovements.reduce((acc, movement) => {
|
||||
if (!acc[movement.itemId]) {
|
||||
acc[movement.itemId] = {
|
||||
uniqueID: movement.uniqueID,
|
||||
@ -588,6 +588,24 @@
|
||||
acc[movement.itemId].movements.push(movement);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Sort items from newest to oldest & filter them
|
||||
for (let itemId in grouped) {
|
||||
let movements = grouped[itemId].movements
|
||||
.sort((a, b) => b.id - a.id); // Newest to oldest
|
||||
|
||||
let stopIndex = movements.findIndex(m =>
|
||||
m.toOther === 'Return' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
if (stopIndex !== -1) {
|
||||
movements = movements.slice(0, stopIndex);
|
||||
}
|
||||
|
||||
grouped[itemId].movements = movements;
|
||||
}
|
||||
|
||||
return grouped;
|
||||
},
|
||||
|
||||
groupedByStation() {
|
||||
@ -659,11 +677,11 @@
|
||||
|
||||
filteredItems() {
|
||||
if (!this.searchQuery.trim()) {
|
||||
return this.groupedItems;
|
||||
return this.processedGroupedItems;
|
||||
}
|
||||
const searchLower = this.searchQuery.toLowerCase();
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.groupedItems).filter(([_, group]) =>
|
||||
Object.entries(this.processedGroupedItems).filter(([_, group]) =>
|
||||
group.uniqueID.toLowerCase().includes(searchLower)
|
||||
)
|
||||
);
|
||||
|
||||
@ -4,6 +4,62 @@
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartialUser.cshtml")
|
||||
<style>
|
||||
.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;
|
||||
}
|
||||
|
||||
.modal-body img {
|
||||
max-width: 100%; /* Ensure it fits the modal */
|
||||
max-height: 150px; /* Adjust max height */
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="requestProduct" class="row">
|
||||
|
||||
<!-- Document Preview Modal -->
|
||||
@ -74,32 +130,71 @@
|
||||
<div class="row register-form">
|
||||
<div class="col-md-13">
|
||||
|
||||
<!-- Product Dropdown -->
|
||||
<!-- Product Name -->
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Product</label>
|
||||
<label class="col-sm-2 col-form-label">Product</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="dropdown">
|
||||
<select class="btn btn-primary dropdown-toggle col-md-12" data-toggle="dropdown" aria-expanded="false" v-model="productId" required>
|
||||
<option class="btn-light" value="" disabled selected>Select Product</option>
|
||||
<option class="btn-light" v-for="(item, index) in products" :key="index" :value="item.productId">
|
||||
<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="searchQuery"
|
||||
placeholder="Search product..." 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 filteredProducts"
|
||||
:key="index" class="dropdown-item" v-on:mousedown.prevent="selectProduct(item)">
|
||||
{{ item.productName + ' (' + item.modelNo + ')' }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Category Dropdown -->
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Category:</label>
|
||||
<label class="col-sm-2 col-form-label">Category:</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" id="category" name="category" v-model="showProduct.category" class="form-control" readonly />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image Product Dropdown-->
|
||||
<div class="form-group row align-items-center">
|
||||
<label for="imageProduct" class="col-sm-2 col-form-label">Product Image: </label>
|
||||
<div class="col-sm-8">
|
||||
<img v-if="showProduct.imageProduct" :src="showProduct.imageProduct" alt="Product Image" class="img-fluid" data-toggle="modal" data-target="#imageModal" />
|
||||
<input type="hidden" id="imageProduct" name="imageProduct" v-model="showProduct">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="imageModal" tabindex="-1" role="dialog" aria-labelledby="imageModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="imageModalLabel">Product Image</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<img :src="showProduct.imageProduct" alt="Product Image" class="img-fluid">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quantity Input -->
|
||||
<div class="form-group row d-flex align-items-center">
|
||||
<label class="col-sm-4 col-form-label">Quantity:</label>
|
||||
<label class="col-sm-2 col-form-label">Quantity:</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" class="form-control" v-model="quantity" required />
|
||||
</div>
|
||||
@ -107,7 +202,7 @@
|
||||
|
||||
@* Who will assign to *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Item Assignment : </label>
|
||||
<label class="col-sm-2 col-form-label">Item Assignment : </label>
|
||||
<div class="col-sm-8">
|
||||
<div class="dropdown">
|
||||
<select class="btn btn-primary dropdown-toggle col-md-12" v-model="assign"required>
|
||||
@ -120,7 +215,7 @@
|
||||
|
||||
<!-- Station Dropdown -->
|
||||
<div class="form-group row" v-if="assign === 'Station'">
|
||||
<label class="col-sm-4 col-form-label">Station:</label>
|
||||
<label class="col-sm-2 col-form-label">Station:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="dropdown">
|
||||
<select class="btn btn-primary dropdown-toggle col-md-12" data-toggle="dropdown" aria-expanded="false" v-model="stationId" required>
|
||||
@ -136,7 +231,7 @@
|
||||
|
||||
<!-- Remark Input (Wider) -->
|
||||
<div class="form-group row d-flex align-items-center">
|
||||
<label class="col-sm-4 col-form-label">Remark:</label>
|
||||
<label class="col-sm-2 col-form-label">Remark:</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control col-md-10" v-model="remark" />
|
||||
</div>
|
||||
@ -144,7 +239,7 @@
|
||||
|
||||
<!-- Document/Picture Input -->
|
||||
<div class="form-group row d-flex align-items-center">
|
||||
<label class="col-sm-4 col-form-label">Document/Picture:</label>
|
||||
<label class="col-sm-2 col-form-label">Document/Picture:</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="file" id="document" name="document" class="form-control-file" v-on:change="handleFileUpload" accept="image/png, image/jpeg, application/pdf" />
|
||||
</div>
|
||||
@ -205,6 +300,8 @@
|
||||
assign: "",
|
||||
|
||||
productName: null,
|
||||
searchQuery: "",
|
||||
dropdownOpen: false,
|
||||
stations: [],
|
||||
selectedProduct: "",
|
||||
selectedStation: "",
|
||||
@ -236,8 +333,25 @@
|
||||
|
||||
return product ? product : {};
|
||||
},
|
||||
filteredProducts() {
|
||||
return this.products.filter(item =>
|
||||
item.productName.toLowerCase().includes(this.searchQuery.toLowerCase()) ||
|
||||
item.modelNo.toLowerCase().includes(this.searchQuery.toLowerCase())
|
||||
);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeDropdown() {
|
||||
this.dropdownOpen = false; // Tutup dropdown
|
||||
},
|
||||
|
||||
selectProduct(item) {
|
||||
this.selectedProduct = item;
|
||||
this.productId = item.productId;
|
||||
this.searchQuery = item.productName + " (" + item.modelNo + ")";
|
||||
this.dropdownOpen = false;
|
||||
},
|
||||
|
||||
handleFileUpload(event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
@ -251,6 +365,7 @@
|
||||
this.document = null;
|
||||
}
|
||||
},
|
||||
|
||||
async addRequest() {
|
||||
try {
|
||||
const requiredFields = ['productId', 'quantity', 'assign'];
|
||||
@ -308,11 +423,11 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
this.resetForm();
|
||||
alert('Success!', 'Request form has been successfully submitted.', 'success');
|
||||
const requestItem = await response.json();
|
||||
this.request.push(requestItem);
|
||||
this.fetchRequest();
|
||||
this.resetForm();
|
||||
} else {
|
||||
throw new Error('Failed to submit form.');
|
||||
}
|
||||
@ -337,6 +452,15 @@
|
||||
$(td).attr('id', `qr${cellData}`);
|
||||
},
|
||||
},
|
||||
{ "title": "Product Image",
|
||||
"data": "productPicture",
|
||||
"render": function (data, type, full, meta) {
|
||||
var image = `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
return image;
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Product Id",
|
||||
"data": "productName",
|
||||
@ -432,6 +556,19 @@
|
||||
$(td).attr('id', `qr${cellData}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Status",
|
||||
"data": "status",
|
||||
},
|
||||
{ "title": "Product Image",
|
||||
"data": "productPicture",
|
||||
"render": function (data, type, full, meta) {
|
||||
var image = `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
return image;
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Product Id",
|
||||
"data": "productName",
|
||||
@ -494,10 +631,6 @@
|
||||
{
|
||||
"title": "Approval Date",
|
||||
"data": "approvalDate",
|
||||
},
|
||||
{
|
||||
"title": "Status",
|
||||
"data": "status",
|
||||
}
|
||||
|
||||
],
|
||||
@ -605,6 +738,7 @@
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.searchQuery = "";
|
||||
this.stationId = "";
|
||||
this.productId = "";
|
||||
this.remark = "";
|
||||
@ -660,6 +794,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>
|
||||
}
|
||||
@ -945,6 +945,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
i.requestID,
|
||||
i.ProductId,
|
||||
productName = i.Product?.ProductName,
|
||||
productPicture = i.Product?.ImageProduct,
|
||||
i.UserId,
|
||||
i.status,
|
||||
i.StationId,
|
||||
@ -962,13 +963,20 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
[HttpDelete("DeleteRequest/{requestId}")]
|
||||
public async Task<IActionResult> DeleteRequest(int requestId)
|
||||
{
|
||||
var request = await _centralDbContext.Requests.FindAsync(requestId);
|
||||
if (request == null)
|
||||
var requestApprove = _centralDbContext.Requests.FirstOrDefault(r => r.requestID == requestId && r.approvalDate != null);
|
||||
var requestDelete = _centralDbContext.Requests.FirstOrDefault(r => r.requestID == requestId);
|
||||
|
||||
if (requestApprove != null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Request not found Or Admin Already Approve or Reject" });
|
||||
}
|
||||
|
||||
if (requestDelete == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Request not found" });
|
||||
}
|
||||
|
||||
_centralDbContext.Requests.Remove(request);
|
||||
_centralDbContext.Requests.Remove(requestDelete);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { success = true, message = "Request deleted successfully" });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user