Backend pagination for Item, Pr Tracking, approved pr, Deleted PR
This commit is contained in:
parent
1b06999f15
commit
d770613286
@ -2,19 +2,13 @@
|
|||||||
using CPRNIMS.Domain.Contracts.PR;
|
using CPRNIMS.Domain.Contracts.PR;
|
||||||
using CPRNIMS.Domain.Services;
|
using CPRNIMS.Domain.Services;
|
||||||
using CPRNIMS.Infrastructure.Dto.Items;
|
using CPRNIMS.Infrastructure.Dto.Items;
|
||||||
using CPRNIMS.Infrastructure.Dto.PO;
|
|
||||||
using CPRNIMS.Infrastructure.Dto.PR;
|
using CPRNIMS.Infrastructure.Dto.PR;
|
||||||
using CPRNIMS.Infrastructure.Entities.PO;
|
|
||||||
using CPRNIMS.Infrastructure.Entities.Purchasing;
|
|
||||||
using CPRNIMS.Infrastructure.Helper;
|
using CPRNIMS.Infrastructure.Helper;
|
||||||
using CPRNIMS.Infrastructure.Models.Common;
|
using CPRNIMS.Infrastructure.Models.Common;
|
||||||
using CPRNIMS.Infrastructure.ViewModel.Common;
|
using CPRNIMS.Infrastructure.ViewModel.Common;
|
||||||
using CPRNIMS.Infrastructure.ViewModel.PR;
|
using CPRNIMS.Infrastructure.ViewModel.PR;
|
||||||
using CPRNIMS.WebApi.Controllers.Base;
|
using CPRNIMS.WebApi.Controllers.Base;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Reflection.Metadata.Ecma335;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
|
||||||
@ -377,6 +371,15 @@ namespace CPRNIMS.WebApi.Controllers.PR
|
|||||||
nameof(GetAllPR), false
|
nameof(GetAllPR), false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
[HttpPost("GetPRArchived")]
|
||||||
|
public async Task<IActionResult> GetPRArchived(PRDto PRDto)
|
||||||
|
{
|
||||||
|
return await ExecuteWithErrorHandling(
|
||||||
|
() => _pRequest.GetPRArchived(PRDto),
|
||||||
|
nameof(GetPRArchived), false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("GetApprovedPR")]
|
[HttpPost("GetApprovedPR")]
|
||||||
public async Task<IActionResult> GetApprovedPR(PRDto PRDto)
|
public async Task<IActionResult> GetApprovedPR(PRDto PRDto)
|
||||||
{
|
{
|
||||||
@ -385,12 +388,12 @@ namespace CPRNIMS.WebApi.Controllers.PR
|
|||||||
nameof(GetApprovedPR), false
|
nameof(GetApprovedPR), false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
[HttpPost("GetRemovedPR")]
|
[HttpPost("GetDeletedPR")]
|
||||||
public async Task<IActionResult> GetRemovedPR(PRDto PRDto)
|
public async Task<IActionResult> GetDeletedPR(PRDto PRDto)
|
||||||
{
|
{
|
||||||
return await ExecuteWithErrorHandling(
|
return await ExecuteWithErrorHandling(
|
||||||
() => _pRequest.GetRemovedPR(PRDto),
|
() => _pRequest.GetDeletedPR(PRDto),
|
||||||
nameof(GetRemovedPR), false
|
nameof(GetDeletedPR), false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
[HttpPost("GetMyPR")]
|
[HttpPost("GetMyPR")]
|
||||||
|
|||||||
@ -326,11 +326,25 @@ namespace CPRNIMS.WebApps.Controllers.Items
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async Task<IActionResult> GetItemList()
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetItemList(string searchTerm = "", int pageNumber = 1, int pageSize = 10)
|
||||||
{
|
{
|
||||||
var viewModels = new ItemVM();
|
var viewModel = new ItemVM
|
||||||
response = await _item.GetItemList(GetUser(), viewModels);
|
{
|
||||||
return GetResponse(response);
|
SearchTerm = searchTerm,
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _item.GetItemList(GetUser(), viewModel);
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
draw = Request.Query["draw"].ToString(),
|
||||||
|
recordsTotal = result.TotalCount,
|
||||||
|
recordsFiltered = result.TotalCount,
|
||||||
|
data = result.Data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public async Task<IActionResult> GetItemCateg(ItemVM viewModels)
|
public async Task<IActionResult> GetItemCateg(ItemVM viewModels)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -21,20 +21,134 @@ namespace CPRNIMS.WebApps.Controllers.PR
|
|||||||
_pRequest = pRequest;
|
_pRequest = pRequest;
|
||||||
}
|
}
|
||||||
#region Get
|
#region Get
|
||||||
public async Task<IActionResult> GetAllPR(PRVM viewModels)
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetAllPR(
|
||||||
|
string searchPRNo = "", string searchItemName = "",
|
||||||
|
string searchDept = "", int pageNumber = 1, int pageSize = 10)
|
||||||
{
|
{
|
||||||
response = await _pRequest.GetAllPR(GetUser(), viewModels);
|
var dto = new PRVM
|
||||||
return GetResponse(response);
|
{
|
||||||
|
SearchPRNo = searchPRNo,
|
||||||
|
SearchItemName = searchItemName,
|
||||||
|
SearchDept = searchDept,
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _pRequest.GetAllPR(GetUser(),dto);
|
||||||
|
int draw = int.TryParse(Request.Query["draw"], out int d) ? d : 1;
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
draw = draw,
|
||||||
|
recordsTotal = result.TotalCount,
|
||||||
|
recordsFiltered = result.TotalCount,
|
||||||
|
data = result.Data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public async Task<IActionResult> GetApprovedPR(PRVM viewModels)
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetPRArchived(
|
||||||
|
string searchPRNo = "", string searchItemName = "",
|
||||||
|
string searchDept = "", int pageNumber = 1, int pageSize = 10)
|
||||||
{
|
{
|
||||||
response = await _pRequest.GetApprovedPR(GetUser(), viewModels);
|
var dto = new PRVM
|
||||||
return GetResponse(response);
|
{
|
||||||
|
SearchPRNo = searchPRNo,
|
||||||
|
SearchItemName = searchItemName,
|
||||||
|
SearchDept = searchDept,
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _pRequest.GetPRArchived(GetUser(), dto);
|
||||||
|
int draw = int.TryParse(Request.Query["draw"], out int d) ? d : 1;
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
draw = draw,
|
||||||
|
recordsTotal = result.TotalCount,
|
||||||
|
recordsFiltered = result.TotalCount,
|
||||||
|
data = result.Data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public async Task<IActionResult> GetRemovedPR(PRVM viewModels)
|
public async Task<IActionResult> GetDetailedPRTracking(
|
||||||
|
string searchPRNo = "", string searchItemName = "", string searchDept = "", string searchStatusName = "",
|
||||||
|
int pageNumber = 1, int pageSize = 10)
|
||||||
{
|
{
|
||||||
response = await _pRequest.GetRemovedPR(GetUser(), viewModels);
|
var dto = new PRVM
|
||||||
return GetResponse(response);
|
{
|
||||||
|
SearchPRNo = searchPRNo,
|
||||||
|
SearchItemName = searchItemName,
|
||||||
|
SearchDept = searchDept,
|
||||||
|
SearchStatusName = searchStatusName,
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _pRequest.GetDetailedPRTracking(GetUser(), dto);
|
||||||
|
int draw = int.TryParse(Request.Query["draw"], out int d) ? d : 1;
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
draw = draw,
|
||||||
|
recordsTotal = result.TotalCount,
|
||||||
|
recordsFiltered = result.TotalCount,
|
||||||
|
data = result.Data,
|
||||||
|
statusList = result.StatusList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetApprovedPR(
|
||||||
|
string searchPRNo = "", string searchItemName = "", string searchDept = "", string searchStatusName = "",
|
||||||
|
int pageNumber = 1, int pageSize = 10)
|
||||||
|
{
|
||||||
|
var dto = new PRVM
|
||||||
|
{
|
||||||
|
SearchPRNo = searchPRNo,
|
||||||
|
SearchItemName = searchItemName,
|
||||||
|
SearchDept = searchDept,
|
||||||
|
SearchStatusName = searchStatusName,
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _pRequest.GetApprovedPR(GetUser(),dto);
|
||||||
|
int draw = int.TryParse(Request.Query["draw"], out int d) ? d : 1;
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
draw = draw,
|
||||||
|
recordsTotal = result.TotalCount,
|
||||||
|
recordsFiltered = result.TotalCount,
|
||||||
|
data = result.Data,
|
||||||
|
statusList = result.StatusList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetDeletedPR(
|
||||||
|
bool isArchived = false, string searchPRNo = "", string searchItemName = "",
|
||||||
|
string searchDept = "", int pageNumber = 1, int pageSize = 10)
|
||||||
|
{
|
||||||
|
var dto = new PRVM
|
||||||
|
{
|
||||||
|
IsArchived = isArchived,
|
||||||
|
SearchPRNo = searchPRNo,
|
||||||
|
SearchItemName = searchItemName,
|
||||||
|
SearchDept = searchDept,
|
||||||
|
PageNumber = pageNumber,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _pRequest.GetDeletedPR(GetUser(), dto);
|
||||||
|
int draw = int.TryParse(Request.Query["draw"], out int d) ? d : 1;
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
draw = draw,
|
||||||
|
recordsTotal = result.TotalCount,
|
||||||
|
recordsFiltered = result.TotalCount,
|
||||||
|
data = result.Data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetPRAttachment(string fileName)
|
public async Task<IActionResult> GetPRAttachment(string fileName)
|
||||||
@ -127,11 +241,6 @@ namespace CPRNIMS.WebApps.Controllers.PR
|
|||||||
response = await _pRequest.GetItemDetailForReceiving(GetUser(), viewModel);
|
response = await _pRequest.GetItemDetailForReceiving(GetUser(), viewModel);
|
||||||
return GetResponse(response);
|
return GetResponse(response);
|
||||||
}
|
}
|
||||||
public async Task<IActionResult> GetDetailedPRTracking(PRVM viewModel)
|
|
||||||
{
|
|
||||||
response = await _pRequest.GetDetailedPRTracking(GetUser(), viewModel);
|
|
||||||
return GetResponse(response);
|
|
||||||
}
|
|
||||||
public async Task<IActionResult> GetSupplierAlternativeOffer(PRVM viewModel)
|
public async Task<IActionResult> GetSupplierAlternativeOffer(PRVM viewModel)
|
||||||
{
|
{
|
||||||
response = await _pRequest.GetSupplierAlternativeOffer(GetUser(), viewModel);
|
response = await _pRequest.GetSupplierAlternativeOffer(GetUser(), viewModel);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ namespace CPRNIMS.WebApps.ViewComponents.PR
|
|||||||
{
|
{
|
||||||
1 => "~/Views/Components/PRMgmt/PRTabbedTable/AllPR.cshtml",
|
1 => "~/Views/Components/PRMgmt/PRTabbedTable/AllPR.cshtml",
|
||||||
2 => "~/Views/Components/PRMgmt/PRTabbedTable/ApprovedPR.cshtml",
|
2 => "~/Views/Components/PRMgmt/PRTabbedTable/ApprovedPR.cshtml",
|
||||||
_ => "~/Views/Components/PRMgmt/PRTabbedTable/RemovedPR.cshtml"
|
_ => "~/Views/Components/PRMgmt/PRTabbedTable/DeletedPR.cshtml"
|
||||||
};
|
};
|
||||||
return View(viewName);
|
return View(viewName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,53 @@
|
|||||||
<!-- AllPR.cshtml - Example View Component -->
|
<div class="tab-content-wrapper">
|
||||||
<div class="tab-content-wrapper">
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||||
|
<div class="search-wrapper" style="min-width:180px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchPRNo" class="search-input"
|
||||||
|
placeholder="PR Number…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchPRNo" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-wrapper" style="min-width:220px; flex:2;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchItem" class="search-input"
|
||||||
|
placeholder="Item Name…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchItem" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-wrapper" style="min-width:180px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchDept" class="search-input"
|
||||||
|
placeholder="Department…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchDept" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<table id="PRTable" class="row-border" cellspacing="0" width="100%">
|
<table id="PRTable" class="row-border" cellspacing="0" width="100%">
|
||||||
<thead >
|
<thead >
|
||||||
<tr>
|
<tr>
|
||||||
@ -19,93 +67,84 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
loader = $('#overlay, #loader');
|
loader = $('#overlay, #loader');
|
||||||
|
|
||||||
UserRights = document.getElementById("roleRights").value;
|
UserRights = document.getElementById("roleRights").value;
|
||||||
|
|
||||||
const reportTitle = `All Purchase Request - as of ${getFormattedDateTime()}`;
|
const reportTitle = `All Purchase Request - as of ${getFormattedDateTime()}`;
|
||||||
|
|
||||||
prTable = $('#PRTable').DataTable({
|
prTable = $('#PRTable').DataTable({
|
||||||
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/PRMgmt/GetAllPR',
|
url: endpoint.GetAllPR,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
beforeSend: function () {
|
data: function (d) {
|
||||||
loader.show();
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchPRNo: ($('#srchPRNo').val() || '').trim(),
|
||||||
|
searchItemName:($('#srchItem').val()|| '').trim(),
|
||||||
|
searchDept: ($('#srchDept').val() || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
},
|
},
|
||||||
complete: function () {
|
dataSrc: function (json) { return json.data; },
|
||||||
loader.hide();
|
beforeSend: function () { loader.show(); },
|
||||||
},
|
complete: function () { loader.hide(); },
|
||||||
error: function (xhr, error, thrown) {
|
error: function (xhr, error) {
|
||||||
loader.hide();
|
loader.hide();
|
||||||
console.error('Error loading data:', error);
|
console.error('Error loading data:', error);
|
||||||
if (typeof toastr !== 'undefined') {
|
if (typeof toastr !== 'undefined')
|
||||||
toastr.error('Failed to load data. Please try again.');
|
toastr.error('Failed to load data. Please try again.');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dom: 'lBfrtip',
|
dom: 'lBfrtip',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
|
||||||
extend: 'csv',
|
|
||||||
title: reportTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extend: 'excel',
|
|
||||||
title: reportTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extend: 'pdf',
|
|
||||||
title: reportTitle
|
|
||||||
}
|
|
||||||
],
|
|
||||||
initComplete: function () {
|
|
||||||
initializeColumnSearch({
|
|
||||||
tableId: '#PRTable',
|
|
||||||
dataTable: prTable,
|
|
||||||
searchableColumns: [
|
|
||||||
{
|
{
|
||||||
columnIndex: 0,
|
text: '<i class="fas fa-file-csv"></i> CSV',
|
||||||
columnName: 'PR No',
|
className: 'btn btn-sm btn-secondary',
|
||||||
placeholder: 'Enter PR Number...',
|
action: function () { exportAllData('csv', endpoint.GetAllPR, loader, reportTitle, colOnPRTable); }
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
columnIndex: 2,
|
text: '<i class="fas fa-file-excel"></i> Excel',
|
||||||
columnName: 'Item Name',
|
className: 'btn btn-sm btn-success',
|
||||||
placeholder: 'Enter Item Name...',
|
action: function () { exportAllData('excel', endpoint.GetAllPR, loader, reportTitle, colOnPRTable); }
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'contains',
|
|
||||||
width: '200px'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
columnIndex: 6,
|
text: '<i class="fas fa-file-pdf"></i> PDF',
|
||||||
columnName: 'Department',
|
className: 'btn btn-sm btn-danger',
|
||||||
placeholder: 'Select Department...',
|
action: function () { exportAllData('pdf', endpoint.GetAllPR, loader ,reportTitle, colOnPRTable); }
|
||||||
searchType: 'select',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
});
|
columns: colOnPRTable,
|
||||||
|
order: [[0, 'asc']],
|
||||||
const uniqueSearchClass = 'column-search-input-PRTable';
|
rowCallback: rowStatusColorCallback,
|
||||||
|
|
||||||
populateSelectOptions(prTable, 6, '.' + uniqueSearchClass + '[data-column="6"]');
|
|
||||||
|
|
||||||
restoreSearchFromURL(uniqueSearchClass, '#PRTable');
|
|
||||||
},
|
|
||||||
columns: colOnPRTable,
|
|
||||||
order: [[3, 'asc']],
|
|
||||||
rowCallback: rowStatusColorCallback,
|
|
||||||
responsive: true,
|
|
||||||
language: {
|
language: {
|
||||||
emptyTable: "No record available",
|
emptyTable: "No record available",
|
||||||
loadingRecords: "Loading data...",
|
processing: "Loading…"
|
||||||
processing: "Processing..."
|
|
||||||
},
|
},
|
||||||
error: errorHandler
|
error: errorHandler
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let t;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#srchPRNo, #srchItem, #srchDept')
|
||||||
|
.on('keyup', debounce(function () {
|
||||||
|
prTable.ajax.reload();
|
||||||
|
}, 400));
|
||||||
|
|
||||||
|
$('.pr-search-clear').on('click', function () {
|
||||||
|
const target = $(this).data('target');
|
||||||
|
$(target).val('');
|
||||||
|
prTable.ajax.reload();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -1,4 +1,74 @@
|
|||||||
<div class="tab-content-wrapper">
|
<div class="tab-content-wrapper">
|
||||||
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||||
|
|
||||||
|
<!-- Status dropdown -->
|
||||||
|
<div class="search-wrapper" style="min-width:170px; flex:1;">
|
||||||
|
<div class="search-inner" style="padding-right:4px;">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<path d="M3 6h18M7 12h10M11 18h2" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<select id="srchStatus" class="search-input" style="cursor:pointer;">
|
||||||
|
<option value="">All Statuses</option>
|
||||||
|
<!-- populated dynamically from first API response -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PR No -->
|
||||||
|
<div class="search-wrapper" style="min-width:150px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchPRNo" class="search-input"
|
||||||
|
placeholder="PR Number…" autocomplete="off" />
|
||||||
|
<span class="search-clear aprv-search-clear"
|
||||||
|
data-target="#srchPRNo" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Item Name -->
|
||||||
|
<div class="search-wrapper" style="min-width:200px; flex:2;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchItem" class="search-input"
|
||||||
|
placeholder="Item Name…" autocomplete="off" />
|
||||||
|
<span class="search-clear aprv-search-clear"
|
||||||
|
data-target="#srchItem" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Department -->
|
||||||
|
<div class="search-wrapper" style="min-width:170px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchDept" class="search-input"
|
||||||
|
placeholder="Department…" autocomplete="off" />
|
||||||
|
<span class="search-clear aprv-search-clear"
|
||||||
|
data-target="#srchDept" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<table id="ApprovedPRTable" class="row-border" cellspacing="0" width="100%">
|
<table id="ApprovedPRTable" class="row-border" cellspacing="0" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -23,144 +93,88 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
|
const reportTitle = `Approved Purchase Request (No PO) - as of ${getFormattedDateTime()}`;
|
||||||
|
|
||||||
const reportTitle = `Approved Purchase Request (No PO) - as of ${getFormattedDateTime()}`;
|
var approvedPRTable = $('#ApprovedPRTable').DataTable({
|
||||||
var approvedPRTable = $('#ApprovedPRTable').DataTable({
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
ajax: {
|
ajax: {
|
||||||
url: '/PRMgmt/GetApprovedPR',
|
url: endpoint.GetApprovedPR,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
beforeSend: function () {
|
data: function (d) {
|
||||||
loader.show();
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchPRNo: ($('#srchPRNo').val() || '').trim(),
|
||||||
|
searchItemName: ($('#srchItem').val() || '').trim(),
|
||||||
|
searchDept: ($('#srchDept').val() || '').trim(),
|
||||||
|
searchStatusName: ($('#srchStatus').val() || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
},
|
},
|
||||||
complete: function () {
|
dataSrc: function (json) {
|
||||||
loader.hide();
|
const $sel = $('#srchStatus');
|
||||||
|
if ($sel.find('option').length <= 1 && json.statusList?.length) {
|
||||||
|
json.statusList.forEach(function (s) {
|
||||||
|
$sel.append(`<option value="${s}">${s}</option>`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return json.data;
|
||||||
},
|
},
|
||||||
error: function (xhr, error, thrown) {
|
beforeSend: function () { loader.show(); },
|
||||||
|
complete: function () { loader.hide(); },
|
||||||
|
error: function (xhr, error) {
|
||||||
loader.hide();
|
loader.hide();
|
||||||
console.error('Error loading data:', error);
|
if (typeof toastr !== 'undefined')
|
||||||
if (typeof toastr !== 'undefined') {
|
|
||||||
toastr.error('Failed to load data. Please try again.');
|
toastr.error('Failed to load data. Please try again.');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dom: 'lBfrtip',
|
dom: 'lBfrtip',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
extend: 'csv',
|
text: '<i class="fas fa-file-csv"></i> CSV',
|
||||||
title: reportTitle
|
className: 'btn btn-sm btn-secondary',
|
||||||
|
action: function () { exportAllData('csv', endpoint.GetApprovedPR, loader, reportTitle, colOnApprovedPR); }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
extend: 'excel',
|
text: '<i class="fas fa-file-excel"></i> Excel',
|
||||||
title: reportTitle
|
className: 'btn btn-sm btn-success',
|
||||||
|
action: function () { exportAllData('excel', endpoint.GetApprovedPR, loader, reportTitle, colOnApprovedPR); }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
extend: 'pdf',
|
text: '<i class="fas fa-file-pdf"></i> PDF',
|
||||||
title: reportTitle
|
className: 'btn btn-sm btn-danger',
|
||||||
|
action: function () { exportAllData('pdf', endpoint.GetApprovedPR, loader, reportTitle, colOnApprovedPR); }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
initComplete: function () {
|
columns: colOnApprovedPR,
|
||||||
initializeColumnSearch({
|
order: [[11, 'asc']],
|
||||||
tableId: '#ApprovedPRTable',
|
|
||||||
dataTable: approvedPRTable,
|
|
||||||
searchableColumns: [
|
|
||||||
{
|
|
||||||
columnIndex: 0,
|
|
||||||
columnName: 'Status',
|
|
||||||
placeholder: 'Select Status...',
|
|
||||||
searchType: 'select',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 2,
|
|
||||||
columnName: 'PR No',
|
|
||||||
placeholder: 'Enter PR Number...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 3,
|
|
||||||
columnName: 'Item No',
|
|
||||||
placeholder: 'Enter Item Number...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 4,
|
|
||||||
columnName: 'Item Name',
|
|
||||||
placeholder: 'Enter Item Name...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'contains',
|
|
||||||
width: '200px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 12,
|
|
||||||
columnName: 'Department',
|
|
||||||
placeholder: 'Select Department...',
|
|
||||||
searchType: 'select',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const uniqueSearchClass = 'column-search-input-ApprovedPRTable';
|
|
||||||
|
|
||||||
populateSelectOptions(approvedPRTable, 0, '.' + uniqueSearchClass + '[data-column="0"]');
|
|
||||||
populateSelectOptions(approvedPRTable, 12, '.' + uniqueSearchClass + '[data-column="12"]');
|
|
||||||
|
|
||||||
restoreSearchFromURL(uniqueSearchClass, '#ApprovedPRTable');
|
|
||||||
},
|
|
||||||
columns: [
|
|
||||||
{ data: 'statusName' },
|
|
||||||
{ data: 'remainingDays', searchable: false },
|
|
||||||
{ data: 'prNo' },
|
|
||||||
{ data: 'itemNo' },
|
|
||||||
{ data: 'itemName' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'qty',
|
|
||||||
render: function (data) {
|
|
||||||
return numberWithCommas(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'createdBy' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'createdDate',
|
|
||||||
render: function (data) {
|
|
||||||
return formatDateTime(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'attestedBy' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'attestedDate',
|
|
||||||
render: function (data) {
|
|
||||||
return formatStrDateTime(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'approvedBy' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'approvedDate',
|
|
||||||
render: function (data) {
|
|
||||||
return formatStrDateTime(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'department' }
|
|
||||||
],
|
|
||||||
order: [[11, 'asc']],
|
|
||||||
responsive: true,
|
responsive: true,
|
||||||
language: {
|
language: { emptyTable: "No approved records available" }
|
||||||
emptyTable: "No approved records available"
|
});
|
||||||
}
|
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let t;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#srchPRNo, #srchItem, #srchDept')
|
||||||
|
.on('keyup', debounce(() => approvedPRTable.ajax.reload(), 400));
|
||||||
|
|
||||||
|
$('#srchStatus').on('change', function () {
|
||||||
|
approvedPRTable.ajax.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.aprv-search-clear').on('click', function () {
|
||||||
|
$($(this).data('target')).val('').trigger('change');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
function formatStrDateTime(dateString) {
|
function formatStrDateTime(dateString) {
|
||||||
if (!dateString || dateString === "None") return "None";
|
if (!dateString || dateString === "None") return "None";
|
||||||
|
|
||||||
let date = new Date(dateString);
|
let date = new Date(dateString);
|
||||||
|
|||||||
@ -0,0 +1,163 @@
|
|||||||
|
<div class="tab-content-wrapper">
|
||||||
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||||
|
<div class="search-wrapper" style="min-width:180px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchPRNo" class="search-input"
|
||||||
|
placeholder="PR Number…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchPRNo" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-wrapper" style="min-width:220px; flex:2;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchItem" class="search-input"
|
||||||
|
placeholder="Item Name…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchItem" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-wrapper" style="min-width:180px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchDept" class="search-input"
|
||||||
|
placeholder="Department…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchDept" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table id="DeletedPRTable" class="row-border" cellspacing="0" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>PRNo</th>
|
||||||
|
<th>ItemNo</th>
|
||||||
|
<th>ItemName</th>
|
||||||
|
<th>Qty</th>
|
||||||
|
<th>PR By</th>
|
||||||
|
<th>PR Date</th>
|
||||||
|
<th>Attested By</th>
|
||||||
|
<th>Attested Date</th>
|
||||||
|
<th>Approved By</th>
|
||||||
|
<th>Approved Date</th>
|
||||||
|
<th>Charge To</th>
|
||||||
|
<th>Remarks</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
const reportTitle = `Deleted Purchase Request - as of ${getFormattedDateTime()}`;
|
||||||
|
|
||||||
|
var deletedPRTable = $('#DeletedPRTable').DataTable({
|
||||||
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
|
ajax: {
|
||||||
|
url: endpoint.GetDeletedPR,
|
||||||
|
type: 'GET',
|
||||||
|
data: function (d) {
|
||||||
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchPRNo: ($('#srchPRNo').val() || '').trim(),
|
||||||
|
searchItemName:($('#srchItem').val()|| '').trim(),
|
||||||
|
searchDept: ($('#srchDept').val() || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dataSrc: function (json) { return json.data; },
|
||||||
|
beforeSend: function () { loader.show(); },
|
||||||
|
complete: function () { loader.hide(); },
|
||||||
|
error: function (xhr, error) {
|
||||||
|
loader.hide();
|
||||||
|
console.error('Error loading data:', error);
|
||||||
|
if (typeof toastr !== 'undefined')
|
||||||
|
toastr.error('Failed to load data. Please try again.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dom: 'lBfrtip',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: '<i class="fas fa-file-csv"></i> CSV',
|
||||||
|
className: 'btn btn-sm btn-secondary',
|
||||||
|
action: function () { exportAllData('csv', endpoint.GetDeletedPR, loader, reportTitle, colOnDeletedPR); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="fas fa-file-excel"></i> Excel',
|
||||||
|
className: 'btn btn-sm btn-success',
|
||||||
|
action: function () { exportAllData('excel', endpoint.GetDeletedPR, loader, reportTitle, colOnDeletedPR); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="fas fa-file-pdf"></i> PDF',
|
||||||
|
className: 'btn btn-sm btn-danger',
|
||||||
|
action: function () { exportAllData('pdf', endpoint.GetDeletedPR, loader, reportTitle, colOnDeletedPR); }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: colOnDeletedPR,
|
||||||
|
rowCallback: rowStatusColorCallback,
|
||||||
|
responsive: true,
|
||||||
|
language: {
|
||||||
|
emptyTable: "No record available",
|
||||||
|
processing: "Loading…"
|
||||||
|
},
|
||||||
|
error: errorHandler
|
||||||
|
});
|
||||||
|
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let t;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#srchPRNo, #srchItem, #srchDept')
|
||||||
|
.on('keyup', debounce(() => deletedPRTable.ajax.reload(), 400));
|
||||||
|
|
||||||
|
$('.pr-search-clear').on('click', function () {
|
||||||
|
const target = $(this).data('target');
|
||||||
|
$(target).val('');
|
||||||
|
deletedPRTable.ajax.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function formatStrDateTime(dateString) {
|
||||||
|
if (!dateString || dateString === "None") return "None";
|
||||||
|
|
||||||
|
let date = new Date(dateString);
|
||||||
|
let month = ('0' + (date.getMonth() + 1)).slice(-2);
|
||||||
|
let day = ('0' + date.getDate()).slice(-2);
|
||||||
|
let year = date.getFullYear();
|
||||||
|
let hours = ('0' + date.getHours()).slice(-2);
|
||||||
|
let minutes = ('0' + date.getMinutes()).slice(-2);
|
||||||
|
let seconds = ('0' + date.getSeconds()).slice(-2);
|
||||||
|
return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -1,164 +0,0 @@
|
|||||||
<div class="tab-content-wrapper">
|
|
||||||
<table id="RemovedPRTable" class="row-border" cellspacing="0" width="100%">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>PRNo</th>
|
|
||||||
<th>ItemNo</th>
|
|
||||||
<th>ItemName</th>
|
|
||||||
<th>Qty</th>
|
|
||||||
<th>PR By</th>
|
|
||||||
<th>PR Date</th>
|
|
||||||
<th>Attested By</th>
|
|
||||||
<th>Attested Date</th>
|
|
||||||
<th>Approved By</th>
|
|
||||||
<th>Approved Date</th>
|
|
||||||
<th>Charge To</th>
|
|
||||||
<th>Remarks</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function() {
|
|
||||||
|
|
||||||
const reportTitle = `Deleted Purchase Request - as of ${getFormattedDateTime()}`;
|
|
||||||
var removedPRTable = $('#RemovedPRTable').DataTable({
|
|
||||||
ajax: {
|
|
||||||
url: '/PRMgmt/GetApprovedPR',
|
|
||||||
type: 'GET',
|
|
||||||
beforeSend: function () {
|
|
||||||
loader.show();
|
|
||||||
},
|
|
||||||
complete: function () {
|
|
||||||
loader.hide();
|
|
||||||
},
|
|
||||||
error: function (xhr, error, thrown) {
|
|
||||||
loader.hide();
|
|
||||||
console.error('Error loading data:', error);
|
|
||||||
if (typeof toastr !== 'undefined') {
|
|
||||||
toastr.error('Failed to load data. Please try again.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dom: 'lBfrtip',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
extend: 'csv',
|
|
||||||
title: reportTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extend: 'excel',
|
|
||||||
title: reportTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extend: 'pdf',
|
|
||||||
title: reportTitle
|
|
||||||
}
|
|
||||||
],
|
|
||||||
initComplete: function () {
|
|
||||||
initializeColumnSearch({
|
|
||||||
tableId: '#RemovedPRTable',
|
|
||||||
dataTable: removedPRTable,
|
|
||||||
searchableColumns: [
|
|
||||||
{
|
|
||||||
columnIndex: 0,
|
|
||||||
columnName: 'PR No',
|
|
||||||
placeholder: 'Enter PR Number...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 1,
|
|
||||||
columnName: 'Item No',
|
|
||||||
placeholder: 'Enter Item Number...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 2,
|
|
||||||
columnName: 'Item Name',
|
|
||||||
placeholder: 'Enter Item Name...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'contains',
|
|
||||||
width: '200px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 10,
|
|
||||||
columnName: 'Department',
|
|
||||||
placeholder: 'Select Department...',
|
|
||||||
searchType: 'select',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const uniqueSearchClass = 'column-search-input-RemovedPRTable';
|
|
||||||
|
|
||||||
populateSelectOptions(removedPRTable, 10, '.' + uniqueSearchClass + '[data-column="10"]');
|
|
||||||
|
|
||||||
restoreSearchFromURL(uniqueSearchClass, '#RemovedPRTable');
|
|
||||||
},
|
|
||||||
columns: [
|
|
||||||
{ data: 'prNo' },
|
|
||||||
{ data: 'itemNo' },
|
|
||||||
{ data: 'itemName' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'qty',
|
|
||||||
render: function (data) {
|
|
||||||
return numberWithCommas(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'createdBy' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'createdDate',
|
|
||||||
render: function (data) {
|
|
||||||
return formatDateTime(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'attestedBy' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'attestedDate',
|
|
||||||
render: function (data) {
|
|
||||||
return formatStrDateTime(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'approvedBy' },
|
|
||||||
{
|
|
||||||
searchable: false,
|
|
||||||
data: 'approvedDate',
|
|
||||||
render: function (data) {
|
|
||||||
return formatStrDateTime(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: 'department' },
|
|
||||||
{ data: 'remarks' }
|
|
||||||
],
|
|
||||||
order: [[9, 'asc']],
|
|
||||||
responsive: true,
|
|
||||||
language: {
|
|
||||||
emptyTable: "No approved records available"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
function formatStrDateTime(dateString) {
|
|
||||||
if (!dateString || dateString === "None") return "None";
|
|
||||||
|
|
||||||
let date = new Date(dateString);
|
|
||||||
let month = ('0' + (date.getMonth() + 1)).slice(-2);
|
|
||||||
let day = ('0' + date.getDate()).slice(-2);
|
|
||||||
let year = date.getFullYear();
|
|
||||||
let hours = ('0' + date.getHours()).slice(-2);
|
|
||||||
let minutes = ('0' + date.getMinutes()).slice(-2);
|
|
||||||
let seconds = ('0' + date.getSeconds()).slice(-2);
|
|
||||||
return `${month}/${day}/${year} ${hours}:${minutes}:${seconds}`;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -10,16 +10,37 @@
|
|||||||
Add new
|
Add new
|
||||||
</button>
|
</button>
|
||||||
<br />
|
<br />
|
||||||
|
<!-- Replace your existing customSearch input with this -->
|
||||||
|
<div class="search-wrapper">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="customSearch" type="text"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="Search by item name, description or category…"
|
||||||
|
autocomplete="off" />
|
||||||
|
<span class="search-clear" id="searchClear" title="Clear">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||||
|
<path d="M18 6 6 18M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="search-hint" id="searchHint">Start typing to search items…</div>
|
||||||
|
</div>
|
||||||
<table id="ItemTable" class="row-border" cellspacing="0" width="100%">
|
<table id="ItemTable" class="row-border" cellspacing="0" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ItemNo</th>
|
<th>ItemNo</th>
|
||||||
<th>ItemName</th>
|
<th>ItemName</th>
|
||||||
<th>ItemSpecs</th>
|
<th>ItemSpecs</th>
|
||||||
<th>CategoryName</th>
|
<th>CategoryName</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
<th hidden></th>
|
|
||||||
<th hidden></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -237,7 +258,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="~/JsFunctions/Items/ItemManagementV7.js"></script>
|
<script src="~/JsFunctions/Items/ItemManagementV8.js"></script>
|
||||||
@await Html.PartialAsync("PagesView/Item/_Scripts")
|
@await Html.PartialAsync("PagesView/Item/_Scripts")
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -17,8 +17,6 @@
|
|||||||
<th>ItemCategory</th>
|
<th>ItemCategory</th>
|
||||||
<th>SupplierName</th>
|
<th>SupplierName</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
<th hidden></th>
|
|
||||||
<th hidden></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@ -33,6 +33,30 @@
|
|||||||
<h2 class="modal-title" id="addItemLabel">Item List</h2>
|
<h2 class="modal-title" id="addItemLabel">Item List</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body p-4">
|
<div class="modal-body p-4">
|
||||||
|
<!-- Inside #viewItemList modal, above the table -->
|
||||||
|
<div class="search-wrapper mb-2">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="modalSearchInput" type="text"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="Search by item name, description or category…"
|
||||||
|
autocomplete="off" />
|
||||||
|
<span class="search-clear" id="modalSearchClear" title="Clear">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||||
|
<path d="M18 6 6 18M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="search-hint" id="modalSearchHint">Start typing to search items…</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="margin-bottom:5px">
|
<div style="margin-bottom:5px">
|
||||||
<span class="fw-semibold">Selected Items:</span>
|
<span class="fw-semibold">Selected Items:</span>
|
||||||
<span id="totalSelectedItem" class="badge bg-danger ms-2">0</span>
|
<span id="totalSelectedItem" class="badge bg-danger ms-2">0</span>
|
||||||
@ -116,7 +140,7 @@
|
|||||||
<link href="~/css/pr/ButtonStyleV2.css" rel="stylesheet" />
|
<link href="~/css/pr/ButtonStyleV2.css" rel="stylesheet" />
|
||||||
<link href="~/css/pr/PRTabs.css" rel="stylesheet" />
|
<link href="~/css/pr/PRTabs.css" rel="stylesheet" />
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRTracking")
|
@await Html.PartialAsync("PagesView/PR/_PRTracking")
|
||||||
<script src="~/JsFunctions/PR/PRV9.js"></script>
|
<script src="~/JsFunctions/PR/PR.js"></script>
|
||||||
<script src="~/JsFunctions/PR/PRTabs.js"></script>
|
<script src="~/JsFunctions/PR/PRTabs.js"></script>
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
||||||
</body>
|
</body>
|
||||||
@ -5,7 +5,55 @@
|
|||||||
<h2 style="display: flex; flex-direction: column; align-items: center;">PR Archived</h2>
|
<h2 style="display: flex; flex-direction: column; align-items: center;">PR Archived</h2>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||||
|
<div class="search-wrapper" style="min-width:180px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchPRNo" class="search-input"
|
||||||
|
placeholder="PR Number…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchPRNo" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-wrapper" style="min-width:220px; flex:2;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchItemName" class="search-input"
|
||||||
|
placeholder="Item Name…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchItemName" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-wrapper" style="min-width:180px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchDept" class="search-input"
|
||||||
|
placeholder="Department…" autocomplete="off" />
|
||||||
|
<span class="search-clear pr-search-clear"
|
||||||
|
data-target="#srchDept" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<table id="PRTable" class="row-border" cellspacing="0" width="100%">
|
<table id="PRTable" class="row-border" cellspacing="0" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -17,8 +65,6 @@
|
|||||||
<th style="width:8%">DateNeeded</th>
|
<th style="width:8%">DateNeeded</th>
|
||||||
<th style="width:7%">ChargeTo</th>
|
<th style="width:7%">ChargeTo</th>
|
||||||
<th style="width:7%">Action</th>
|
<th style="width:7%">Action</th>
|
||||||
<th hidden></th>
|
|
||||||
<th hidden></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -27,7 +73,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<link href="~/css/pr/TrackingV3.css" rel="stylesheet" />
|
<link href="~/css/pr/TrackingV3.css" rel="stylesheet" />
|
||||||
|
<link href="~/css/item/item.css" rel="stylesheet" />
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRTracking")
|
@await Html.PartialAsync("PagesView/PR/_PRTracking")
|
||||||
<script src="~/JsFunctions/PR/ArchivedV2.js"></script>
|
<script src="~/JsFunctions/PR/ArchivedV3.js"></script>
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
||||||
</body>
|
</body>
|
||||||
@ -5,6 +5,76 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="table-container shadow-lg p-3 mb-3 bg-white rounded" style="overflow-x: auto;height:110%">
|
<div class="table-container shadow-lg p-3 mb-3 bg-white rounded" style="overflow-x: auto;height:110%">
|
||||||
<br />
|
<br />
|
||||||
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
||||||
|
|
||||||
|
<!-- Status dropdown -->
|
||||||
|
<div class="search-wrapper" style="min-width:170px; flex:1;">
|
||||||
|
<div class="search-inner" style="padding-right:4px;">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<path d="M3 6h18M7 12h10M11 18h2" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<select id="srchStatus" class="search-input" style="cursor:pointer;">
|
||||||
|
<option value="">All Statuses</option>
|
||||||
|
<!-- populated dynamically from first API response -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PR No -->
|
||||||
|
<div class="search-wrapper" style="min-width:150px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchPRNo" class="search-input"
|
||||||
|
placeholder="PR Number…" autocomplete="off" />
|
||||||
|
<span class="search-clear aprv-search-clear"
|
||||||
|
data-target="#srchPRNo" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Item Name -->
|
||||||
|
<div class="search-wrapper" style="min-width:200px; flex:2;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchItem" class="search-input"
|
||||||
|
placeholder="Item Name…" autocomplete="off" />
|
||||||
|
<span class="search-clear aprv-search-clear"
|
||||||
|
data-target="#srchItem" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Department -->
|
||||||
|
<div class="search-wrapper" style="min-width:170px; flex:1;">
|
||||||
|
<div class="search-inner">
|
||||||
|
<span class="search-icon">
|
||||||
|
<svg width="14" height="14" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor" stroke-width="2.5">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<path d="m21 21-4.35-4.35" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<input id="srchDept" class="search-input"
|
||||||
|
placeholder="Department…" autocomplete="off" />
|
||||||
|
<span class="search-clear aprv-search-clear"
|
||||||
|
data-target="#srchDept" title="Clear">✕</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<table id="PRTable" class="row-border" width="100%">
|
<table id="PRTable" class="row-border" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -45,6 +115,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<link href="~/css/pr/trackingdetail.css" rel="stylesheet" />
|
<link href="~/css/pr/trackingdetail.css" rel="stylesheet" />
|
||||||
<script src="~/jsfunctions/pr/DetailedPRTrackingV7.js"></script>
|
<script src="~/jsfunctions/pr/DetailedPRTrackingV8.js"></script>
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
||||||
</body>
|
</body>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
<input hidden id="roleRights" value="@ViewBag.UserRoles" />
|
<link href="~/css/item/item.css" rel="stylesheet" />
|
||||||
|
<input hidden id="roleRights" value="@ViewBag.UserRoles" />
|
||||||
<div id="overlay" class="overlay" style="display: none;">
|
<div id="overlay" class="overlay" style="display: none;">
|
||||||
<div id="loader" class="loader"></div>
|
<div id="loader" class="loader"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
<script src="~/jsfunctions/po/POButtonV9.js"></script>
|
<script src="~/jsfunctions/po/POButtonV9.js"></script>
|
||||||
<script src="~/jsfunctions/po/PrintingV2.js"></script>
|
<script src="~/jsfunctions/po/PrintingV2.js"></script>
|
||||||
<script src="~/JsFunctions/PO/POColumn.js"></script>
|
<script src="~/JsFunctions/PO/POColumn.js"></script>
|
||||||
<script src="~/jsfunctions/po/ApiV4.js"></script>
|
<script src="~/jsfunctions/po/ApiV5.js"></script>
|
||||||
<script src="~/jsfunctions/po/populatetable.js"></script>
|
<script src="~/jsfunctions/po/populatetable.js"></script>
|
||||||
<script src="~/jsfunctions/po/POVarV6.js"></script>
|
<script src="~/jsfunctions/po/POVarV6.js"></script>
|
||||||
<script src="~/jsfunctions/po/POViewV4.js"></script>
|
<script src="~/jsfunctions/po/POViewV4.js"></script>
|
||||||
|
|||||||
@ -1,20 +1,22 @@
|
|||||||
<div id="overlay" class="overlay" style="display: none;">
|
<div id="overlay" class="overlay" style="display: none;">
|
||||||
<div id="loader" class="loader"></div>
|
<div id="loader" class="loader"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<link href="~/css/item/item.css" rel="stylesheet" />
|
||||||
<link href="~/css/common/rowhighlighter.css" rel="stylesheet" />
|
<link href="~/css/common/rowhighlighter.css" rel="stylesheet" />
|
||||||
|
|
||||||
<script src="~/jsfunctions/pr/PRColumnV8.js"></script>
|
<script src="~/jsfunctions/pr/PRColumnV9.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRViewV8.js"></script>
|
<script src="~/jsfunctions/pr/PRViewV9.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRPostPut.js"></script>
|
<script src="~/jsfunctions/pr/PRPostPut.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRButtonv3.js"></script>
|
<script src="~/jsfunctions/pr/PRButtonv3.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRVarV3.js"></script>
|
<script src="~/jsfunctions/pr/PRVarV3.js"></script>
|
||||||
<script src="~/jsfunctions/pr/Configv6.js"></script>
|
<script src="~/jsfunctions/pr/ConfigV7.js"></script>
|
||||||
<script src="~/jsfunctions/pr/populatedropdown.js"></script>
|
<script src="~/jsfunctions/pr/populatedropdown.js"></script>
|
||||||
<script src="~/jsfunctions/pr/prRowCallbackV3.js"></script>
|
<script src="~/jsfunctions/pr/prRowCallbackV3.js"></script>
|
||||||
<script src="~/jsfunctions/utilities/columnstyle.js"></script>
|
<script src="~/jsfunctions/utilities/columnstyle.js"></script>
|
||||||
<script src="~/jsfunctions/tracking/tracking.js"></script>
|
<script src="~/jsfunctions/tracking/tracking.js"></script>
|
||||||
|
|
||||||
<script src="~/jsfunctions/utilities/NewStyle.js"></script>
|
<script src="~/jsfunctions/utilities/NewStyle.js"></script>
|
||||||
|
<script src="~/jsfunctions/utilities/exportcsvexcel.js"></script>
|
||||||
<script src="~/jsfunctions/utilities/utilsV3.js"></script>
|
<script src="~/jsfunctions/utilities/utilsV3.js"></script>
|
||||||
<script src="~/jsfunctions/utilities/StylesV3.js"></script>
|
<script src="~/jsfunctions/utilities/StylesV3.js"></script>
|
||||||
<script src="~/jsfunctions/utilities/searchengine.js"></script>
|
<script src="~/jsfunctions/utilities/searchengine.js"></script>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<link href="~/css/pr/receiving.css" rel="stylesheet" />
|
<!-- Modal viewPRItemDetails -->
|
||||||
<!-- Modal viewPRItemDetails -->
|
|
||||||
<div class="modal fade custom-modal-backdrop" id="viewPRItemDetails"
|
<div class="modal fade custom-modal-backdrop" id="viewPRItemDetails"
|
||||||
tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true">
|
tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-xl">
|
<div class="modal-dialog modal-xl">
|
||||||
|
|||||||
@ -41,18 +41,32 @@ function isFullFilled() {
|
|||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
loader = $('#overlay, #loader');
|
loader = $('#overlay, #loader');
|
||||||
let fetchedData = [];
|
|
||||||
let hasFetched = false; // ensures only one fetch per page load
|
|
||||||
let fetchInProgress = false; // prevents multiple calls before first finishes
|
|
||||||
|
|
||||||
let cartItemCount = $('#cartItemCount').val();
|
let cartItemCount = $('#cartItemCount').val();
|
||||||
$('#cartCount').text(cartItemCount);
|
$('#cartCount').text(cartItemCount);
|
||||||
|
|
||||||
itemTable = $('#ItemTable').DataTable({
|
itemTable = $('#ItemTable').DataTable({
|
||||||
serverSide: false,
|
serverSide: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
deferLoading: 0,
|
searching: false, // disable built-in search box (we drive it manually)
|
||||||
data: [],
|
|
||||||
|
ajax: {
|
||||||
|
url: '/ItemMgmt/GetItemList',
|
||||||
|
type: 'GET',
|
||||||
|
data: function (d) {
|
||||||
|
var searchVal = $('#customSearch').length ? $('#customSearch').val() : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchTerm: (searchVal || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dataSrc: function (json) {
|
||||||
|
return json.data; // ← extract data array here
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
{ data: 'itemNo' },
|
{ data: 'itemNo' },
|
||||||
{ data: 'itemName' },
|
{ data: 'itemName' },
|
||||||
@ -63,69 +77,34 @@ $(document).ready(function () {
|
|||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
return renderItembtns(data, row);
|
return renderItembtns(data, row);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
{ data: 'cartItemCount', visible: false },
|
|
||||||
{ data: 'createdDate', visible: false },
|
|
||||||
{ data: 'requestTypeId', visible: false }
|
|
||||||
],
|
],
|
||||||
|
|
||||||
order: [[0, 'desc']],
|
order: [[0, 'desc']],
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
|
||||||
language: {
|
language: {
|
||||||
emptyTable: "Type in the search box to fetch data."
|
emptyTable: "Type in the search box to fetch data."
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simple debounce helper
|
// Debounce helper
|
||||||
function debounce(func, delay) {
|
function debounce(fn, delay) {
|
||||||
let timer;
|
let t;
|
||||||
return function (...args) {
|
return function (...args) {
|
||||||
clearTimeout(timer);
|
clearTimeout(t);
|
||||||
timer = setTimeout(() => func.apply(this, args), delay);
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search handler
|
// Custom search box
|
||||||
$('#ItemTable_filter input').unbind().on('keyup', debounce(function () {
|
$('#customSearch').on('keyup', debounce(function () {
|
||||||
let searchValue = $(this).val().trim();
|
if ($(this).val().trim().length === 0) {
|
||||||
|
itemTable.clear().draw(); // show nothing until user types
|
||||||
if (searchValue.length === 0) {
|
|
||||||
itemTable.clear().draw();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
itemTable.ajax.reload(); // triggers new server call with searchTerm
|
||||||
if (!hasFetched && !fetchInProgress) {
|
}, 400));
|
||||||
// Fetch only once on first user input
|
|
||||||
fetchInProgress = true;
|
|
||||||
loader.show();
|
|
||||||
$.ajax({
|
|
||||||
url: '/ItemMgmt/GetItemList',
|
|
||||||
type: 'GET',
|
|
||||||
success: function (response) {
|
|
||||||
fetchedData = response.data || [];
|
|
||||||
itemTable.clear().rows.add(fetchedData).draw();
|
|
||||||
hasFetched = true; // mark as fetched
|
|
||||||
},
|
|
||||||
complete: function () {
|
|
||||||
loader.hide();
|
|
||||||
fetchInProgress = false;
|
|
||||||
},
|
|
||||||
error: function (xhr, error) {
|
|
||||||
console.log('Error fetching data:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (hasFetched) {
|
|
||||||
// Local filtering only
|
|
||||||
let filteredData = fetchedData.filter(function (item) {
|
|
||||||
return (
|
|
||||||
item.itemCodeId.toString().includes(searchValue) ||
|
|
||||||
item.itemName.toLowerCase().includes(searchValue.toLowerCase()) ||
|
|
||||||
item.itemDescription.toLowerCase().includes(searchValue.toLowerCase()) ||
|
|
||||||
item.itemCategoryName.toLowerCase().includes(searchValue.toLowerCase())
|
|
||||||
);
|
|
||||||
});
|
|
||||||
itemTable.clear().rows.add(filteredData).draw();
|
|
||||||
}
|
|
||||||
}, 300)); // debounce delay in ms
|
|
||||||
});
|
});
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const triggers = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
const triggers = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||||
@ -1,42 +0,0 @@
|
|||||||
$(document).ready(function () {
|
|
||||||
loader = $('#overlay, #loader');
|
|
||||||
UserRights = document.getElementById("roleRights").value;
|
|
||||||
let IsArchived = true;
|
|
||||||
prTable = $('#PRTable').DataTable({
|
|
||||||
ajax: $.extend({
|
|
||||||
url: '/PRMgmt/GetAllPR',
|
|
||||||
type: 'GET',
|
|
||||||
data: { IsArchived }
|
|
||||||
}, beforeComplete(loader)),
|
|
||||||
initComplete: initCompleteCallback,
|
|
||||||
columns: colOnPRTable,
|
|
||||||
order: [10, 'asc'],
|
|
||||||
rowCallback: rowStatusColorCallback,
|
|
||||||
responsive: true,
|
|
||||||
language: {
|
|
||||||
emptyTable: "No record available"
|
|
||||||
},
|
|
||||||
error: errorHandler
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
$(document).on('click', '.toggle-btn', function () {
|
|
||||||
var $button = $(this);
|
|
||||||
var $span = $button.prev('.item-display');
|
|
||||||
var isExpanded = $button.data('expanded');
|
|
||||||
|
|
||||||
if (!isExpanded) {
|
|
||||||
// Show the full content
|
|
||||||
var fullItems = decodeURIComponent($button.attr('data-full-items'));
|
|
||||||
$span.html(fullItems);
|
|
||||||
$button.text('...see less');
|
|
||||||
} else {
|
|
||||||
// Show the short content
|
|
||||||
var shortItems = decodeURIComponent($button.attr('data-short-items'));
|
|
||||||
$span.html(shortItems);
|
|
||||||
$button.text('...see more');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle the expanded state
|
|
||||||
$button.data('expanded', !isExpanded);
|
|
||||||
});
|
|
||||||
80
CPRNIMS.WebApps/wwwroot/JsFunctions/PR/ArchivedV3.js
Normal file
80
CPRNIMS.WebApps/wwwroot/JsFunctions/PR/ArchivedV3.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
loader = $('#overlay, #loader');
|
||||||
|
prTable = $('#PRTable').DataTable({
|
||||||
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
|
ajax: {
|
||||||
|
url: '/PRMgmt/GetPRArchived',
|
||||||
|
type: 'GET',
|
||||||
|
data: function (d) {
|
||||||
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchPRNo: ($('#srchPRNo').val() || '').trim(),
|
||||||
|
searchItemName: ($('#srchItemName').val() || '').trim(),
|
||||||
|
searchDept: ($('#srchDept').val() || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dataSrc: function (json) { return json.data; },
|
||||||
|
beforeSend: function () { loader.show(); },
|
||||||
|
complete: function () { loader.hide(); },
|
||||||
|
error: function (xhr, error) {
|
||||||
|
loader.hide();
|
||||||
|
console.error('Error loading data:', error);
|
||||||
|
if (typeof toastr !== 'undefined')
|
||||||
|
toastr.error('Failed to load data. Please try again.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: colOnPRTable,
|
||||||
|
order: [[0, 'asc']],
|
||||||
|
language: {
|
||||||
|
emptyTable: "No record available",
|
||||||
|
processing: "Loading…"
|
||||||
|
},
|
||||||
|
error: errorHandler
|
||||||
|
});
|
||||||
|
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let t;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#srchPRNo, #srchItemName, #srchDept')
|
||||||
|
.on('keyup', debounce(function () {
|
||||||
|
prTable.ajax.reload();
|
||||||
|
}, 400));
|
||||||
|
|
||||||
|
$('.pr-search-clear').on('click', function () {
|
||||||
|
const target = $(this).data('target');
|
||||||
|
$(target).val('');
|
||||||
|
prTable.ajax.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$(document).on('click', '.toggle-btn', function () {
|
||||||
|
var $button = $(this);
|
||||||
|
var $span = $button.prev('.item-display');
|
||||||
|
var isExpanded = $button.data('expanded');
|
||||||
|
|
||||||
|
if (!isExpanded) {
|
||||||
|
// Show the full content
|
||||||
|
var fullItems = decodeURIComponent($button.attr('data-full-items'));
|
||||||
|
$span.html(fullItems);
|
||||||
|
$button.text('...see less');
|
||||||
|
} else {
|
||||||
|
// Show the short content
|
||||||
|
var shortItems = decodeURIComponent($button.attr('data-short-items'));
|
||||||
|
$span.html(shortItems);
|
||||||
|
$button.text('...see more');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the expanded state
|
||||||
|
$button.data('expanded', !isExpanded);
|
||||||
|
});
|
||||||
@ -3,10 +3,14 @@
|
|||||||
GetItemColor: '/ItemMgmt/GetItemColor',
|
GetItemColor: '/ItemMgmt/GetItemColor',
|
||||||
GetItemLocalization: '/ItemMgmt/GetItemLocalization',
|
GetItemLocalization: '/ItemMgmt/GetItemLocalization',
|
||||||
GetItemUOM: '/ItemMgmt/GetItemUOM',
|
GetItemUOM: '/ItemMgmt/GetItemUOM',
|
||||||
|
GetAllPR: '/PRMgmt/GetAllPR',
|
||||||
|
GetApprovedPR: '/PRMgmt/GetApprovedPR',
|
||||||
|
GetDeletedPR: '/PRMgmt/GetDeletedPR',
|
||||||
|
|
||||||
GetProjectCodes: '/PRMgmt/GetProjectCodes',
|
GetProjectCodes: '/PRMgmt/GetProjectCodes',
|
||||||
GetSupplierAlternativeOffer: '/PRMgmt/GetSupplierAlternativeOffer',
|
GetSupplierAlternativeOffer: '/PRMgmt/GetSupplierAlternativeOffer',
|
||||||
GetSupplierAlterOfferDetails: '/PRMgmt/GetSupplierAlterOfferDetails',
|
GetSupplierAlterOfferDetails: '/PRMgmt/GetSupplierAlterOfferDetails',
|
||||||
|
GetDetailedPRTracking: '/PRMgmt/GetDetailedPRTracking',
|
||||||
|
|
||||||
PutSupplierAlterOffer: '/PRMgmt/PutSupplierAlterOffer',
|
PutSupplierAlterOffer: '/PRMgmt/PutSupplierAlterOffer',
|
||||||
PostPutProjectCode: '/PRMgmt/PostPutProjectCode',
|
PostPutProjectCode: '/PRMgmt/PostPutProjectCode',
|
||||||
@ -1,106 +0,0 @@
|
|||||||
$(document).ready(function () {
|
|
||||||
loader = $('#overlay, #loader');
|
|
||||||
const reportTitle = `PR Tracking - as of ${getFormattedDateTime()}`;
|
|
||||||
prTable = $('#PRTable').DataTable({
|
|
||||||
ajax: $.extend({
|
|
||||||
url: '/PRMgmt/GetDetailedPRTracking',
|
|
||||||
type: 'GET',
|
|
||||||
}, beforeComplete(loader)),
|
|
||||||
language: {
|
|
||||||
emptyTable: "No record available"
|
|
||||||
},
|
|
||||||
columns: colOnPRtracking,
|
|
||||||
pageLength: 5,
|
|
||||||
lengthMenu: [[5, 10, 25, 50, 100, -1], [5, 10, 25, 50, 100, "All"]],
|
|
||||||
initComplete: function () {
|
|
||||||
initializeColumnSearch({
|
|
||||||
tableId: '#PRTable',
|
|
||||||
dataTable: prTable,
|
|
||||||
searchableColumns: [
|
|
||||||
{
|
|
||||||
columnIndex: 0,
|
|
||||||
columnName: 'Status',
|
|
||||||
placeholder: 'Select Status...',
|
|
||||||
searchType: 'select',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 1,
|
|
||||||
columnName: 'PR No',
|
|
||||||
placeholder: 'Enter PR Number...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 3,
|
|
||||||
columnName: 'Item No',
|
|
||||||
placeholder: 'Enter Item Number...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 6,
|
|
||||||
columnName: 'Item Name',
|
|
||||||
placeholder: 'Enter Item Name...',
|
|
||||||
searchType: 'text',
|
|
||||||
searchMode: 'contains',
|
|
||||||
width: '200px'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columnIndex: 13,
|
|
||||||
columnName: 'Department',
|
|
||||||
placeholder: 'Select Department...',
|
|
||||||
searchType: 'select',
|
|
||||||
searchMode: 'exact',
|
|
||||||
width: '150px'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const uniqueSearchClass = 'column-search-input-PRTable';
|
|
||||||
populateSelectOptions(prTable, 0, '.' + uniqueSearchClass + '[data-column="0"]');
|
|
||||||
populateSelectOptions(prTable, 13, '.' + uniqueSearchClass + '[data-column="13"]');
|
|
||||||
restoreSearchFromURL(uniqueSearchClass, '#PRTable');
|
|
||||||
},
|
|
||||||
dom: 'lBfrtip',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
extend: 'csv',
|
|
||||||
title: reportTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extend: 'excel',
|
|
||||||
title: reportTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extend: 'pdf',
|
|
||||||
title: reportTitle
|
|
||||||
}
|
|
||||||
],
|
|
||||||
rowCallback: rowStatusColorCallback,
|
|
||||||
error: errorHandler,
|
|
||||||
columnDefs: [
|
|
||||||
{
|
|
||||||
targets: '_all',
|
|
||||||
render: function (data, type, row) {
|
|
||||||
if (type === 'display' && data && data.length > 50) {
|
|
||||||
return '<div style="white-space: normal; word-wrap: break-word;">' + data + '</div>';
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
function getFormattedDateTime() {
|
|
||||||
const now = new Date();
|
|
||||||
const yyyy = now.getFullYear();
|
|
||||||
const mm = String(now.getMonth() + 1).padStart(2, '0');
|
|
||||||
const dd = String(now.getDate()).padStart(2, '0');
|
|
||||||
const hh = String(now.getHours()).padStart(2, '0');
|
|
||||||
const min = String(now.getMinutes()).padStart(2, '0');
|
|
||||||
const ss = String(now.getSeconds()).padStart(2, '0');
|
|
||||||
return `${yyyy}-${mm}-${dd}_${hh}${min}${ss}`;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
const reportTitle = `PR Tracking - as of ${getFormattedDateTime()}`;
|
||||||
|
|
||||||
|
loader = $('#overlay, #loader');
|
||||||
|
var prTable = $('#PRTable').DataTable({
|
||||||
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
|
ajax: {
|
||||||
|
url: endpoint.GetDetailedPRTracking,
|
||||||
|
type: 'GET',
|
||||||
|
data: function (d) {
|
||||||
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchPRNo: ($('#srchPRNo').val() || '').trim(),
|
||||||
|
searchItemName: ($('#srchItem').val() || '').trim(),
|
||||||
|
searchDept: ($('#srchDept').val() || '').trim(),
|
||||||
|
searchStatusName: ($('#srchStatus').val() || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dataSrc: function (json) {
|
||||||
|
const $sel = $('#srchStatus');
|
||||||
|
if ($sel.find('option').length <= 1 && json.statusList?.length) {
|
||||||
|
json.statusList.forEach(function (s) {
|
||||||
|
$sel.append(`<option value="${s}">${s}</option>`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return json.data;
|
||||||
|
},
|
||||||
|
beforeSend: function () { loader.show(); },
|
||||||
|
complete: function () { loader.hide(); },
|
||||||
|
error: function (xhr, error) {
|
||||||
|
loader.hide();
|
||||||
|
if (typeof toastr !== 'undefined')
|
||||||
|
toastr.error('Failed to load data. Please try again.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dom: 'lBfrtip',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: '<i class="fas fa-file-csv"></i> CSV',
|
||||||
|
className: 'btn btn-sm btn-secondary',
|
||||||
|
action: function () { exportAllData('csv', endpoint.GetDetailedPRTracking, loader, reportTitle, colOnPRtracking); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="fas fa-file-excel"></i> Excel',
|
||||||
|
className: 'btn btn-sm btn-success',
|
||||||
|
action: function () { exportAllData('excel', endpoint.GetDetailedPRTracking, loader, reportTitle, colOnPRtracking); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="fas fa-file-pdf"></i> PDF',
|
||||||
|
className: 'btn btn-sm btn-danger',
|
||||||
|
action: function () { exportAllData('pdf', endpoint.GetDetailedPRTracking, loader, reportTitle, colOnPRtracking); }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: colOnPRtracking,
|
||||||
|
rowCallback: rowStatusColorCallback,
|
||||||
|
language: { emptyTable: "No approved records available" }
|
||||||
|
});
|
||||||
|
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let t;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#srchPRNo, #srchItem, #srchDept')
|
||||||
|
.on('keyup', debounce(() => prTable.ajax.reload(), 400));
|
||||||
|
|
||||||
|
$('#srchStatus').on('change', function () {
|
||||||
|
prTable.ajax.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.aprv-search-clear').on('click', function () {
|
||||||
|
$($(this).data('target')).val('').trigger('change');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getFormattedDateTime() {
|
||||||
|
const now = new Date();
|
||||||
|
const yyyy = now.getFullYear();
|
||||||
|
const mm = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(now.getDate()).padStart(2, '0');
|
||||||
|
const hh = String(now.getHours()).padStart(2, '0');
|
||||||
|
const min = String(now.getMinutes()).padStart(2, '0');
|
||||||
|
const ss = String(now.getSeconds()).padStart(2, '0');
|
||||||
|
return `${yyyy}-${mm}-${dd}_${hh}${min}${ss}`;
|
||||||
|
}
|
||||||
@ -45,8 +45,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: Add helper function to refresh current tab
|
|
||||||
function refreshCurrentTab() {
|
function refreshCurrentTab() {
|
||||||
if (typeof PRTabs !== 'undefined') {
|
if (typeof PRTabs !== 'undefined') {
|
||||||
PRTabs.reload();
|
PRTabs.reload();
|
||||||
@ -55,7 +53,6 @@ function refreshCurrentTab() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: Add helper to switch tabs programmatically
|
|
||||||
function switchToPRTab(tabName) {
|
function switchToPRTab(tabName) {
|
||||||
if (typeof PRTabs !== 'undefined') {
|
if (typeof PRTabs !== 'undefined') {
|
||||||
PRTabs.switchToTab(tabName);
|
PRTabs.switchToTab(tabName);
|
||||||
@ -117,22 +114,20 @@ const btnUploadContainer = document.getElementById('btnUploadContainer');
|
|||||||
const btnUploadText = document.getElementById('btnUploadText');
|
const btnUploadText = document.getElementById('btnUploadText');
|
||||||
|
|
||||||
let selectedFile = null;
|
let selectedFile = null;
|
||||||
let existingAttachment = false; // Set this to true if there's already an attachment
|
let existingAttachment = false;
|
||||||
|
|
||||||
// File input change event
|
|
||||||
fileInput.addEventListener('change', function (e) {
|
fileInput.addEventListener('change', function (e) {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
// Validate file size (5MB max)
|
// Validate file size (5MB max)
|
||||||
const maxSize = 5 * 1024 * 1024; // 5MB in bytes
|
const maxSize = 5 * 1024 * 1024;
|
||||||
if (file.size > maxSize) {
|
if (file.size > maxSize) {
|
||||||
showToast('warning', 'File size exceeds 5MB. Please choose a smaller file.', 'File Upload', 4000);
|
showToast('warning', 'File size exceeds 5MB. Please choose a smaller file.', 'File Upload', 4000);
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate file type
|
|
||||||
const allowedExtensions = ['csv', 'xlsx', 'xls', 'pdf'];
|
const allowedExtensions = ['csv', 'xlsx', 'xls', 'pdf'];
|
||||||
const fileExtension = file.name.split('.').pop().toLowerCase();
|
const fileExtension = file.name.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
@ -142,23 +137,19 @@ fileInput.addEventListener('change', function (e) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the selected file
|
|
||||||
selectedFile = file;
|
selectedFile = file;
|
||||||
|
|
||||||
// Show file info and upload button
|
|
||||||
showFileInfo(file);
|
showFileInfo(file);
|
||||||
showUploadButton();
|
showUploadButton();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show file information
|
|
||||||
function showFileInfo(file) {
|
function showFileInfo(file) {
|
||||||
fileNameDisplay.textContent = truncateFileName(file.name, 30);
|
fileNameDisplay.textContent = truncateFileName(file.name, 30);
|
||||||
fileSizeDisplay.textContent = formatFileSize(file.size);
|
fileSizeDisplay.textContent = formatFileSize(file.size);
|
||||||
fileInfoBadge.classList.remove('d-none');
|
fileInfoBadge.classList.remove('d-none');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show upload button
|
|
||||||
function showUploadButton() {
|
function showUploadButton() {
|
||||||
// Change button text based on whether there's an existing attachment
|
// Change button text based on whether there's an existing attachment
|
||||||
if (existingAttachment) {
|
if (existingAttachment) {
|
||||||
@ -170,7 +161,6 @@ function showUploadButton() {
|
|||||||
btnUploadContainer.classList.remove('d-none');
|
btnUploadContainer.classList.remove('d-none');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear file attachment
|
|
||||||
function clearFileAttachment() {
|
function clearFileAttachment() {
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
selectedFile = null;
|
selectedFile = null;
|
||||||
@ -178,7 +168,6 @@ function clearFileAttachment() {
|
|||||||
btnUploadContainer.classList.add('d-none');
|
btnUploadContainer.classList.add('d-none');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload attachment function
|
|
||||||
function uploadAttachment() {
|
function uploadAttachment() {
|
||||||
if (!selectedFile) {
|
if (!selectedFile) {
|
||||||
showToast('warning', 'Please select a file first.', 'File Upload', 3000);
|
showToast('warning', 'Please select a file first.', 'File Upload', 3000);
|
||||||
@ -205,7 +194,7 @@ function uploadAttachment() {
|
|||||||
existingAttachment = true;
|
existingAttachment = true;
|
||||||
|
|
||||||
$('#fileName').val(data.newFileName)
|
$('#fileName').val(data.newFileName)
|
||||||
// Update the download button if needed
|
|
||||||
document.getElementById('btnDownloadAttachment').classList.remove('d-none');
|
document.getElementById('btnDownloadAttachment').classList.remove('d-none');
|
||||||
|
|
||||||
clearFileAttachment();
|
clearFileAttachment();
|
||||||
@ -217,13 +206,11 @@ function uploadAttachment() {
|
|||||||
showToast('error', 'Upload error:', error, 'File Upload', 4000);
|
showToast('error', 'Upload error:', error, 'File Upload', 4000);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// Reset button state
|
|
||||||
document.querySelector('#btnUploadContainer button').disabled = false;
|
document.querySelector('#btnUploadContainer button').disabled = false;
|
||||||
showUploadButton(); // Restore button text
|
showUploadButton();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate long file names
|
|
||||||
function truncateFileName(fileName, maxLength) {
|
function truncateFileName(fileName, maxLength) {
|
||||||
if (fileName.length <= maxLength) return fileName;
|
if (fileName.length <= maxLength) return fileName;
|
||||||
|
|
||||||
@ -234,7 +221,6 @@ function truncateFileName(fileName, maxLength) {
|
|||||||
return truncatedName + '.' + extension;
|
return truncatedName + '.' + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format file size
|
|
||||||
function formatFileSize(bytes) {
|
function formatFileSize(bytes) {
|
||||||
if (bytes === 0) return '0 Bytes';
|
if (bytes === 0) return '0 Bytes';
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
@ -243,10 +229,7 @@ function formatFileSize(bytes) {
|
|||||||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize: Check if there's an existing attachment on page load
|
|
||||||
function checkExistingAttachment() {
|
function checkExistingAttachment() {
|
||||||
// Replace with your logic to check if attachment exists
|
|
||||||
// For example, check if there's a filename in your hidden input
|
|
||||||
const existingFileName = document.getElementById('fileName').value;
|
const existingFileName = document.getElementById('fileName').value;
|
||||||
|
|
||||||
if (existingFileName) {
|
if (existingFileName) {
|
||||||
@ -14,25 +14,25 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
var colOnPRtracking = [
|
var colOnPRtracking = [
|
||||||
{ data: 'statusName' },
|
{ data: 'statusName', title: 'Status' },
|
||||||
{ data: 'prNo' },
|
{ data: 'prNo', title: 'PRNo' },
|
||||||
{ data: 'newPRNo' },
|
{ data: 'newPRNo', title: 'NewPRNo' },
|
||||||
{ data: 'itemNo' },
|
{ data: 'itemNo', title: 'ItemNo' },
|
||||||
{ data: 'uomName' },
|
{ data: 'uomName', title: 'UOM' },
|
||||||
{
|
{
|
||||||
data: 'qty',
|
data: 'qty', title: 'Qty' ,
|
||||||
render: function (data, type, row, meta) {
|
render: function (data, type, row, meta) {
|
||||||
return numberWithCommas(data);
|
return numberWithCommas(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ data: 'itemName' },
|
{ data: 'itemName', title: 'ItemName' },
|
||||||
{ data: 'itemDescription' },
|
{ data: 'itemDescription', title: 'Description' },
|
||||||
{ data: 'specification' },
|
{ data: 'specification', title: 'Specification' },
|
||||||
{ data: 'itemCategoryName' },
|
{ data: 'itemCategoryName', title: 'Category' },
|
||||||
{ data: 'prTypeName' },
|
{ data: 'prTypeName', title: 'PRType' },
|
||||||
{ data: 'prBy' },
|
{ data: 'prBy', title: 'PRBy' },
|
||||||
{
|
{
|
||||||
data: 'prDate',
|
data: 'prDate', title: 'PRDate',
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
if (type === 'display' && data) {
|
if (type === 'display' && data) {
|
||||||
return formatDate(data);
|
return formatDate(data);
|
||||||
@ -40,39 +40,39 @@ var colOnPRtracking = [
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ data: 'department' },
|
{ data: 'department', title: 'Department' },
|
||||||
{ data: 'remarks' },
|
{ data: 'remarks', title: 'Remarks' },
|
||||||
{ data: 'attestedBy' },
|
{ data: 'attestedBy', title: 'AttestedBy' },
|
||||||
{ data: 'approvedBy' },
|
{ data: 'approvedBy', title: 'ApprovedBy' },
|
||||||
{ data: 'canvassNo' },
|
{ data: 'canvassNo', title: 'CanvassNo' },
|
||||||
{ data: 'canvassBy' },
|
{ data: 'canvassBy', title: 'CanvassBy' },
|
||||||
{ data: 'canvassDate' },
|
{ data: 'canvassDate', title: 'CanvassDate' },
|
||||||
{ data: 'poNo' },
|
{ data: 'poNo', title: 'PONo' },
|
||||||
{ data: 'poBy' },
|
{ data: 'poBy', title: 'POBy' },
|
||||||
{ data: 'poDate' },
|
{ data: 'poDate', title: 'PODate' },
|
||||||
{ data: 'rrNo' },
|
{ data: 'rrNo', title: 'RRNo' },
|
||||||
{ data: 'receivedBy' },
|
{ data: 'receivedBy', title: 'ReceivedBy' },
|
||||||
{ data: 'rrDate' },
|
{ data: 'rrDate', title: 'RRDate' },
|
||||||
{
|
{
|
||||||
data: 'quantityReceived',
|
data: 'quantityReceived', title: 'QtyReceived',
|
||||||
render: function (data, type, row, meta) {
|
render: function (data, type, row, meta) {
|
||||||
return numberWithCommas(data);
|
return numberWithCommas(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ data: 'acknowledgeBy' },
|
{ data: 'acknowledgeBy', title: 'AcknowledgeBy' },
|
||||||
{ data: 'acknowledgeDate' },
|
{ data: 'acknowledgeDate', title: 'AcknowledgeDate' },
|
||||||
];
|
];
|
||||||
var colOnPRTable = [
|
var colOnPRTable = [
|
||||||
{ data: 'prNo' },
|
{ data: 'prNo', title: 'PRNo' },
|
||||||
{ data: 'newPRNo' },
|
{ data: 'newPRNo', title: 'NewPRNo' },
|
||||||
{
|
{
|
||||||
data: 'aggreItemName',
|
data: 'aggreItemName', title: 'ItemName',
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
return renderExpandableItems(data, 3);
|
return renderExpandableItems(data, 3);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: 'createdDate',
|
data: 'createdDate', title: 'PRDate',
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
if (type === 'display' && data) {
|
if (type === 'display' && data) {
|
||||||
return formatDate(data);
|
return formatDate(data);
|
||||||
@ -80,9 +80,9 @@ var colOnPRTable = [
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ data: 'createdBy' },
|
{ data: 'createdBy', title: 'PRBy', },
|
||||||
{
|
{
|
||||||
data: 'dateNeeded',
|
data: 'dateNeeded', title: 'DateNeeded',
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
if (type === 'display' && data) {
|
if (type === 'display' && data) {
|
||||||
return formatDate(data);
|
return formatDate(data);
|
||||||
@ -90,7 +90,7 @@ var colOnPRTable = [
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ data: 'department' },
|
{ data: 'department', title: 'Department', },
|
||||||
{
|
{
|
||||||
data: null,
|
data: null,
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
@ -98,6 +98,71 @@ var colOnPRTable = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
var colOnApprovedPR = [
|
||||||
|
{ data: 'statusName' },
|
||||||
|
{ data: 'remainingDays', searchable: false },
|
||||||
|
{ data: 'prNo' },
|
||||||
|
{ data: 'itemNo' },
|
||||||
|
{ data: 'itemName' },
|
||||||
|
{
|
||||||
|
data: 'qty', searchable: false,
|
||||||
|
render: data => numberWithCommas(data)
|
||||||
|
},
|
||||||
|
{ data: 'createdBy' },
|
||||||
|
{
|
||||||
|
data: 'createdDate', searchable: false,
|
||||||
|
render: data => formatDateTime(data)
|
||||||
|
},
|
||||||
|
{ data: 'attestedBy' },
|
||||||
|
{
|
||||||
|
data: 'attestedDate', searchable: false,
|
||||||
|
render: data => formatStrDateTime(data)
|
||||||
|
},
|
||||||
|
{ data: 'approvedBy' },
|
||||||
|
{
|
||||||
|
data: 'approvedDate', searchable: false,
|
||||||
|
render: data => formatStrDateTime(data)
|
||||||
|
},
|
||||||
|
{ data: 'department' }
|
||||||
|
];
|
||||||
|
var colOnDeletedPR = [
|
||||||
|
{ data: 'prNo' },
|
||||||
|
{ data: 'itemNo' },
|
||||||
|
{ data: 'itemName' },
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
data: 'qty',
|
||||||
|
render: function (data) {
|
||||||
|
return numberWithCommas(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ data: 'createdBy' },
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
data: 'createdDate',
|
||||||
|
render: function (data) {
|
||||||
|
return formatDateTime(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ data: 'attestedBy' },
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
data: 'attestedDate',
|
||||||
|
render: function (data) {
|
||||||
|
return formatStrDateTime(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ data: 'approvedBy' },
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
data: 'approvedDate',
|
||||||
|
render: function (data) {
|
||||||
|
return formatStrDateTime(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ data: 'department' },
|
||||||
|
{ data: 'remarks' }
|
||||||
|
];
|
||||||
var colOnAlterOfferTable = [
|
var colOnAlterOfferTable = [
|
||||||
{ data: 'prNo' },
|
{ data: 'prNo' },
|
||||||
{ data: 'itemNo' },
|
{ data: 'itemNo' },
|
||||||
@ -1,7 +1,6 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Tab configuration with table IDs for view components
|
|
||||||
const tabConfig = {
|
const tabConfig = {
|
||||||
'pr-list': {
|
'pr-list': {
|
||||||
title: 'All Purchase Requests',
|
title: 'All Purchase Requests',
|
||||||
@ -29,7 +28,6 @@
|
|||||||
tab.addEventListener('click', handleTabClick);
|
tab.addEventListener('click', handleTabClick);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load initial tab content
|
|
||||||
loadTabContent('pr-list');
|
loadTabContent('pr-list');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,16 +50,13 @@
|
|||||||
|
|
||||||
isSwitching = true;
|
isSwitching = true;
|
||||||
|
|
||||||
// Update active states
|
|
||||||
document.querySelectorAll('.pr-tab-btn').forEach(b => {
|
document.querySelectorAll('.pr-tab-btn').forEach(b => {
|
||||||
b.classList.remove('active');
|
b.classList.remove('active');
|
||||||
});
|
});
|
||||||
btn.classList.add('active');
|
btn.classList.add('active');
|
||||||
|
|
||||||
// Update title
|
|
||||||
updateTitle(tabConfig[tabId].title);
|
updateTitle(tabConfig[tabId].title);
|
||||||
|
|
||||||
// Load view component
|
|
||||||
loadTabContent(tabId, btn);
|
loadTabContent(tabId, btn);
|
||||||
|
|
||||||
currentTab = tabId;
|
currentTab = tabId;
|
||||||
@ -71,16 +66,13 @@
|
|||||||
const titleEl = document.getElementById('pageTitle');
|
const titleEl = document.getElementById('pageTitle');
|
||||||
if (!titleEl) return;
|
if (!titleEl) return;
|
||||||
|
|
||||||
// Fade out with slide
|
|
||||||
titleEl.classList.add('updating');
|
titleEl.classList.add('updating');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
titleEl.textContent = title;
|
titleEl.textContent = title;
|
||||||
|
|
||||||
// Trigger reflow
|
|
||||||
void titleEl.offsetWidth;
|
void titleEl.offsetWidth;
|
||||||
|
|
||||||
// Fade in
|
|
||||||
titleEl.classList.remove('updating');
|
titleEl.classList.remove('updating');
|
||||||
}, 250);
|
}, 250);
|
||||||
}
|
}
|
||||||
@ -93,15 +85,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show loading state
|
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.classList.add('loading');
|
btn.classList.add('loading');
|
||||||
}
|
}
|
||||||
showContainerLoading(true);
|
showContainerLoading(true);
|
||||||
|
|
||||||
// Call your existing function
|
|
||||||
prTabbedComponent(config.tableId, config.endpoint, function (success) {
|
prTabbedComponent(config.tableId, config.endpoint, function (success) {
|
||||||
// Hide loading state
|
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.classList.remove('loading');
|
btn.classList.remove('loading');
|
||||||
}
|
}
|
||||||
@ -127,14 +116,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize on DOM ready
|
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
} else {
|
} else {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API
|
|
||||||
window.PRTabs = {
|
window.PRTabs = {
|
||||||
switchTo: function (tabId) {
|
switchTo: function (tabId) {
|
||||||
const btn = document.querySelector(`.pr-tab-btn[data-tab="${tabId}"]`);
|
const btn = document.querySelector(`.pr-tab-btn[data-tab="${tabId}"]`);
|
||||||
@ -149,19 +136,17 @@
|
|||||||
return currentTab;
|
return currentTab;
|
||||||
},
|
},
|
||||||
addTab: function (tabId, config) {
|
addTab: function (tabId, config) {
|
||||||
// Dynamically add new tab configuration
|
|
||||||
tabConfig[tabId] = config;
|
tabConfig[tabId] = config;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Updated prTabbedComponent to work with callback
|
|
||||||
function prTabbedComponent(id, endpoint, callback) {
|
function prTabbedComponent(id, endpoint, callback) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
data: { TableId: id }, // Note: capital T to match your controller parameter
|
data: { TableId: id },
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
$('#TabbedContainer').html(response);
|
$('#TabbedContainer').html(response);
|
||||||
|
|
||||||
|
|||||||
@ -2,84 +2,129 @@
|
|||||||
var loader = $('#overlay, #loader').css('z-index', 1100);
|
var loader = $('#overlay, #loader').css('z-index', 1100);
|
||||||
$('#viewItemList').modal('show');
|
$('#viewItemList').modal('show');
|
||||||
$('#viewItemList').css('z-index', 1090);
|
$('#viewItemList').css('z-index', 1090);
|
||||||
|
|
||||||
tableElement = $('#ItemTable');
|
tableElement = $('#ItemTable');
|
||||||
tableName = '#ItemTable';
|
tableName = '#ItemTable';
|
||||||
|
|
||||||
tableDestroy(tableElement);
|
tableDestroy(tableElement);
|
||||||
totalSelectedLabel = $('#totalSelectedItem');
|
totalSelectedLabel = $('#totalSelectedItem');
|
||||||
|
|
||||||
clearTableSelection(tableName, selectedProductsMap, () => {
|
clearTableSelection(tableName, selectedProductsMap, () => {
|
||||||
totalSelectedLabel.text(0);
|
totalSelectedLabel.text(0);
|
||||||
}, 'selected-row', '.select-all-item-checkbox');
|
}, 'selected-row', '.select-all-item-checkbox');
|
||||||
|
|
||||||
var itemListTable = tableElement.DataTable({
|
// Reset search input on modal open
|
||||||
ajax: $.extend({
|
$('#modalSearchInput').val('');
|
||||||
|
$('#modalSearchClear').css('display', 'none');
|
||||||
|
$('#modalSearchHint').text('Start typing to search items…');
|
||||||
|
|
||||||
|
var itemListTable = tableElement.DataTable({
|
||||||
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
|
ajax: {
|
||||||
url: '/ItemMgmt/GetItemList',
|
url: '/ItemMgmt/GetItemList',
|
||||||
type: 'POST',
|
type: 'GET',
|
||||||
}, beforeComplete(loader)),
|
data: function (d) {
|
||||||
|
var searchVal = $('#modalSearchInput').length
|
||||||
|
? $('#modalSearchInput').val()
|
||||||
|
: '';
|
||||||
|
return {
|
||||||
|
draw: d.draw,
|
||||||
|
searchTerm: (searchVal || '').trim(),
|
||||||
|
pageNumber: Math.floor(d.start / d.length) + 1,
|
||||||
|
pageSize: d.length
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dataSrc: function (json) {
|
||||||
|
return json.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
responsive: true,
|
responsive: true,
|
||||||
language: {
|
language: {
|
||||||
emptyTable: "No record available"
|
emptyTable: "No record available",
|
||||||
|
processing: "Searching…"
|
||||||
},
|
},
|
||||||
initComplete: function () {
|
initComplete: function () {
|
||||||
initializeTableSelection({
|
initializeTableSelection({
|
||||||
tableName: tableName,
|
tableName: tableName,
|
||||||
dataTable: itemListTable,
|
dataTable: itemListTable,
|
||||||
selectedItemsMap: selectedProductsMap,
|
selectedItemsMap: selectedProductsMap,
|
||||||
idKey: 'itemNo',
|
idKey: 'itemNo',
|
||||||
idKey2: 'itemCodeId',
|
idKey2: 'itemCodeId',
|
||||||
checkboxClass: '.select-item-checkbox',
|
checkboxClass: '.select-item-checkbox',
|
||||||
selectAllClass: '.select-all-item-checkbox',
|
selectAllClass: '.select-all-item-checkbox',
|
||||||
selectedRowClass: 'selected-row',
|
selectedRowClass: 'selected-row',
|
||||||
updateCountCallback: function () {
|
updateCountCallback: function () {
|
||||||
totalSelectedLabel.text(getSelectedCount(selectedProductsMap));
|
totalSelectedLabel.text(getSelectedCount(selectedProductsMap));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
data: 'itemNo',
|
data: 'itemNo',
|
||||||
title: '<input type="checkbox" class="select-all-item-checkbox" />',
|
title: '<input type="checkbox" class="select-all-item-checkbox" />',
|
||||||
render: function () {
|
render: function () {
|
||||||
return '<input type="checkbox" class="select-item-checkbox" style="text-align:center;"/>';
|
return '<input type="checkbox" class="select-item-checkbox" style="text-align:center;"/>';
|
||||||
},
|
},
|
||||||
orderable: false,
|
orderable: false,
|
||||||
searchable: false
|
searchable: false
|
||||||
},
|
},
|
||||||
{ data: 'itemNo' },
|
{ data: 'itemNo' },
|
||||||
{ data: 'itemName' },
|
{ data: 'itemName' },
|
||||||
{ data: 'itemDescription' },
|
{ data: 'itemDescription' },
|
||||||
{ data: 'itemCategoryName' },
|
{ data: 'itemCategoryName' },
|
||||||
{ data: 'qty' },
|
{ data: 'qty' }
|
||||||
],
|
],
|
||||||
"columnDefs": [
|
columnDefs: [
|
||||||
{
|
{
|
||||||
"targets": [5],
|
targets: [5],
|
||||||
"render": function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
return '<input type="number" class="editable-qty" style="width:60px;" value="' + data + '" />';
|
return `<input type="number" class="editable-qty"
|
||||||
}
|
style="width:60px;" value="${data}" />`;
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
pageLength: 5,
|
],
|
||||||
lengthMenu: [[5, 10, 25, 50, 100, -1], [5, 10, 25, 50, 100, "All"]],
|
pageLength: 5,
|
||||||
|
lengthMenu: [[5, 10, 25, 50, 100], [5, 10, 25, 50, 100]],
|
||||||
error: errorHandler
|
error: errorHandler
|
||||||
});
|
});
|
||||||
// Add this inside viewItemList(), after the DataTable is initialized
|
|
||||||
|
// Debounce helper (local scope)
|
||||||
|
function debounce(fn, delay) {
|
||||||
|
let t;
|
||||||
|
return function (...args) {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn.apply(this, args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search input handler
|
||||||
|
$('#modalSearchInput').off('keyup').on('keyup', debounce(function () {
|
||||||
|
const val = $(this).val().trim();
|
||||||
|
$('#modalSearchHint').text(val.length ? `Searching for "${val}"…` : 'Start typing to search items…');
|
||||||
|
itemListTable.ajax.reload();
|
||||||
|
}, 400));
|
||||||
|
|
||||||
|
// Show/hide clear button
|
||||||
|
$('#modalSearchInput').off('input').on('input', function () {
|
||||||
|
$('#modalSearchClear').css('display', $(this).val().length ? 'flex' : 'none');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear button handler
|
||||||
|
$('#modalSearchClear').off('click').on('click', function () {
|
||||||
|
$('#modalSearchInput').val('').trigger('input');
|
||||||
|
$('#modalSearchHint').text('Start typing to search items…');
|
||||||
|
itemListTable.ajax.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Editable qty handler
|
||||||
$('#ItemTable').on('change', '.editable-qty', function () {
|
$('#ItemTable').on('change', '.editable-qty', function () {
|
||||||
const $row = $(this).closest('tr');
|
const $row = $(this).closest('tr');
|
||||||
const rowData = itemListTable.row($row).data();
|
const rowData = itemListTable.row($row).data();
|
||||||
if (!rowData) return;
|
if (!rowData) return;
|
||||||
|
|
||||||
const newQty = parseFloat($(this).val()) || 0;
|
const newQty = parseFloat($(this).val()) || 0;
|
||||||
const itemId = `${rowData.itemNo}|${rowData.qty}`; // original key
|
const itemId = `${rowData.itemNo}|${rowData.qty}`;
|
||||||
|
|
||||||
// ✅ If this item is already selected, update its qty in the map
|
|
||||||
if (selectedProductsMap[itemId]) {
|
if (selectedProductsMap[itemId]) {
|
||||||
selectedProductsMap[itemId].qty = newQty;
|
selectedProductsMap[itemId].qty = newQty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the DataTable's internal data too
|
|
||||||
rowData.qty = newQty;
|
rowData.qty = newQty;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -155,7 +200,6 @@ function getApproverName(prDetailsId, prNo) {
|
|||||||
const attestedBy = document.getElementById('label-pr-attestedBy');
|
const attestedBy = document.getElementById('label-pr-attestedBy');
|
||||||
const approveBy = document.getElementById('label-pr-approvedBy');
|
const approveBy = document.getElementById('label-pr-approvedBy');
|
||||||
|
|
||||||
// Security: Use textContent to prevent XSS
|
|
||||||
if (attestedBy) attestedBy.textContent = item.attestedBy || '';
|
if (attestedBy) attestedBy.textContent = item.attestedBy || '';
|
||||||
if (approveBy) approveBy.textContent = item.approvedBy || '';
|
if (approveBy) approveBy.textContent = item.approvedBy || '';
|
||||||
}
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
function exportAllData(format, api, loader, reportTitle, column) {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
searchPRNo: ($('#srchPRNo').val() || '').trim(),
|
||||||
|
searchItemName: ($('#srchItem').val() || '').trim(),
|
||||||
|
searchDept: ($('#srchDept').val() || '').trim(),
|
||||||
|
searchStatusName: ($('#srchStatus').val() || '').trim(),
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 999999
|
||||||
|
});
|
||||||
|
|
||||||
|
loader.show();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: `${api}?${params}`,
|
||||||
|
type: 'GET',
|
||||||
|
success: function (json) {
|
||||||
|
loader.hide();
|
||||||
|
if (!json.data || json.data.length === 0) {
|
||||||
|
toastr.warning('No data to export.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
triggerClientExport(json.data, format, reportTitle, column);
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
loader.hide();
|
||||||
|
toastr.error('Failed to fetch data for export.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function triggerClientExport(data, format, reportTitle, column) {
|
||||||
|
const tempTableId = 'tempExportTable';
|
||||||
|
|
||||||
|
// Filter out action/button columns (data: null) and strip render functions
|
||||||
|
const exportColumns = column
|
||||||
|
.filter(c => c.data !== null && c.data !== undefined)
|
||||||
|
.map(c => ({
|
||||||
|
data: c.data,
|
||||||
|
title: c.title || c.data,
|
||||||
|
// Keep render only for non-display logic (dates etc),
|
||||||
|
// but strip HTML from output
|
||||||
|
render: c.render
|
||||||
|
? function (data, type, row) {
|
||||||
|
if (type === 'export' || type === 'display') {
|
||||||
|
const result = c.render(data, 'display', row);
|
||||||
|
// Strip HTML tags for export
|
||||||
|
return $('<div>').html(result).text();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}));
|
||||||
|
|
||||||
|
$('body').append(`
|
||||||
|
<div id="tempExportWrapper" style="position:fixed;left:-9999px;top:-9999px;">
|
||||||
|
<table id="${tempTableId}"></table>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
const tempTable = $(`#${tempTableId}`).DataTable({
|
||||||
|
data: data,
|
||||||
|
columns: exportColumns,
|
||||||
|
dom: 'Brt',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
extend: format,
|
||||||
|
title: reportTitle,
|
||||||
|
exportOptions: {
|
||||||
|
columns: ':visible',
|
||||||
|
format: {
|
||||||
|
body: function (data) {
|
||||||
|
// Strip any remaining HTML tags
|
||||||
|
return $('<div>').html(data).text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
paging: false,
|
||||||
|
searching: false,
|
||||||
|
info: false
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
tempTable.button(0).trigger();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
tempTable.destroy();
|
||||||
|
$('#tempExportWrapper').remove();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
@ -1,232 +0,0 @@
|
|||||||
// ===========================================
|
|
||||||
// REUSABLE DATATABLE COLUMN SEARCH UTILITY
|
|
||||||
// ===========================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize column-specific search functionality for DataTable
|
|
||||||
* @param {Object} config - Configuration object
|
|
||||||
* @param {string} config.tableId - DataTable selector (e.g., '#PRItemTable')
|
|
||||||
* @param {Object} config.dataTable - DataTable instance
|
|
||||||
* @param {Array} config.searchableColumns - Array of column configurations
|
|
||||||
* @param {string} config.containerId - Container ID for search inputs (optional)
|
|
||||||
* @param {string} config.searchInputClass - CSS class for search inputs (optional)
|
|
||||||
*/
|
|
||||||
function initializeColumnSearch(config) {
|
|
||||||
const {
|
|
||||||
tableId,
|
|
||||||
dataTable,
|
|
||||||
searchableColumns,
|
|
||||||
containerId = null,
|
|
||||||
searchInputClass = 'column-search-input'
|
|
||||||
} = config;
|
|
||||||
|
|
||||||
// Create search container if not provided
|
|
||||||
let searchContainer;
|
|
||||||
if (containerId) {
|
|
||||||
searchContainer = $('#' + containerId);
|
|
||||||
} else {
|
|
||||||
// Create default search container above the table
|
|
||||||
searchContainer = $('<div class="column-search-container mb-3"></div>');
|
|
||||||
$(tableId).parent().prepend(searchContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear existing search inputs
|
|
||||||
searchContainer.empty();
|
|
||||||
|
|
||||||
// Create search inputs for each specified column
|
|
||||||
searchableColumns.forEach(function (columnConfig) {
|
|
||||||
const columnIndex = columnConfig.columnIndex;
|
|
||||||
const columnName = columnConfig.columnName;
|
|
||||||
const placeholder = columnConfig.placeholder;
|
|
||||||
const searchType = columnConfig.searchType || 'text';
|
|
||||||
const searchMode = columnConfig.searchMode || 'contains';
|
|
||||||
const selectOptions = columnConfig.selectOptions || null;
|
|
||||||
const width = columnConfig.width || 'auto';
|
|
||||||
|
|
||||||
// Create input wrapper
|
|
||||||
const inputWrapper = $('<div class="d-inline-block me-2 mb-2" style="width: ' + width + ';"><label class="form-label small">' + columnName + ':</label></div>');
|
|
||||||
|
|
||||||
let searchInput;
|
|
||||||
|
|
||||||
// Create different input types based on searchType
|
|
||||||
switch (searchType) {
|
|
||||||
case 'select':
|
|
||||||
searchInput = $('<select class="' + searchInputClass + ' form-select form-select-sm" data-column="' + columnIndex + '" data-column-name="' + columnName + '" data-search-mode="' + searchMode + '"><option value="">All ' + columnName + '</option></select>');
|
|
||||||
|
|
||||||
// Add options if provided
|
|
||||||
if (selectOptions && Array.isArray(selectOptions)) {
|
|
||||||
selectOptions.forEach(function (option) {
|
|
||||||
searchInput.append('<option value="' + option.value + '">' + option.text + '</option>');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'date':
|
|
||||||
searchInput = $('<input type="date" class="' + searchInputClass + ' form-control form-control-sm" data-column="' + columnIndex + '" data-column-name="' + columnName + '" data-search-mode="' + searchMode + '" placeholder="' + (placeholder || 'Search ' + columnName + '...') + '">');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'number':
|
|
||||||
searchInput = $('<input type="number" class="' + searchInputClass + ' form-control form-control-sm" data-column="' + columnIndex + '" data-column-name="' + columnName + '" data-search-mode="' + searchMode + '" placeholder="' + (placeholder || 'Search ' + columnName + '...') + '">');
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // text
|
|
||||||
searchInput = $('<input type="text" class="' + searchInputClass + ' form-control form-control-sm" data-column="' + columnIndex + '" data-column-name="' + columnName + '" data-search-mode="' + searchMode + '" placeholder="' + (placeholder || 'Search ' + columnName + '...') + '">');
|
|
||||||
}
|
|
||||||
|
|
||||||
inputWrapper.append(searchInput);
|
|
||||||
searchContainer.append(inputWrapper);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add clear all button
|
|
||||||
const clearButton = $('<div class="d-inline-block me-2 mb-2"><label class="form-label small"> </label><button type="button" class="btn btn-outline-secondary btn-sm d-block clear-column-search">Clear All</button></div>');
|
|
||||||
searchContainer.append(clearButton);
|
|
||||||
|
|
||||||
// Bind search events
|
|
||||||
bindColumnSearchEvents(dataTable, searchInputClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind search events to column search inputs
|
|
||||||
* @param {Object} dataTable - DataTable instance
|
|
||||||
* @param {string} searchInputClass - CSS class for search inputs
|
|
||||||
*/
|
|
||||||
function bindColumnSearchEvents(dataTable, searchInputClass) {
|
|
||||||
// Individual column search
|
|
||||||
$('.' + searchInputClass).off('keyup change').on('keyup change', function () {
|
|
||||||
const columnIndex = $(this).data('column');
|
|
||||||
const searchValue = $(this).val();
|
|
||||||
const searchMode = $(this).data('search-mode') || 'contains';
|
|
||||||
|
|
||||||
// Apply column-specific search with regex based on search mode
|
|
||||||
let searchRegex = '';
|
|
||||||
|
|
||||||
if (searchValue) {
|
|
||||||
switch (searchMode) {
|
|
||||||
case 'exact':
|
|
||||||
// Exact match - search for the exact value
|
|
||||||
searchRegex = '^' + escapeRegex(searchValue) + '$';
|
|
||||||
break;
|
|
||||||
case 'starts':
|
|
||||||
// Starts with - search for values that start with the input
|
|
||||||
searchRegex = '^' + escapeRegex(searchValue);
|
|
||||||
break;
|
|
||||||
case 'ends':
|
|
||||||
// Ends with - search for values that end with the input
|
|
||||||
searchRegex = escapeRegex(searchValue) + '$';
|
|
||||||
break;
|
|
||||||
default: // 'contains'
|
|
||||||
// Contains - default DataTable behavior (no regex needed)
|
|
||||||
searchRegex = searchValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply search with regex if needed
|
|
||||||
if (searchMode !== 'contains' && searchValue) {
|
|
||||||
dataTable.column(columnIndex).search(searchRegex, true, false).draw();
|
|
||||||
} else {
|
|
||||||
dataTable.column(columnIndex).search(searchValue).draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update URL parameters (optional)
|
|
||||||
updateSearchParams($(this).data('column-name'), searchValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear all searches
|
|
||||||
$('.clear-column-search').off('click').on('click', function () {
|
|
||||||
$('.' + searchInputClass).val('');
|
|
||||||
|
|
||||||
// Clear all column searches
|
|
||||||
dataTable.columns().search('').draw();
|
|
||||||
|
|
||||||
// Clear URL parameters (optional)
|
|
||||||
clearAllSearchParams();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape special regex characters
|
|
||||||
* @param {string} string - String to escape
|
|
||||||
* @returns {string} Escaped string
|
|
||||||
*/
|
|
||||||
function escapeRegex(string) {
|
|
||||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populate select options dynamically from table data
|
|
||||||
* @param {Object} dataTable - DataTable instance
|
|
||||||
* @param {number} columnIndex - Column index to extract unique values
|
|
||||||
* @param {string} selectSelector - jQuery selector for the select element
|
|
||||||
*/
|
|
||||||
function populateSelectOptions(dataTable, columnIndex, selectSelector) {
|
|
||||||
// Get unique values from the column
|
|
||||||
const uniqueValues = dataTable.column(columnIndex).data().unique().sort();
|
|
||||||
const selectElement = $(selectSelector);
|
|
||||||
|
|
||||||
// Clear existing options (except the first "All" option)
|
|
||||||
selectElement.find('option:not(:first)').remove();
|
|
||||||
|
|
||||||
// Add unique values as options
|
|
||||||
uniqueValues.each(function (value) {
|
|
||||||
if (value && value.toString().trim() !== '') {
|
|
||||||
selectElement.append('<option value="' + value + '">' + value + '</option>');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update URL search parameters (optional feature)
|
|
||||||
* @param {string} paramName - Parameter name
|
|
||||||
* @param {string} paramValue - Parameter value
|
|
||||||
*/
|
|
||||||
function updateSearchParams(paramName, paramValue) {
|
|
||||||
if (typeof URLSearchParams === 'undefined') return;
|
|
||||||
|
|
||||||
const url = new URL(window.location);
|
|
||||||
if (paramValue && paramValue.trim() !== '') {
|
|
||||||
url.searchParams.set('search_' + paramName, paramValue);
|
|
||||||
} else {
|
|
||||||
url.searchParams.delete('search_' + paramName);
|
|
||||||
}
|
|
||||||
window.history.replaceState({}, '', url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all search parameters from URL
|
|
||||||
*/
|
|
||||||
function clearAllSearchParams() {
|
|
||||||
if (typeof URLSearchParams === 'undefined') return;
|
|
||||||
|
|
||||||
const url = new URL(window.location);
|
|
||||||
const keysToDelete = [];
|
|
||||||
|
|
||||||
for (const key of url.searchParams.keys()) {
|
|
||||||
if (key.indexOf('search_') === 0) {
|
|
||||||
keysToDelete.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keysToDelete.forEach(function (key) {
|
|
||||||
url.searchParams.delete(key);
|
|
||||||
});
|
|
||||||
window.history.replaceState({}, '', url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore search values from URL parameters
|
|
||||||
* @param {string} searchInputClass - CSS class for search inputs
|
|
||||||
*/
|
|
||||||
function restoreSearchFromURL(searchInputClass) {
|
|
||||||
if (typeof URLSearchParams === 'undefined') return;
|
|
||||||
|
|
||||||
const url = new URL(window.location);
|
|
||||||
|
|
||||||
$('.' + searchInputClass).each(function () {
|
|
||||||
const columnName = $(this).data('column-name');
|
|
||||||
const searchValue = url.searchParams.get('search_' + columnName);
|
|
||||||
|
|
||||||
if (searchValue) {
|
|
||||||
$(this).val(searchValue).trigger('change');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
74
CPRNIMS.WebApps/wwwroot/css/Item/Item.css
Normal file
74
CPRNIMS.WebApps/wwwroot/css/Item/Item.css
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
.search-wrapper {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-inner {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #fff;
|
||||||
|
border: 1.5px solid #d0e8e5;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 8px rgba(26, 122, 110, 0.08);
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-inner:focus-within {
|
||||||
|
border-color: #1a7a6e;
|
||||||
|
box-shadow: 0 0 0 3px rgba(26, 122, 110, 0.15), 0 2px 8px rgba(26, 122, 110, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12px 0 14px;
|
||||||
|
color: #1a7a6e;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-inner:focus-within .search-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
padding: 11px 4px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #2d2d2d;
|
||||||
|
background: transparent;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input::placeholder {
|
||||||
|
color: #aab8b6;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #888;
|
||||||
|
transition: color 0.15s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear:hover {
|
||||||
|
color: #1a7a6e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-hint {
|
||||||
|
font-size: 0.76rem;
|
||||||
|
color: #9ab5b1;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding-left: 2px;
|
||||||
|
min-height: 1.1em;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user