initial commit

This commit is contained in:
2026-07-21 15:03:40 +07:00
commit 5c20bb8a9e
2752 changed files with 864209 additions and 0 deletions
@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@ViewData["Title"]</title>
@await Html.PartialAsync("~/FrontEnd/Pages/Shared/AdminLTE/__css.cshtml")
@await RenderSectionAsync("styles", required: false)
<style>
[v-cloak] {
display: none !important;
}
</style>
@* vue *@
<script src="~/lib/vue/vue.global.prod.js"></script>
</head>
<body class="hold-transition sidebar-mini">
<!-- Site wrapper -->
<div class="wrapper">
<!-- Navbar -->
@await Html.PartialAsync("~/FrontEnd/Pages/Shared/AdminLTE/__navbar.cshtml")
<!-- /.navbar -->
<!-- Main Sidebar Container -->
@await Html.PartialAsync("~/FrontEnd/Pages/Shared/AdminLTE/__sidebar.cshtml")
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper p-4">
@Html.AntiForgeryToken()
@RenderBody()
</div>
<!-- /.content-wrapper -->
@await Html.PartialAsync("~/FrontEnd/Pages/Shared/AdminLTE/__footer.cshtml")
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->
</div>
<!-- ./wrapper -->
@await Html.PartialAsync("~/FrontEnd/Pages/Shared/AdminLTE/__js.cshtml")
@await RenderSectionAsync("scripts", required: false)
<script>
document.addEventListener('DOMContentLoaded', async function () {
async function getAvatar() {
try {
const avatar = StorageManager.getAvatar();
const response = await AxiosManager.get('/FileImage/GetImage?imageName=' + avatar, {
responseType: 'blob'
});
if (response.status === 200) {
const reader = new FileReader();
reader.onloadend = function () {
const base64Image = reader.result;
document.getElementById('AvatarImageSide').src = base64Image;
};
reader.readAsDataURL(response.data);
} else {
console.error('Error:', response.statusText);
}
} catch (error) {
console.error('Error:', error);
}
}
getAvatar();
});
</script>
<script>
document.addEventListener('DOMContentLoaded', async function () {
async function getCompany() {
try {
const response = await AxiosManager.get('/Company/GetCompanyList', {});
const data = response?.data?.content?.data;
if (data && data.length > 0) {
StorageManager.saveCompany(data[0]);
const companyName = data[0]?.name ?? 'Default Company';
const companyWeb = data[0]?.website ?? '#';
const year = new Date().getFullYear();
const currency = data[0]?.currency ?? '---';
document.getElementById('footerContent').innerHTML =
`© ${year} <a href="${companyWeb}">${companyName}</a>, All Right Reserved.`;
document.getElementById('topbarContent').innerHTML =
`Currency: ${currency}`;
}
} catch (error) {
console.error('Error:', error);
}
}
getCompany();
});
</script>
</body>
</html>
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@ViewData["Title"]</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
<!-- Font Awesome -->
<link rel="stylesheet" href="~/adminlte/plugins/fontawesome-free/css/all.min.css">
<!-- icheck bootstrap -->
<link rel="stylesheet" href="~/adminlte/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="~/adminlte/css/adminlte.min.css">
@await RenderSectionAsync("styles", required: false)
<style>
[v-cloak] {
display: none !important;
}
</style>
@* vue *@
<script src="~/lib/vue/vue.global.prod.js"></script>
<!-- JavaScript Libraries -->
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js" defer></script>
<script src="~/lib/axios/axios.min.js" defer></script>
<script src="~/lib/sweetalert/sweetalert2v11.js" defer></script>
<script src="~/lib/indotalent/storage-manager.js" defer></script>
<script src="~/lib/indotalent/security-manager.js" defer></script>
<script src="~/lib/indotalent/axios-manager.js" defer></script>
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<a asp-area="" asp-page="/Index"><b>INDOTALENT</b> WHMS.</a>
</div>
<!-- /.login-logo -->
<div class="card">
@RenderBody()
<!-- /.login-card-body -->
</div>
</div>
<!-- /.login-box -->
<!-- jQuery -->
<script src="~/adminlte/plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="~/adminlte/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- AdminLTE App -->
<script src="~/adminlte/js/adminlte.min.js"></script>
@await RenderSectionAsync("scripts", required: false)
</body>
</html>
@@ -0,0 +1,374 @@
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
<!-- Font Awesome -->
<link rel="stylesheet" href="~/adminlte/plugins/fontawesome-free/css/all.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="~/adminlte/css/adminlte.min.css">
<link href="~/lib/syncfusion/css/bootstrap5.css" rel="stylesheet" />
<script src="~/lib/syncfusion/js/ej2.min.js"></script>
<link href="~/lib/font-awesome/css/all.min.css" rel="stylesheet">
<link href="~/lib/bootstrap/dist/css/bootstrap-icons.css" rel="stylesheet">
<link href="~/lib/dropzone/dropzone.min.css" rel="stylesheet">
<style>
/* CUSTOM */
/*Form Group*/
.form-group {
margin-top: 10px;
}
/* For Syncfusion Grid */
.grid-container .e-gridcontent {
cursor: pointer;
}
.e-grid .e-row.e-altrow {
background-color: #F3F6F9;
}
.e-headertext {
padding-left: 5px;
}
.e-grid .e-gridpager .e-currentitem {
background-color: var(--primary);
color: #fff !important;
}
.e-grid .e-gridpager .e-numericitem {
color: var(--primary);
}
.e-contextmenu-wrapper ul .e-menu-item.e-selected, .e-contextmenu-container ul .e-menu-item.e-selected {
background-color: var(--primary);
color: #fff;
outline: 0 solid var(--primary);
outline-offset: 0;
}
.e-checkbox-wrapper .e-frame.e-stop, .e-css.e-checkbox-wrapper .e-frame.e-stop {
background-color: var(--primary);
border-color: var(--primary);
color: #fff;
}
.e-checkbox-wrapper .e-frame.e-check, .e-css.e-checkbox-wrapper .e-frame.e-check {
background-color: var(--primary);
border-color: var(--primary);
color: #fff;
}
.e-treeview .e-list-item.e-hover > .e-text-content .e-icon-collapsible {
color: #fff;
}
.e-treeview .e-list-item.e-hover > .e-text-content .e-icon-expandable {
color: #fff;
}
.e-treeview .e-list-item.e-hover > .e-text-content .e-list-text {
color: #fff;
}
.e-treeview .e-list-item.e-active.e-animation-active > .e-text-content .e-list-text {
color: #fff;
}
.e-treeview .e-list-item.e-active > .e-text-content {
background-color: #2a2a3c;
color: #fff;
}
.e-treeview .e-list-item.e-active > .e-fullrow {
background-color: #2a2a3c;
border-color: #2a2a3c;
}
.e-treeview .e-list-item.e-active.e-animation-active > .e-fullrow {
background-color: #2a2a3c;
border-color: #2a2a3c;
}
.e-treeview .e-list-item.e-hover > .e-fullrow {
background-color: #2a2a3c;
border-color: #2a2a3c;
}
.e-treeview .e-list-item.e-active.e-hover > .e-fullrow {
background-color: #2a2a3c;
border-color: #2a2a3c;
color: #fff;
}
.e-treeview .e-list-text {
color: #fff;
font-size: 14px;
}
.e-dropdownbase .e-list-item.e-item-focus, .e-dropdownbase .e-list-item.e-active, .e-dropdownbase .e-list-item.e-active.e-hover, .e-dropdownbase .e-list-item.e-hover {
background-color: var(--primary);
color: #fff;
}
.e-schedule .e-timeline-view .e-appointment, .e-schedule .e-timeline-month-view .e-appointment {
cursor: pointer;
}
.e-schedule .e-agenda-view .e-appointment {
border-left: 0px solid var(--primary);
cursor: pointer;
}
.e-quick-popup-wrapper .e-event-popup .e-popup-header .e-subject-wrap .e-subject {
border-left: 6px solid var(--primary);
}
.e-btn.e-primary, .e-css.e-btn.e-primary {
background-color: var(--primary);
border-color: var(--primary);
color: #fff;
}
.e-footer-content .e-btn.e-primary.e-flat:not([DISABLED]) {
background-color: var(--primary);
border-color: var(--primary);
color: #fff;
}
/*BS Navbar*/
.nav-tabs .nav-item .nav-link {
font-size: 0.9rem;
padding: 0.3rem 0.5rem;
}
.e-input-group, .e-input-group.e-control-wrapper {
border-radius: 0px;
}
.e-input-group.e-control-wrapper.e-disabled input.e-input {
background: var(--light);
}
/* Fieldset */
fieldset {
background-color: #fff;
width: 100%;
padding: 15px;
margin-left: 0px;
margin-right: 0px;
margin-top: 10px;
margin-bottom: 10px;
border: 1px solid #dee2e6;
}
/* For FormCard */
.form-card {
border: 1px solid #dee2e6;
width: 100%;
overflow-y: auto;
}
.form-card-header {
background-color: #ffffff;
color: #000000;
padding: 10px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
text-align: left;
}
.form-card-body {
background-color: #ffffff;
padding-top: 5px;
padding-right: 0;
padding-bottom: 5px;
padding-left: 0px;
}
.form-card-footer {
background-color: #ffffff;
color: #000000;
padding-top: 5px;
padding-right: 0;
padding-bottom: 5px;
padding-left: 0px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
text-align: left;
}
.form-label {
margin-bottom: 0px;
font-size: 0.875em;
}
.form-group {
margin-top: 0px;
}
.sticky-top {
z-index: 900 !important;
}
.content .navbar .navbar-nav .dropdown-item {
background-color: transparent !important;
}
.small-italic-left {
font-size: small;
font-style: italic;
text-align: left;
margin-bottom: -10px;
margin-left: 0px;
padding-top: 10px;
}
.small-italic {
font-size: small;
font-style: italic;
position: relative;
margin: 10px;
}
.small-italic::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
border-bottom: 1px solid #dee2e6;
}
</style>
<style>
/* CSS scrollbar sidebar */
#navbarnav::-webkit-scrollbar {
width: 12px;
}
#navbarnav::-webkit-scrollbar-track {
background-color: #191C24;
}
#navbarnav::-webkit-scrollbar-thumb {
background-color: #2a2a3c;
border-radius: 6px;
border: 3px solid #191C24;
transition: filter 0.3s;
}
#navbarnav::-webkit-scrollbar-thumb:hover {
cursor: pointer;
background-color: var(--dark);
}
</style>
<style>
/* CSS scrollbar other */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background-color: #f8f9fa;
}
::-webkit-scrollbar-thumb {
background-color: #dee2e6;
border-radius: 6px;
border: 3px solid #f8f9fa;
transition: filter 0.3s;
}
::-webkit-scrollbar-thumb:hover {
cursor: pointer;
background-color: var(--primary);
}
</style>
<style>
#navbarnav {
width: 250px;
height: 70vh;
overflow-y: auto;
}
a {
color: var(--primary);
}
.nav-link {
color: var(--primary);
}
.text-primary {
color: var(--primary) !important;
}
:root {
--primary: #1b84ff;
--bs-blue: #1b84ff;
--bs-primary: #1b84ff;
}
</style>
@* smaller form *@
<style>
.select2-container--bootstrap-5 .select2-results__option {
font-size: 0.9rem !important;
line-height: 1.5 !important;
}
form input.form-control,
form textarea.form-control,
form select.form-control,
.select2-container--bootstrap-5 .select2-selection {
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 0.9rem;
line-height: 1.5;
border-radius: 0.25rem;
box-shadow: none;
border: 1px solid #ced4da;
}
form textarea.form-control {
height: auto;
}
form .row.mb-3 {
margin-bottom: 0.5rem;
}
.card-header h5 {
font-size: 1rem;
margin-bottom: 0;
}
.card-body {
padding: 0.75rem;
}
</style>
@@ -0,0 +1,15 @@
@{
var companyName = "companyName";
var companyWeb = "companyWeb";
var year = DateTime.Now.Year;
}
<footer class="main-footer">
<div class="float-right d-none d-sm-block">
<!--/*** This app is free as long as you keep the footer authors credit link/attribution link/backlink. If you'd like to use the app without the footer authors credit link/attribution link/backlink, you can purchase the Credit Removal License from "https://store.indotalent.com". Thank you for your support. ***/-->
<b>Developed By:</b> <a href="https://store.indotalent.com">https://store.indotalent.com</a>
</div>
<small id="footerContent">
&copy; @year <a href="@companyWeb">@companyName</a>, All Right Reserved.
</small>
</footer>
@@ -0,0 +1,126 @@
<!-- jQuery -->
<script src="~/adminlte/plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="~/adminlte/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- AdminLTE App -->
<script src="~/adminlte/js/adminlte.min.js"></script>
<!-- JavaScript Libraries -->
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="~/lib/axios/axios.min.js"></script>
<script src="~/lib/sweetalert/sweetalert2v11.js"></script>
<script src="~/lib/dropzone/dropzone.min.js"></script>
<script src="~/lib/indotalent/storage-manager.js"></script>
<script src="~/lib/indotalent/security-manager.js"></script>
<script src="~/lib/indotalent/axios-manager.js"></script>
<script src="~/lib/indotalent/number-format-manager.js"></script>
<script src="~/lib/indotalent/date-format-manager.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script>
// Sidebar Toggler
document.addEventListener('DOMContentLoaded', function() {
const sidebarToggler = document.querySelector('.sidebar-toggler');
if (sidebarToggler) {
sidebarToggler.addEventListener('click', function(event) {
document.querySelector('.sidebar').classList.toggle("open");
document.querySelector('.content').classList.toggle("open");
event.preventDefault();
});
}
});
var hideSpinnerAndShowContent = () => {
const spinner = document.getElementById('spinner');
if (spinner) {
spinner.classList.remove('show');
}
}
window.addEventListener('load', function () {
var formcard = document.getElementById('formcard');
if (formcard) {
var formcardPosition = sessionStorage.getItem('formcardPosition');
if (formcardPosition !== null) {
setTimeout(function () {
formcard.scrollTop = parseInt(formcardPosition);
sessionStorage.removeItem('formcardPosition');
}, 1000);
}
} else {
sessionStorage.setItem('formcardPosition', 0);
}
});
window.addEventListener('beforeunload', () => {
var formcard = document.getElementById('formcard');
if (formcard) {
sessionStorage.setItem('formcardPosition', formcard.scrollTop);
}
});
</script>
<script>
// for maintaining sidebar scroll position
document.addEventListener('DOMContentLoaded', function () {
var sidebar = document.getElementById('navbarnav');
if (sidebar) {
var scrollPosition = sessionStorage.getItem('sidebarScrollPosition');
if (scrollPosition !== null) {
setTimeout(() => {
sidebar.scrollTop = parseInt(scrollPosition);
sessionStorage.removeItem('sidebarScrollPosition');
}, 0);
}
sidebar.addEventListener('scroll', function () {
sessionStorage.setItem('sidebarScrollPosition', sidebar.scrollTop);
});
}
});
window.addEventListener('beforeunload', () => {
var sidebar = document.getElementById('navbarnav');
if (sidebar) {
sessionStorage.setItem('sidebarScrollPosition', sidebar.scrollTop);
}
});
</script>
<script>
function showStatusMessage(statusMessage) {
if (statusMessage && statusMessage.includes("Error")) {
new ej.notifications.Toast({
content: statusMessage,
cssClass: "e-toast-danger",
icon: 'e-error toast-icons',
position: { X: "Left", Y: "Top" }
}, '#Toast').show();
} else {
new ej.notifications.Toast({
content: statusMessage,
cssClass: "e-toast-success",
icon: 'e-success toast-icons',
position: { X: "Left", Y: "Top" }
}, '#Toast').show();
}
}
</script>
<script>
function formatNumberToN2(number) {
return Number(number).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
</script>
@@ -0,0 +1,34 @@
@{
}
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Left navbar links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
</li>
</ul>
<!-- Right navbar links -->
<span class="mt-0" id="topbarContent">Currency: </span>
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown">
<a class="nav-link" data-toggle="dropdown" href="#">
<i class="fas fa-th-large"></i>
</a>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
<div class="dropdown-divider"></div>
<a asp-area="" asp-page="/Profiles/MyProfile" class="dropdown-item">
<i class="fas fa-users mr-2"></i> Profile
</a>
<div class="dropdown-divider"></div>
<form class="form-inline" asp-area="" asp-page="/Accounts/Logout">
<button type="submit" class="nav-link btn btn-link text-black dropdown-item"><i class="fas fa-lock mr-2"></i>Log Out</button>
</form>
<div class="dropdown-divider"></div>
</div>
</li>
</ul>
</nav>
@@ -0,0 +1,116 @@
@{
var userEmail = "Loading...";
var avatarUrl = "/noimage.png";
var appName = "WMS";
}
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a asp-area="" asp-page="/Dashboards/DefaultDashboard" class="brand-link d-flex justify-content-center align-items-center">
<span class="brand-text font-weight-light">@appName</span>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar user (optional) -->
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img id="AvatarImageSide" src="@avatarUrl" class="img-circle elevation-2" alt="User Image" style="width: 40px; height: 40px;">
</div>
<div class="info">
<a asp-area="" asp-page="/Profiles/MyProfile" class="d-block" id="userEmail">@userEmail</a>
</div>
</div>
<!-- Sidebar Menu -->
<nav class="mt-2">
<div id="navbarnav" class="navbar-nav w-100">
<div id="mainMenuContainer">
<div id="mainMenu"></div>
</div>
</div>
</nav>
<!-- /.sidebar-menu -->
</div>
<!-- /.sidebar -->
</aside>
@* _sidebar.cshtml *@
<script>
document.addEventListener('DOMContentLoaded', function () {
try {
const email = StorageManager.getEmail();
const userEmail = `${email}`;
document.getElementById('userEmail').textContent = userEmail;
const currentUrl = window.location.pathname;
const firstSegment = currentUrl.split('/')[1];
function filterMenu(menuData, userRoles) {
const childMenus = menuData.filter(menu => {
if (menu.navURL) {
const firstSegment = menu.navURL.split('/')[1];
return userRoles.some(role => role.toLowerCase() === firstSegment.toLowerCase());
}
return false;
});
const validChildIds = new Set(childMenus.map(menu => menu.id));
const parentIds = new Set(childMenus.map(menu => menu.pid));
return menuData.filter(menu => validChildIds.has(menu.id) || parentIds.has(menu.id));
}
function updateMenuSelection(menuData, segment) {
const parentMap = {};
menuData.forEach(item => {
parentMap[item.id] = item;
});
menuData.forEach(item => {
item.isSelected = false;
item.expanded = false;
if (item.navURL && item.navURL.includes(`/${segment}`)) {
item.isSelected = true;
let parentId = item.pid;
while (parentId) {
const parent = parentMap[parentId];
if (parent) {
parent.expanded = true;
parentId = parent.pid;
} else {
break;
}
}
}
});
return menuData;
}
const userRoles = StorageManager.getUserRoles();
let menuData = StorageManager.getMenuNavigation();
const filteredMenu = filterMenu(menuData, userRoles);
menuData = updateMenuSelection(filteredMenu, firstSegment);
const mainMenu = new ej.navigations.TreeView({
fullRowNavigable: true,
fields: {
dataSource: menuData,
id: 'id',
text: 'name',
selected: 'isSelected',
parentID: 'pid',
hasChildren: 'hasChild',
expanded: 'expanded',
navigateUrl: 'navURL',
},
enablePersistence: false,
});
mainMenu.appendTo('#mainMenu');
} catch (error) {
console.error('Error fetching data from StorageManager:', error);
}
});
</script>
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WHMS - .NET 9 - Warehouse & Inventory Management System</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="" name="keywords">
<meta content="" name="description">
@await Html.PartialAsync("__css")
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
@RenderBody()
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
@@ -0,0 +1,46 @@
<!-- Google Web Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500&family=Roboto:wght@500;700;900&display=swap" rel="stylesheet">
<!-- Icon Font Stylesheet -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css" rel="stylesheet">
<style>
.lock-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #212529;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
flex-direction: column;
}
.lock-icon {
font-size: 3em;
color: #c2c7d0;
border: 2px solid #F6B53F;
border-radius: 10px;
padding: 20px;
}
.lock-welcome {
color: #c2c7d0;
font-size: 2em;
font-family: Heebo, sans-serif;
margin-bottom: 20px;
}
</style>