NonInventPurchasingSystem/CPRNIMS.WebApps/Views/Account/Index.cshtml
2026-05-20 16:50:48 +08:00

1585 lines
56 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<title>User Management</title>
<link href="~/lib/bootstrap-icons/font/bootstrap-icons.min.css" rel="stylesheet" />
<style>
:root {
--teal-900: #04342C;
--teal-800: #085041;
--teal-700: #0F6E56;
--teal-600: #0F7A60;
--teal-500: #1D9E75;
--teal-400: #3dbf96;
--teal-100: #9FE1CB;
--teal-50: #E1F5EE;
--surface: #f6f8f7;
--card-bg: #ffffff;
--text-primary: #0d1f1a;
--text-secondary: #4a6b61;
--text-muted: #8ba89f;
--border: #ddeee8;
--shadow-sm: 0 1px 3px rgba(15,110,86,.07), 0 1px 2px rgba(15,110,86,.04);
--shadow-md: 0 4px 16px rgba(15,110,86,.10), 0 1px 4px rgba(15,110,86,.06);
--shadow-lg: 0 8px 32px rgba(15,110,86,.13), 0 2px 8px rgba(15,110,86,.07);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 24px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'DM Sans', sans-serif;
background: var(--surface);
color: var(--text-primary);
min-height: 100vh;
}
/* ── Page header ── */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0px 32px 0;
margin-bottom: 24px;
}
.page-title-group h1 {
font-family: 'Space Grotesk', sans-serif;
font-size: 22px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -.3px;
}
.page-title-group p {
font-size: 13.5px;
color: var(--text-muted);
margin-top: 2px;
}
.btn-create {
display: inline-flex;
align-items: center;
gap: 8px;
background: var(--teal-600);
color: #fff;
border: none;
border-radius: var(--radius-md);
padding: 10px 20px;
font-size: 14px;
font-weight: 500;
font-family: 'DM Sans', sans-serif;
cursor: pointer;
transition: background .18s, transform .12s;
box-shadow: 0 2px 8px rgba(29,158,117,.25);
}
.btn-create:hover {
background: var(--teal-700);
transform: translateY(-1px);
}
.btn-create:active {
transform: scale(.98);
}
/* ── Stat cards ── */
.stats-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
padding: 0 32px;
margin-bottom: 24px;
}
.stat-card {
background: var(--card-bg);
border-radius: var(--radius-lg);
border: 1px solid var(--border);
padding: 18px 20px;
box-shadow: var(--shadow-sm);
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: var(--teal-500);
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.stat-card.danger::before {
background: #E24B4A;
}
.stat-card.amber::before {
background: #EF9F27;
}
.stat-card.blue::before {
background: #378ADD;
}
.stat-label {
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: .6px;
color: var(--text-muted);
margin-bottom: 6px;
}
.stat-value {
font-family: 'Space Grotesk', sans-serif;
font-size: 26px;
font-weight: 600;
color: var(--text-primary);
line-height: 1;
}
.stat-icon {
position: absolute;
right: 16px;
top: 16px;
width: 36px;
height: 36px;
border-radius: var(--radius-sm);
background: var(--teal-50);
display: flex;
align-items: center;
justify-content: center;
color: var(--teal-700);
font-size: 17px;
}
.stat-card.danger .stat-icon {
background: #FCEBEB;
color: #A32D2D;
}
.stat-card.amber .stat-icon {
background: #FAEEDA;
color: #854F0B;
}
.stat-card.blue .stat-icon {
background: #E6F1FB;
color: #185FA5;
}
/* ── Main card / table ── */
.table-card {
margin: 0 32px 32px;
background: var(--card-bg);
border-radius: var(--radius-xl);
border: 1px solid var(--border);
box-shadow: var(--shadow-md);
overflow: hidden;
}
.table-card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 24px;
border-bottom: 1px solid var(--border);
background: linear-gradient(135deg, var(--teal-900) 0%, var(--teal-700) 100%);
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
.table-card-header h2 {
font-family: 'Space Grotesk', sans-serif;
font-size: 16px;
font-weight: 600;
color: #fff;
letter-spacing: -.2px;
}
.search-box {
display: flex;
align-items: center;
gap: 8px;
background: rgba(255,255,255,.12);
border: 1px solid rgba(255,255,255,.2);
border-radius: var(--radius-md);
padding: 7px 14px;
color: #fff;
font-size: 13px;
}
.search-box input {
background: transparent;
border: none;
outline: none;
color: #fff;
font-size: 13px;
font-family: 'DM Sans', sans-serif;
width: 180px;
}
.search-box input::placeholder {
color: rgba(255,255,255,.55);
}
table.um-table {
width: 100%;
border-collapse: collapse;
}
table.um-table thead th {
font-size: 11.5px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .5px;
color: var(--text-muted);
padding: 13px 16px;
border-bottom: 1px solid var(--border);
background: #fafcfb;
white-space: nowrap;
}
table.um-table tbody tr {
border-bottom: 1px solid var(--border);
transition: background .14s;
}
table.um-table tbody tr:last-child {
border-bottom: none;
}
table.um-table tbody tr:hover {
background: var(--teal-50);
}
table.um-table td {
padding: 14px 16px;
font-size: 13.5px;
color: var(--text-primary);
vertical-align: middle;
}
/* user cell with avatar */
.user-cell {
display: flex;
align-items: center;
gap: 10px;
}
.avatar {
width: 34px;
height: 34px;
border-radius: 50%;
background: var(--teal-100);
color: var(--teal-800);
font-size: 12px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-family: 'Space Grotesk', sans-serif;
}
.avatar.blue {
background: #B5D4F4;
color: #0C447C;
}
.avatar.amber {
background: #FAC775;
color: #633806;
}
.avatar.purple {
background: #CECBF6;
color: #3C3489;
}
.avatar.coral {
background: #F5C4B3;
color: #712B13;
}
.user-name {
font-weight: 500;
font-size: 13.5px;
}
.user-sub {
font-size: 11.5px;
color: var(--text-muted);
}
/* badges */
.badge-status, .badge-role, .badge-company {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 10px;
border-radius: 20px;
font-size: 11.5px;
font-weight: 500;
}
.badge-status.active {
background: #EAF3DE;
color: #3B6D11;
}
.badge-status.inactive {
background: #FCEBEB;
color: #A32D2D;
}
.badge-role {
background: var(--teal-50);
color: var(--teal-800);
}
.badge-company {
background: #E6F1FB;
color: #185FA5;
}
/* action buttons */
.action-wrap {
display: flex;
gap: 6px;
}
.action-btn {
width: 30px;
height: 30px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: var(--card-bg);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
font-size: 14px;
transition: all .15s;
}
.action-btn:hover.edit {
background: var(--teal-50);
border-color: var(--teal-400);
color: var(--teal-700);
}
.action-btn:hover.access {
background: #E6F1FB;
border-color: #85B7EB;
color: #185FA5;
}
.action-btn:hover.delete {
background: #FCEBEB;
border-color: #F09595;
color: #A32D2D;
}
/* pagination */
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 24px;
border-top: 1px solid var(--border);
background: #fafcfb;
border-radius: 0 0 var(--radius-xl) var(--radius-xl);
}
.table-footer span {
font-size: 13px;
color: var(--text-muted);
}
.pager {
display: flex;
gap: 4px;
}
.pager-btn {
width: 30px;
height: 30px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: var(--card-bg);
font-size: 12.5px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all .15s;
}
.pager-btn.active {
background: var(--teal-600);
border-color: var(--teal-600);
color: #fff;
}
.pager-btn:hover:not(.active) {
background: var(--teal-50);
border-color: var(--teal-400);
color: var(--teal-700);
}
/* ── MODAL styles ── */
.modal-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(4,52,44,.45);
backdrop-filter: blur(3px);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal-overlay.show {
display: flex;
}
.modal-box {
background: var(--card-bg);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
width: 90%;
max-width: 520px;
overflow: hidden;
animation: slideUp .22s cubic-bezier(.34,1.3,.64,1);
}
.modal-box.lg {
max-width: 760px;
}
.modal-box.xl {
max-width: 980px;
}
.modal-head {
padding: 22px 24px 18px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(135deg, var(--teal-900), var(--teal-700));
}
.modal-head h3 {
font-family: 'Space Grotesk', sans-serif;
font-size: 15px;
font-weight: 600;
color: #fff;
display: flex;
align-items: center;
gap: 8px;
}
.modal-head .icon-badge {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
background: rgba(255,255,255,.15);
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.modal-close {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
border: 1px solid rgba(255,255,255,.25);
background: rgba(255,255,255,.1);
color: rgba(255,255,255,.8);
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
transition: background .15s;
}
.modal-close:hover {
background: rgba(255,255,255,.2);
color: #fff;
}
.modal-body {
padding: 24px;
max-height: 70vh;
overflow-y: auto;
}
.form-label {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .5px;
color: var(--text-muted);
display: block;
margin-bottom: 6px;
}
.form-input {
width: 100%;
padding: 9px 13px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: 13.5px;
font-family: 'DM Sans', sans-serif;
color: var(--text-primary);
background: var(--card-bg);
transition: border .15s, box-shadow .15s;
outline: none;
}
.form-input:focus {
border-color: var(--teal-500);
box-shadow: 0 0 0 3px rgba(29,158,117,.12);
}
.form-input[readonly] {
background: var(--surface);
color: var(--text-muted);
}
select.form-input {
cursor: pointer;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 16px;
}
.form-group {
margin-bottom: 16px;
}
.modal-foot {
padding: 16px 24px;
border-top: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
background: #fafcfb;
}
.btn-secondary {
padding: 9px 18px;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--card-bg);
font-size: 13.5px;
font-family: 'DM Sans', sans-serif;
font-weight: 500;
color: var(--text-secondary);
cursor: pointer;
transition: all .15s;
}
.btn-secondary:hover {
background: var(--surface);
border-color: var(--teal-400);
color: var(--teal-700);
}
.btn-primary {
padding: 9px 22px;
border: none;
border-radius: var(--radius-md);
background: var(--teal-600);
font-size: 13.5px;
font-family: 'DM Sans', sans-serif;
font-weight: 500;
color: #fff;
cursor: pointer;
transition: all .15s;
box-shadow: 0 2px 8px rgba(29,158,117,.22);
}
.btn-primary:hover {
background: var(--teal-700);
transform: translateY(-1px);
}
/* Profile modal sidebar */
.profile-layout {
display: grid;
grid-template-columns: 220px 1fr;
gap: 0;
}
.profile-sidebar {
background: var(--teal-900);
padding: 24px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.profile-avatar-lg {
width: 80px;
height: 80px;
border-radius: 50%;
background: var(--teal-500);
color: #fff;
font-size: 28px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Space Grotesk', sans-serif;
border: 3px solid rgba(255,255,255,.2);
}
.profile-sidebar h4 {
color: #fff;
font-size: 14px;
font-weight: 600;
text-align: center;
}
.profile-sidebar span {
color: var(--teal-100);
font-size: 12px;
}
.profile-nav {
width: 100%;
margin-top: 8px;
}
.profile-nav a {
display: flex;
align-items: center;
gap: 8px;
padding: 9px 12px;
border-radius: var(--radius-sm);
color: rgba(255,255,255,.65);
font-size: 13px;
text-decoration: none;
transition: all .15s;
margin-bottom: 2px;
}
.profile-nav a:hover, .profile-nav a.active {
background: rgba(255,255,255,.1);
color: #fff;
}
.profile-content {
padding: 24px;
overflow-y: auto;
max-height: 60vh;
}
/* Nav tabs inside modal */
.um-tabs {
display: flex;
gap: 0;
border-bottom: 1px solid var(--border);
margin-bottom: 20px;
}
.um-tab {
padding: 10px 18px;
font-size: 13px;
font-weight: 500;
color: var(--text-muted);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all .15s;
}
.um-tab.active {
color: var(--teal-600);
border-bottom-color: var(--teal-600);
}
.um-tab-pane {
display: none;
}
.um-tab-pane.active {
display: block;
}
/* Access rights table inside modal */
table.access-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
table.access-table th {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .4px;
color: var(--text-muted);
padding: 9px 12px;
background: #fafcfb;
border-bottom: 1px solid var(--border);
}
table.access-table td {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
vertical-align: middle;
}
table.access-table tr:last-child td {
border-bottom: none;
}
table.access-table tr:hover td {
background: var(--teal-50);
}
.counter-pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
background: var(--teal-50);
border-radius: 20px;
font-size: 13px;
color: var(--teal-800);
font-weight: 500;
margin-bottom: 14px;
}
/* Success toast */
/* .toast-wrap {
position: fixed;
bottom: 28px;
right: 28px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
background: var(--teal-900);
color: #fff;
border-radius: var(--radius-md);
padding: 12px 18px;
font-size: 13.5px;
display: flex;
align-items: center;
gap: 10px;
box-shadow: var(--shadow-lg);
animation: toastIn .22s ease;
} */
/* Checkbox custom */
input[type="checkbox"] {
accent-color: var(--teal-600);
width: 15px;
height: 15px;
cursor: pointer;
}
/* scrollbar */
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--teal-100);
border-radius: 4px;
}
</style>
<body>
<!-- ── Page Header ── -->
<div class="page-header">
<div class="page-title-group">
<h1><i class="bi bi-people" style="font-size:20px; margin-right:8px; color:var(--teal-600)"></i>User Management</h1>
<p>Manage system users, roles, and access permissions</p>
</div>
<button class="btn-create" onclick="openModal('modalCreate')">
<i class="bi bi-person-plus-fill"></i> Create New User
</button>
</div>
<!-- ── Stat Cards ── -->
<div class="stats-row">
<div class="stat-card">
<div class="stat-label">Total Users</div>
<div class="stat-value">124</div>
<div class="stat-icon"><i class="bi bi-people-fill"></i></div>
</div>
<div class="stat-card">
<div class="stat-label">Active</div>
<div class="stat-value">110</div>
<div class="stat-icon"><i class="bi bi-check-circle-fill"></i></div>
</div>
<div class="stat-card danger">
<div class="stat-label">Inactive</div>
<div class="stat-value">14</div>
<div class="stat-icon"><i class="bi bi-slash-circle-fill"></i></div>
</div>
<div class="stat-card blue">
<div class="stat-label">Companies</div>
<div class="stat-value">4</div>
<div class="stat-icon"><i class="bi bi-building-fill"></i></div>
</div>
</div>
<!-- ── Table Card ── -->
<div class="table-card">
<div class="table-card-header">
<h2><i class="bi bi-list-ul" style="margin-right:8px;opacity:.8"></i>List of Users</h2>
<div class="search-box">
<i class="bi bi-search" style="opacity:.7; font-size:13px;"></i>
<input type="text" placeholder="Search users..." oninput="filterTable(this.value)">
</div>
</div>
<div style="overflow-x:auto;">
<table id="UserListTable" class="um-table" cellspacing="0" width="100%">
<thead>
<tr>
<th style="width:100px;">Actions</th>
<th>Full Name</th>
<th>Username</th>
<th>Email</th>
<th>Company</th>
<th>Role</th>
<th>Status</th>
<th>Created By</th>
<th>Created Date</th>
<th>Updated By</th>
<th>Updated Date</th>
<th hidden></th>
<th hidden></th>
<th hidden></th>
</tr>
</thead>
<tbody id="userTbody">
</tbody>
</table>
</div>
<div class="table-footer">
<span id="pageInfo">Showing 16 of 6 users</span>
<div class="pager">
<button class="pager-btn"><i class="bi bi-chevron-left" style="font-size:11px;"></i></button>
<button class="pager-btn active">1</button>
<button class="pager-btn">2</button>
<button class="pager-btn">3</button>
<button class="pager-btn"><i class="bi bi-chevron-right" style="font-size:11px;"></i></button>
</div>
</div>
</div>
<!-- ══════════════════════════════════
MODAL: Create New User
══════════════════════════════════ -->
<div class="modal-overlay" id="modalCreate">
<div class="modal-box">
<div class="modal-head">
<h3><span class="icon-badge"><i class="bi bi-person-plus"></i></span>Create New User</h3>
<button class="modal-close" onclick="closeModal('modalCreate')"><i class="bi bi-x"></i></button>
</div>
<div class="modal-body">
<div class="form-row">
<div><label class="form-label">Full Name</label><input class="form-input" type="text" placeholder="e.g. Juan dela Cruz"></div>
<div><label class="form-label">Username</label><input class="form-input" type="text" placeholder="e.g. JDCRUZ25"></div>
</div>
<div class="form-row">
<div>
<label class="form-label">Company</label>
<select class="form-input">
<option>LLI</option>
<option>MDLD</option>
<option>EUROPRINT</option>
<option>Traders</option>
</select>
</div>
<div>
<label class="form-label">Department</label>
<select class="form-input"><option>-- Select --</option></select>
</div>
</div>
<div class="form-row">
<div>
<label class="form-label">Role</label>
<select class="form-input"><option>LLIAdmin</option><option>LLIApprover</option><option>Admin</option></select>
</div>
<div><label class="form-label">Email</label><input class="form-input" type="email" placeholder="user@lloydlab.com"></div>
</div>
<div class="form-group">
<label class="form-label">Password</label>
<input class="form-input" type="password" placeholder="Min. 8 characters">
</div>
</div>
<div class="modal-foot">
<button class="btn-secondary" onclick="closeModal('modalCreate')">Cancel</button>
<button class="btn-primary" onclick="showToast('User created successfully!'); closeModal('modalCreate')"><i class="bi bi-person-check-fill" style="margin-right:6px;"></i>Register</button>
</div>
</div>
</div>
<!-- ══════════════════════════════════
MODAL: Update User Profile
══════════════════════════════════ -->
<div class="modal-overlay" id="modalProfile">
<div class="modal-box xl">
<div class="modal-head">
<h3><span class="icon-badge"><i class="bi bi-person-badge"></i></span>User Profile</h3>
<button class="modal-close" onclick="closeModal('modalProfile')"><i class="bi bi-x"></i></button>
</div>
<div class="profile-layout">
<!-- sidebar -->
<div class="profile-sidebar">
<div class="profile-avatar-lg" id="profileAvatarLg">CT</div>
<h4 id="profileSidebarName">Catherine S. Toronon</h4>
<span id="profileSidebarRole">Approver</span>
<div style="display:flex; gap:8px; margin-top:4px;">
<button style="padding:6px 12px; border-radius:var(--radius-sm); border:1px solid rgba(255,255,255,.2); background:rgba(255,255,255,.1); color:#fff; font-size:12px; cursor:pointer;">Follow</button>
<button style="padding:6px 12px; border-radius:var(--radius-sm); border:1px solid rgba(255,255,255,.2); background:rgba(255,255,255,.1); color:#fff; font-size:12px; cursor:pointer;">Message</button>
</div>
<nav class="profile-nav">
<a href="#" class="active"><i class="bi bi-ticket-perforated"></i> Ticket List</a>
<a href="#"><i class="bi bi-headset"></i> Support Staff</a>
<a href="#"><i class="bi bi-sliders"></i> Configurations</a>
</nav>
</div>
<!-- content tabs -->
<div class="profile-content">
<div class="um-tabs">
<div class="um-tab active" onclick="switchTab(this,'tabDetail')">Detail</div>
<div class="um-tab" onclick="switchTab(this,'tabProfile')">Profile</div>
<div class="um-tab" onclick="switchTab(this,'tabSettings')">Settings</div>
</div>
<div class="um-tab-pane active" id="tabDetail">
<div class="form-row">
<div><label class="form-label">Username</label><input class="form-input" type="text" value="LSCATTOR25" readonly></div>
<div><label class="form-label">Full Name</label><input class="form-input" type="text" value="Catherine S. Toronon"></div>
</div>
<div class="form-row">
<div>
<label class="form-label">Role</label>
<select class="form-input"><option selected>LLIApprover</option><option>LLIAdmin</option><option>Admin</option></select>
</div>
<div><label class="form-label">Email</label><input class="form-input" type="email" value="cstoronon@lloydlab.com"></div>
</div>
<div class="form-group">
<label class="form-label">New Password</label>
<input class="form-input" type="password" placeholder="Leave blank to keep current">
</div>
</div>
<div class="um-tab-pane" id="tabProfile">
<div class="form-group">
<label class="form-label">Address</label>
<input class="form-input" type="text" placeholder="Street address">
</div>
<div class="form-row">
<div><label class="form-label">Phone Number</label><input class="form-input" type="text" placeholder="+63"></div>
<div>
<label class="form-label">Company</label>
<select class="form-input"><option>LLI</option><option>MDLD</option><option>EUROPRINT</option><option>Traders</option></select>
</div>
</div>
<div>
<label class="form-label">Gender</label>
<div style="display:flex; gap:20px; margin-top:4px;">
<label style="display:flex;align-items:center;gap:6px;font-size:13.5px;cursor:pointer;"><input type="radio" name="gender" style="accent-color:var(--teal-600);"> Male</label>
<label style="display:flex;align-items:center;gap:6px;font-size:13.5px;cursor:pointer;"><input type="radio" name="gender" style="accent-color:var(--teal-600);"> Female</label>
</div>
</div>
</div>
<div class="um-tab-pane" id="tabSettings">
<div style="display:flex; align-items:center; justify-content:space-between; padding:14px 0; border-bottom:1px solid var(--border);">
<div>
<div style="font-size:13.5px; font-weight:500;">Account Lockout</div>
<div style="font-size:12px; color:var(--text-muted);">Prevent user from logging in</div>
</div>
<label style="position:relative; width:42px; height:24px; cursor:pointer;">
<input type="checkbox" id="lockoutToggle" style="opacity:0;width:0;height:0;" onchange="toggleSwitch(this)">
<span id="lockoutSwitchTrack" style="position:absolute;inset:0;border-radius:12px;background:var(--border);transition:background .2s;"></span>
<span id="lockoutSwitchThumb" style="position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#fff;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,.2);"></span>
</label>
</div>
</div>
<div style="margin-top:20px;">
<button class="btn-primary" onclick="showToast('Profile updated!'); closeModal('modalProfile')"><i class="bi bi-check2-circle" style="margin-right:6px;"></i>Save Changes</button>
</div>
</div>
</div>
</div>
</div>
<!-- ══════════════════════════════════
MODAL: User Access Rights
══════════════════════════════════ -->
<div class="modal-overlay" id="modalAccess">
<div class="modal-box xl">
<div class="modal-head">
<h3 id="labelAddNewAccess"><span class="icon-badge"><i class="bi bi-shield-lock"></i></span></h3>
<button class="modal-close" onclick="closeModal('modalAccess')"><i class="bi bi-x"></i></button>
</div>
<div class="modal-body">
<div class="form-row" style="margin-bottom:20px;">
<div>
<label class="form-label">Full Name</label>
<input class="form-input" type="text" id="ua-FullName" readonly>
</div>
<div>
<label class="form-label">Email Address</label>
<input class="form-input" type="text" id="ua-EmailAddress" readonly>
</div>
</div>
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:12px;">
<div class="counter-pill"><i class="bi bi-check2-square"></i> Total Selected: <strong id="totalSelAccess">0</strong></div>
<button class="btn-create" id="btnAddAccess" style="font-size:13px; padding:8px 14px;" onclick="viewUserAccessNotExist()"><i class="bi bi-plus"></i> Add Access</button>
</div>
<table id="access-table" class="table-hover" style="width:100%;">
<thead>
<tr>
<th>
<input id="selectAllCheckboxAccess"
type="checkbox"
class="selectAllCheckboxAccess"
style="margin-right:6px;" />
All
</th>
<th>Page Type</th>
<th>Page Name</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="modal-foot">
<button class="btn-secondary" onclick="closeModal('modalAccess')">Cancel</button>
<button class="btn-primary" onclick="postPutAccessRights(true); closeModal('modalAccess')"><i class="bi bi-shield-check" style="margin-right:6px;"></i>Save Rights</button>
</div>
</div>
</div>
<script>
$(document).ready(function () {
loader = $('#overlay, #loader');
populateRoles();
userListTable = $('#UserListTable').DataTable({
dom: 'lrtip',
ajax: $.extend({
url: '/Account/GetAllUsers',
type: 'GET',
}, beforeComplete(loader)),
columns: [
// [0] Action buttons
{
data: null,
render: function (data, type, row) {
return renderUserBtns(data, row);
}
},
// [1] Full Name — avatar + name
{
data: 'fullName',
render: function (data) {
if (!data) return '';
const initials = data.split(' ').filter(Boolean).slice(0, 2).map(w => w[0].toUpperCase()).join('');
const colors = ['', 'blue', 'amber', 'purple', 'coral'];
const colorClass = colors[data.charCodeAt(0) % colors.length];
return `<div class="user-cell">
<div class="avatar ${colorClass}">${initials}</div>
<div class="user-name">${data}</div>
</div>`;
}
},
// [2] Username
{ data: 'userName' },
// [3] Email
{ data: 'email' },
// [4] Company
{
data: 'company',
render: d => `<span class="badge-company">${d ?? ''}</span>`
},
// [5] Role
{
data: 'role',
render: d => `<span class="badge-role">${d ?? ''}</span>`
},
// [6] Status — from lockoutEnabled
{
data: 'lockoutEnabled',
render: function (data) {
const isActive = !data;
return `<span class="badge-status ${isActive ? 'active' : 'inactive'}">
<i class="bi bi-circle-fill" style="font-size:7px;"></i>
${isActive ? 'Active' : 'Inactive'}
</span>`;
}
},
// [7] Created By
{ data: 'createdBy' },
// [8] Created Date
{ data: 'createdDate', render: d => formatDate(d) },
// [9] Updated By
{ data: 'updatedBy' },
// [10] Updated Date
{ data: 'updatedDate', render: d => formatDate(d) },
// [11] ID — hidden
{ data: 'id', visible: false },
// [12] Address — hidden
{ data: 'address', visible: false },
// [13] Profile picture — hidden
{
data: null,
visible: false,
render: (d, t, row) => `<img src="/${row.url}" alt="profilePicture" />`
}
],
columnDefs: colOnColDef,
language: { emptyTable: "No record available" },
rowCallback: rowAccountCallback,
responsive: true,
error: errorHandler,
initComplete: function () {
const table = this.api();
updateStatCards(table);
initCompleteCallback.call(this);
}
});
});
function filterTable(value) {
if (userListTable) {
userListTable.search(value).draw();
}
}
// ═══════════════════════════════════════════════════════
// Action buttons — rendered inside each DataTable row
// ═══════════════════════════════════════════════════════
function renderUserBtns(data, row) {
// Derive avatar values from live row data
const name = row.fullName ?? '';
const initials = name.split(' ').filter(Boolean).slice(0, 2).map(w => w[0].toUpperCase()).join('');
const colors = ['', 'blue', 'amber', 'purple', 'coral'];
const colorClass = name ? colors[name.charCodeAt(0) % colors.length] : '';
// Safely encode strings for onclick attributes
const safeName = name.replace(/'/g, "\\'");
const safeInitials = initials.replace(/'/g, "\\'");
const safeColor = colorClass.replace(/'/g, "\\'");
const safeRole = (row.role ?? '').replace(/'/g, "\\'");
const safeId = row.id ?? '';
// Encode full row as JSON — passed to viewUserAccess without many args
const jsonRow = encodeURIComponent(JSON.stringify(row));
let html = '<div class="action-wrap">';
// Edit / Profile button
html += `<button
class="action-btn edit"
title="Edit Profile"
onclick="openProfile('${safeName}','${safeInitials}','${safeColor}','${safeRole}','${safeId}')">
<i class="bi bi-pencil-square"></i>
</button>`;
// Access Rights button — calls your existing viewUserAccess with the full row
html += `<button
class="action-btn access"
title="Access Rights"
onclick="viewUserAccess(JSON.parse(decodeURIComponent('${jsonRow}')))">
<i class="bi bi-shield-lock"></i>
</button>`;
html += '</div>';
return html;
}
// ═══════════════════════════════════════════════════════
// Existing Access Rights functions
// Bootstrap modals — kept exactly as your original logic
// ═══════════════════════════════════════════════════════
function viewUserAccess(data) {
loader = $('#overlay, #loader');
UserId = data.id;
document.getElementById('btnAddAccess').style.display='block';
document.getElementById('labelAddNewAccess').innerHTML='User Access Rights';
$('#ua-EmailAddress').val(data.email);
console.log('data.email',data.email);
$('#ua-FullName').val(data.fullName);
$('#uan-EmailAddress').val(data.email);
$('#uan-FullName').val(data.fullName);
openModal('modalAccess');
tableName = '#access-table';
totalSelectedLabel = $('#totalSelAccess');
clearTableSelection(tableName, selectedProductsMap, () => {
totalSelectedLabel.text(0);
}, 'selected-row', '.select-all-Access-checkbox');
tableElement = $(tableName);
tableDestroy(tableElement);
accessDataTable = tableElement.DataTable({
ajax: $.extend({
url: '/Account/GetUserRights',
type: 'GET',
data: { userId: UserId },
}, beforeComplete(loader)),
language: { emptyTable: "No record available" },
initComplete: function () {
initializeTableSelection({
tableName: tableName,
dataTable: accessDataTable,
selectedItemsMap: selectedProductsMap,
idKey: 'userAccessId',
idKey2: 'contAccId',
checkboxClass: '.select-Access-checkbox',
selectAllClass: '.select-all-Access-checkbox',
selectedRowClass: 'selected-row',
updateCountCallback: function () {
totalSelectedLabel.text(getSelectedCount(selectedProductsMap));
}
});
},
columns: [
{
data: 'userAccessId',
render: function () {
return '<input type="checkbox" class="select-Access-checkbox" />';
}
},
{ data: 'accessType' },
{ data: 'pageName' },
{ data: 'isActive' },
{
data: null,
render: function (data, type, row) {
return renderAccessDetailbtn(data, row);
}
}],
responsive: true,
error: errorHandler
});
}
function viewUserAccessNotExist() {
closeModal('modalAccess');
loader = $('#overlay, #loader');
document.getElementById('btnAddAccess').style.display='none';
document.getElementById('labelAddNewAccess').innerHTML='Add New Access Rights';
var submitButton = $('#btnNotAccess');
var totalSelectedLabel = $('#totalSelNotAccess');
var IsNotExist = true;
openModal('modalAccess');
tableName = '#access-table';
totalSelectedLabel = $('#totalSelAccess');
clearTableSelection(tableName, selectedProductsMap, () => {
totalSelectedLabel.text(0);
}, 'selected-row', '.select-all-Access-checkbox');
tableElement = $(tableName);
tableDestroy(tableElement);
accessDataTable = tableElement.DataTable({
ajax: $.extend({
url: '/Account/GetUserRights',
type: 'GET',
data: { UserId, IsNotExist },
}, beforeComplete(loader)),
language: { emptyTable: "No record available" },
initComplete: function () {
initializeTableSelection({
tableName: tableName,
dataTable: accessDataTable,
selectedItemsMap: selectedProductsMap,
idKey: 'userAccessId',
idKey2: 'contAccId',
checkboxClass: '.select-Access-checkbox',
selectAllClass: '.select-all-Access-checkbox',
selectedRowClass: 'selected-row',
updateCountCallback: function () {
totalSelectedLabel.text(getSelectedCount(selectedProductsMap));
}
});
},
columns: [
{
data: 'userAccessId',
title: '<input type="checkbox" class="select-all-Access-checkbox" />',
render: function () {
return '<input type="checkbox" class="select-Access-checkbox" />';
},
orderable: false,
searchable: false
},
{ data: 'accessType' },
{ data: 'pageName' },
{ data: 'isActive' },
{
data: null,
render: function (data, type, row) {
return renderAccessDetailbtn(data, row);
}
}],
responsive: true,
error: errorHandler
});
}
// ═══════════════════════════════════════════════════════
// Profile modal
// ═══════════════════════════════════════════════════════
function openProfile(name, initials, color, role, userId) {
console.log('pasok naman? openProfile');
// Sidebar fields
const avatar = document.getElementById('profileAvatarLg');
if (avatar) {
avatar.textContent = initials;
avatar.style.background = color ? 'var(--teal-400)' : 'var(--teal-500)';
}
setContent('profileSidebarName', name);
setContent('profileSidebarRole', role);
// Pull matching row from DataTable and pre-fill every form field
if (userListTable && userId) {
userListTable.rows().every(function () {
const row = this.data();
if (String(row.id) === String(userId)) {
// Detail tab
setValue('UserName', row.userName);
setValue('FullName', row.fullName);
setValue('Email', row.email);
setValue('Password', ''); // never pre-fill password
setSelect('UserRole', row.role);
// Profile tab
setValue('Address', row.address);
setValue('PhoneNumber', row.phoneNumber);
setSelect('Company', row.company);
// Settings tab — lockout toggle
const lockCb = document.getElementById('lockoutToggle');
if (lockCb) {
lockCb.checked = row.lockoutEnabled;
toggleSwitch(lockCb);
}
// Profile picture
const img = document.getElementById('profilePictureImage');
if (img && row.url) img.src = '/' + row.url;
// Hidden fields your postPut reads
setValue('PFullName', row.fullName);
setValue('PUserRole', row.role);
}
});
}
openModal('modalProfile');
}
// ═══════════════════════════════════════════════════════
// Custom modal helpers (new UI overlay modals)
// ═══════════════════════════════════════════════════════
function openModal(id) {
const el = document.getElementById(id);
if (el) el.classList.add('show');
}
function closeModal(id) {
const el = document.getElementById(id);
if (el) el.classList.remove('show');
}
// Close on backdrop click
document.querySelectorAll('.modal-overlay').forEach(function (overlay) {
overlay.addEventListener('click', function (e) {
if (e.target === overlay) overlay.classList.remove('show');
});
});
// ═══════════════════════════════════════════════════════
// Profile modal — tab switching
// ═══════════════════════════════════════════════════════
function switchTab(el, target) {
document.querySelectorAll('.um-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.um-tab-pane').forEach(p => p.classList.remove('active'));
el.classList.add('active');
const pane = document.getElementById(target);
if (pane) pane.classList.add('active');
}
// ═══════════════════════════════════════════════════════
// Settings tab — lockout toggle switch
// ═══════════════════════════════════════════════════════
function toggleSwitch(cb) {
const track = document.getElementById('lockoutSwitchTrack');
const thumb = document.getElementById('lockoutSwitchThumb');
if (!track || !thumb) return;
if (cb.checked) {
track.style.background = 'var(--teal-600)';
thumb.style.transform = 'translateX(18px)';
} else {
track.style.background = 'var(--border)';
thumb.style.transform = 'translateX(0)';
}
}
// ═══════════════════════════════════════════════════════
// Toast notifications
// ═══════════════════════════════════════════════════════
function showToast(msg) {
closeModal('modalAccess');
viewUserAccessNotExist();
// const wrap = document.getElementById('toastWrap');
// if (!wrap) return;
// const t = document.createElement('div');
// t.className = 'toast';
// t.innerHTML = `<i class="bi bi-check-circle-fill" style="color:var(--teal-400);font-size:16px;"></i> ${msg}`;
// wrap.appendChild(t);
// setTimeout(() => t.remove(), 3200);
}
// ═══════════════════════════════════════════════════════
// Stat cards — updated from live DataTable data
// ═══════════════════════════════════════════════════════
function updateStatCards(api) {
try {
const rows = api.rows().data().toArray();
const total = rows.length;
const active = rows.filter(r => !r.lockoutEnabled).length;
const inactive = rows.filter(r => r.lockoutEnabled).length;
const companies = [...new Set(rows.map(r => r.company).filter(Boolean))].length;
const set = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; };
set('statTotal', total);
set('statActive', active);
set('statInactive', inactive);
set('statCompanies', companies);
} catch (e) {
console.warn('updateStatCards:', e);
}
}
// ═══════════════════════════════════════════════════════
// Shared helpers
// ═══════════════════════════════════════════════════════
function formatDate(data) {
if (!data) return '';
const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
const d = new Date(data);
return `${months[d.getMonth()]}/${d.getDate()}/${d.getFullYear()}`;
}
// Set input/select value safely
function setValue(id, val) {
const el = document.getElementById(id);
if (el) el.value = val ?? '';
}
// Set textContent (labels, spans) safely
function setContent(id, val) {
const el = document.getElementById(id);
if (el) el.textContent = val ?? '';
}
// Set a <select> to the matching option
function setSelect(id, val) {
const el = document.getElementById(id);
if (!el) return;
const opt = [...el.options].find(o => o.value === val);
if (opt) opt.selected = true;
}
</script>
</body>
<script src="~/jsfunctions/account/index.js"></script>
<script src="~/jsfunctions/account/AccountPostPut.js"></script>
<script src="~/jsfunctions/account/accountvar.js"></script>
<script src="~/jsfunctions/utilities/StylesV3.js"></script>
<script src="~/JsFunctions/Utilities/utilsV3.js"></script>
<script src="~/jsfunctions/utilities/NewStyle.js"></script>
<script src="~/jsfunctions/account/rowcallback.js"></script>
<script src="~/jsfunctions/account/accountcolumn.js"></script>