Initial commit
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
@model PowderCoating.Application.DTOs.User.CreateCompanyUserDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Add New User";
|
||||
ViewData["PageIcon"] = "bi-person-plus";
|
||||
ViewData["PageHelpTitle"] = "Add New User";
|
||||
ViewData["PageHelpContent"] = "Creates a new login account for a member of your company. The email address doubles as the login username. Set a temporary password — the user can change it from their Profile page after their first login. Assign a Role, then fine-tune individual permissions below.";
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="d-flex justify-content-end mb-4">
|
||||
<a asp-action="Index" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-1"></i>Back to List
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<form asp-action="Create" method="post">
|
||||
<partial name="_ValidationSummary" />
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 pb-2 border-bottom">
|
||||
<h5 class="card-title mb-0">Basic Information</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Basic Information"
|
||||
data-bs-content="First Name, Last Name, and Email are required. The email is used as the login username — it must be unique across the system. Employee Number is an optional internal reference. The user can update their name and phone from their own Profile page after logging in.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="FirstName" class="form-label">First Name *</label>
|
||||
<input asp-for="FirstName" class="form-control" />
|
||||
<span asp-validation-for="FirstName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="LastName" class="form-label">Last Name *</label>
|
||||
<input asp-for="LastName" class="form-control" />
|
||||
<span asp-validation-for="LastName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Email" class="form-label">Email *</label>
|
||||
<input asp-for="Email" class="form-control" type="email" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Password" class="form-label">Password *</label>
|
||||
<input asp-for="Password" class="form-control" type="password" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="EmployeeNumber" class="form-label">Employee Number</label>
|
||||
<input asp-for="EmployeeNumber" class="form-control" />
|
||||
<span asp-validation-for="EmployeeNumber" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Phone" class="form-label">Phone</label>
|
||||
<input asp-for="Phone" class="form-control" />
|
||||
<span asp-validation-for="Phone" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 pb-2 border-bottom">
|
||||
<h5 class="card-title mb-0">Role & Department</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Role & Department"
|
||||
data-bs-content="Role controls default access level: Viewer = read-only, Worker = day-to-day tasks, Manager = broader access, Company Admin = full access to everything. Department and Position are informational and appear on the user's profile. Hire Date is used for record-keeping.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label asp-for="CompanyRole" class="form-label">Role *</label>
|
||||
<select asp-for="CompanyRole" class="form-select">
|
||||
<option value="Viewer">Viewer (Read-only)</option>
|
||||
<option value="Worker">Worker</option>
|
||||
<option value="Manager">Manager</option>
|
||||
<option value="CompanyAdmin">Company Admin</option>
|
||||
</select>
|
||||
<span asp-validation-for="CompanyRole" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Department" class="form-label">Department</label>
|
||||
<input asp-for="Department" class="form-control" />
|
||||
<span asp-validation-for="Department" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Position" class="form-label">Position</label>
|
||||
<input asp-for="Position" class="form-control" />
|
||||
<span asp-validation-for="Position" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="HireDate" class="form-label">Hire Date</label>
|
||||
<input asp-for="HireDate" class="form-control" type="date" />
|
||||
<span asp-validation-for="HireDate" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 pb-2 border-bottom">
|
||||
<h5 class="card-title mb-0">Permissions</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Permissions"
|
||||
data-bs-content="Fine-grained permissions let you grant specific capabilities beyond what the Role provides. Company Admin automatically receives all permissions and the checkboxes will be locked. For other roles, check only what this user needs. Permissions can be updated at any time from the Edit page.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div id="companyAdminAlert" class="alert alert-info" style="display: none;">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<strong>Company Admins automatically have all permissions.</strong> These checkboxes are disabled because Company Admins always have full access to all features.
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageJobs" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageJobs" class="form-check-label">Can Manage Jobs</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageInventory" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageInventory" class="form-check-label">Can Manage Inventory</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageCustomers" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageCustomers" class="form-check-label">Can Manage Customers</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanCreateQuotes" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanCreateQuotes" class="form-check-label">Can Create Quotes</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanApproveQuotes" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanApproveQuotes" class="form-check-label">Can Approve Quotes</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageCalendar" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageCalendar" class="form-check-label">Can Manage Calendar</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanViewCalendar" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanViewCalendar" class="form-check-label">Can View Calendar</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageProducts" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageProducts" class="form-check-label">Can Manage Products</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanViewProducts" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanViewProducts" class="form-check-label">Can View Products</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageEquipment" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageEquipment" class="form-check-label">Can Manage Equipment</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageVendors" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageVendors" class="form-check-label">Can Manage Vendors</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageMaintenance" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageMaintenance" class="form-check-label">Can Manage Maintenance</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageInvoices" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageInvoices" class="form-check-label">Can Manage Invoices</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanViewReports" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanViewReports" class="form-check-label">Can View Reports</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 justify-content-end">
|
||||
<a asp-action="Index" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save me-1"></i>Create User
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const roleSelect = document.getElementById('CompanyRole');
|
||||
const permissionCheckboxes = document.querySelectorAll('.permission-checkbox');
|
||||
const adminAlert = document.getElementById('companyAdminAlert');
|
||||
|
||||
function updatePermissionState() {
|
||||
const isCompanyAdmin = roleSelect.value === 'CompanyAdmin';
|
||||
|
||||
// Show/hide alert
|
||||
adminAlert.style.display = isCompanyAdmin ? 'block' : 'none';
|
||||
|
||||
// Check all and disable if Company Admin, otherwise enable
|
||||
permissionCheckboxes.forEach(checkbox => {
|
||||
if (isCompanyAdmin) {
|
||||
checkbox.checked = true;
|
||||
checkbox.disabled = true;
|
||||
} else {
|
||||
checkbox.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
updatePermissionState();
|
||||
|
||||
// Run when role changes
|
||||
roleSelect.addEventListener('change', updatePermissionState);
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
@model PowderCoating.Application.DTOs.User.UpdateCompanyUserDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Edit User";
|
||||
ViewData["PageIcon"] = "bi-person-gear";
|
||||
ViewData["PageHelpTitle"] = "Edit User";
|
||||
ViewData["PageHelpContent"] = "Update this user's account details, role, and permissions. Unchecking User Active prevents the user from logging in without deleting their account or history. Changing the email here also changes their login username — notify them so they can log in with the new address.";
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="d-flex justify-content-end mb-4">
|
||||
@if (!string.IsNullOrEmpty(ViewBag.ReturnUrl))
|
||||
{
|
||||
<a href="@ViewBag.ReturnUrl" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-1"></i>Back
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-action="Index" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left me-1"></i>Back to List
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<form asp-action="Edit" method="post">
|
||||
<input type="hidden" asp-for="Id" />
|
||||
<partial name="_ValidationSummary" />
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 pb-2 border-bottom">
|
||||
<h5 class="card-title mb-0">Basic Information</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Basic Information"
|
||||
data-bs-content="Email is this user's login username — changing it here means they must use the new address to log in. User Active controls whether the account can sign in; deactivating preserves all data without deleting the account. To reset a password, the user can use Forgot Password on the login page, or a SuperAdmin can set one directly.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="FirstName" class="form-label">First Name *</label>
|
||||
<input asp-for="FirstName" class="form-control" />
|
||||
<span asp-validation-for="FirstName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="LastName" class="form-label">Last Name *</label>
|
||||
<input asp-for="LastName" class="form-control" />
|
||||
<span asp-validation-for="LastName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Email" class="form-label">Email *</label>
|
||||
<input asp-for="Email" class="form-control" type="email" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
<small class="form-text text-muted">This is also the user's login address.</small>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="EmployeeNumber" class="form-label">Employee Number</label>
|
||||
<input asp-for="EmployeeNumber" class="form-control" />
|
||||
<span asp-validation-for="EmployeeNumber" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Phone" class="form-label">Phone</label>
|
||||
<input asp-for="Phone" class="form-control" />
|
||||
<span asp-validation-for="Phone" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch mt-4">
|
||||
<input asp-for="IsActive" class="form-check-input" />
|
||||
<label asp-for="IsActive" class="form-check-label">User Active</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 pb-2 border-bottom">
|
||||
<h5 class="card-title mb-0">Role & Department</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Role & Department"
|
||||
data-bs-content="Changing the Role updates the user's base access level immediately on save. Termination Date is informational — to actually prevent login, also uncheck User Active above. Department and Position appear on the user's profile card and in the Manage Users list.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label asp-for="CompanyRole" class="form-label">Role *</label>
|
||||
<select asp-for="CompanyRole" class="form-select">
|
||||
<option value="Viewer">Viewer (Read-only)</option>
|
||||
<option value="Worker">Worker</option>
|
||||
<option value="Manager">Manager</option>
|
||||
<option value="CompanyAdmin">Company Admin</option>
|
||||
</select>
|
||||
<span asp-validation-for="CompanyRole" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Department" class="form-label">Department</label>
|
||||
<input asp-for="Department" class="form-control" />
|
||||
<span asp-validation-for="Department" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label asp-for="Position" class="form-label">Position</label>
|
||||
<input asp-for="Position" class="form-control" />
|
||||
<span asp-validation-for="Position" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="HireDate" class="form-label">Hire Date</label>
|
||||
<input asp-for="HireDate" class="form-control" type="date" />
|
||||
<span asp-validation-for="HireDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label asp-for="TerminationDate" class="form-label">Termination Date</label>
|
||||
<input asp-for="TerminationDate" class="form-control" type="date" />
|
||||
<span asp-validation-for="TerminationDate" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 pb-2 border-bottom">
|
||||
<h5 class="card-title mb-0">Permissions</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Permissions"
|
||||
data-bs-content="Fine-grained permissions let you grant specific capabilities beyond what the Role provides. Company Admin automatically receives all permissions and the checkboxes will be locked. SuperAdmins can override individual permissions regardless of role. Changes take effect immediately on save.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div id="companyAdminAlert" class="alert alert-info" style="display: none;">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<strong>Company Admins automatically have all permissions.</strong> These checkboxes are disabled because Company Admins always have full access to all features.
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageJobs" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageJobs" class="form-check-label">Can Manage Jobs</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageInventory" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageInventory" class="form-check-label">Can Manage Inventory</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageCustomers" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageCustomers" class="form-check-label">Can Manage Customers</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanCreateQuotes" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanCreateQuotes" class="form-check-label">Can Create Quotes</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanApproveQuotes" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanApproveQuotes" class="form-check-label">Can Approve Quotes</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageCalendar" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageCalendar" class="form-check-label">Can Manage Calendar</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanViewCalendar" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanViewCalendar" class="form-check-label">Can View Calendar</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageProducts" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageProducts" class="form-check-label">Can Manage Products</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanViewProducts" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanViewProducts" class="form-check-label">Can View Products</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageEquipment" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageEquipment" class="form-check-label">Can Manage Equipment</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageVendors" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageVendors" class="form-check-label">Can Manage Vendors</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageMaintenance" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageMaintenance" class="form-check-label">Can Manage Maintenance</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanManageInvoices" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanManageInvoices" class="form-check-label">Can Manage Invoices</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input asp-for="CanViewReports" class="form-check-input permission-checkbox" />
|
||||
<label asp-for="CanViewReports" class="form-check-label">Can View Reports</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 justify-content-end">
|
||||
@if (!string.IsNullOrEmpty(ViewBag.ReturnUrl))
|
||||
{
|
||||
<input type="hidden" name="returnUrl" value="@ViewBag.ReturnUrl" />
|
||||
<a href="@ViewBag.ReturnUrl" class="btn btn-secondary">Cancel</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-action="Index" class="btn btn-secondary">Cancel</a>
|
||||
}
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save me-1"></i>Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const roleSelect = document.getElementById('CompanyRole');
|
||||
const permissionCheckboxes = document.querySelectorAll('.permission-checkbox');
|
||||
const adminAlert = document.getElementById('companyAdminAlert');
|
||||
const isSuperAdmin = @((ViewBag.IsSuperAdmin as bool? ?? false) ? "true" : "false");
|
||||
|
||||
function updatePermissionState() {
|
||||
const isCompanyAdmin = roleSelect.value === 'CompanyAdmin';
|
||||
|
||||
if (isSuperAdmin) {
|
||||
// SuperAdmins can always edit individual permissions
|
||||
adminAlert.style.display = 'none';
|
||||
permissionCheckboxes.forEach(checkbox => { checkbox.disabled = false; });
|
||||
return;
|
||||
}
|
||||
|
||||
// Show/hide alert
|
||||
adminAlert.style.display = isCompanyAdmin ? 'block' : 'none';
|
||||
|
||||
// Check all and disable if Company Admin, otherwise enable
|
||||
permissionCheckboxes.forEach(checkbox => {
|
||||
if (isCompanyAdmin) {
|
||||
checkbox.checked = true;
|
||||
checkbox.disabled = true;
|
||||
} else {
|
||||
checkbox.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
updatePermissionState();
|
||||
|
||||
// Run when role changes
|
||||
roleSelect.addEventListener('change', updatePermissionState);
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
@model PagedResult<PowderCoating.Application.DTOs.User.CompanyUserListDto>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Manage Users";
|
||||
ViewData["PageIcon"] = "bi-people";
|
||||
ViewData["PageHelpTitle"] = "Manage Users";
|
||||
ViewData["PageHelpContent"] = "Add and manage user accounts for your company. Each user has a Role (Viewer, Worker, Manager, Company Admin) and individual permissions that control what they can access. Inactive users cannot log in but their history is preserved. Click a row to edit a user, or use the pause/play button to quickly toggle their active status.";
|
||||
}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex justify-content-end mb-4">
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="bi bi-person-plus me-1"></i>Add New User
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
@if (Model != null && Model.Items.Any())
|
||||
{
|
||||
<!-- Desktop Table View -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th sortable="Name" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Name</th>
|
||||
<th sortable="Email" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Email</th>
|
||||
<th sortable="CompanyRole" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Role</th>
|
||||
<th sortable="Department" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Department</th>
|
||||
<th>Hire Date</th>
|
||||
<th sortable="LastLoginDate" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Last Login</th>
|
||||
<th sortable="IsActive" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var user in Model.Items)
|
||||
{
|
||||
<tr style="cursor:pointer" onclick="window.location='@Url.Action("Edit", new { id = user.Id })'">
|
||||
<td>
|
||||
<strong>@user.FullName</strong>
|
||||
</td>
|
||||
<td>@user.Email</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(user.CompanyRole))
|
||||
{
|
||||
<span class="badge @(user.CompanyRole switch
|
||||
{
|
||||
"CompanyAdmin" => "bg-danger",
|
||||
"Manager" => "bg-primary",
|
||||
"Worker" => "bg-info",
|
||||
_ => "bg-secondary"
|
||||
})">@user.CompanyRole</span>
|
||||
}
|
||||
</td>
|
||||
<td>@(user.Department ?? "N/A")</td>
|
||||
<td>
|
||||
<small class="text-muted">@user.HireDate.ToString("MMM d, yyyy")</small>
|
||||
</td>
|
||||
<td>
|
||||
@if (user.LastLoginDate.HasValue)
|
||||
{
|
||||
<small class="text-muted">@user.LastLoginDate.Value.ToString("MMM d, yyyy")</small>
|
||||
}
|
||||
else
|
||||
{
|
||||
<small class="text-muted">Never</small>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (user.IsBanned)
|
||||
{
|
||||
<span class="badge bg-danger"><i class="bi bi-slash-circle"></i> Banned</span>
|
||||
}
|
||||
else if (user.IsActive)
|
||||
{
|
||||
<span class="badge bg-success">Active</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end" onclick="event.stopPropagation()">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<a asp-action="Edit" asp-route-id="@user.Id"
|
||||
class="btn btn-outline-primary" title="Edit">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form asp-action="ToggleActive" asp-route-id="@user.Id"
|
||||
method="post" class="d-inline">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit"
|
||||
class="btn @(user.IsActive ? "btn-outline-warning" : "btn-outline-success")"
|
||||
title="@(user.IsActive ? "Deactivate" : "Activate")">
|
||||
<i class="bi bi-@(user.IsActive ? "pause" : "play")"></i>
|
||||
</button>
|
||||
</form>
|
||||
@if (!user.IsBanned)
|
||||
{
|
||||
<button type="button"
|
||||
class="btn btn-outline-danger"
|
||||
title="Ban User"
|
||||
onclick="openBanModal('@user.Id', '@Html.Encode(user.FullName)')">
|
||||
<i class="bi bi-slash-circle"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form asp-action="UnbanUser" asp-route-id="@user.Id" method="post" class="d-inline"
|
||||
onsubmit="return confirm('Lift the ban on @Html.Encode(user.FullName)?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-outline-success" title="Unban User">
|
||||
<i class="bi bi-check-circle"></i>
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Card View -->
|
||||
<div class="mobile-card-view">
|
||||
<div class="mobile-card-list">
|
||||
@foreach (var user in Model.Items)
|
||||
{
|
||||
<div class="mobile-data-card">
|
||||
<div class="mobile-card-header">
|
||||
<div class="mobile-card-icon" style="background: @(user.CompanyRole switch
|
||||
{
|
||||
"CompanyAdmin" => "linear-gradient(135deg, #eb3349 0%, #f45c43 100%)",
|
||||
"Manager" => "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
||||
"Worker" => "linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)",
|
||||
_ => "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
|
||||
});">
|
||||
<i class="bi bi-person-circle"></i>
|
||||
</div>
|
||||
<div class="mobile-card-title">
|
||||
<h6>@user.FullName</h6>
|
||||
<small>
|
||||
@if (!string.IsNullOrEmpty(user.CompanyRole))
|
||||
{
|
||||
<span class="badge @(user.CompanyRole switch
|
||||
{
|
||||
"CompanyAdmin" => "bg-danger",
|
||||
"Manager" => "bg-primary",
|
||||
"Worker" => "bg-info",
|
||||
_ => "bg-secondary"
|
||||
})">@user.CompanyRole</span>
|
||||
}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-card-body">
|
||||
<div class="mobile-card-row">
|
||||
<span class="mobile-card-label">Email</span>
|
||||
<span class="mobile-card-value">@user.Email</span>
|
||||
</div>
|
||||
<div class="mobile-card-row">
|
||||
<span class="mobile-card-label">Department</span>
|
||||
<span class="mobile-card-value">@(user.Department ?? "N/A")</span>
|
||||
</div>
|
||||
<div class="mobile-card-row">
|
||||
<span class="mobile-card-label">Hire Date</span>
|
||||
<span class="mobile-card-value text-muted">@user.HireDate.ToString("MMM d, yyyy")</span>
|
||||
</div>
|
||||
<div class="mobile-card-row">
|
||||
<span class="mobile-card-label">Last Login</span>
|
||||
<span class="mobile-card-value text-muted">
|
||||
@if (user.LastLoginDate.HasValue)
|
||||
{
|
||||
@user.LastLoginDate.Value.ToString("MMM d, yyyy")
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Never</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mobile-card-row">
|
||||
<span class="mobile-card-label">Status</span>
|
||||
<span class="mobile-card-value">
|
||||
@if (user.IsBanned)
|
||||
{
|
||||
<span class="badge bg-danger"><i class="bi bi-slash-circle"></i> Banned</span>
|
||||
}
|
||||
else if (user.IsActive)
|
||||
{
|
||||
<span class="badge bg-success">Active</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-card-footer">
|
||||
<a asp-action="Edit" asp-route-id="@user.Id" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-pencil me-1"></i>Edit
|
||||
</a>
|
||||
<form asp-action="ToggleActive" asp-route-id="@user.Id" method="post" class="d-inline">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-sm @(user.IsActive ? "btn-outline-warning" : "btn-outline-success")">
|
||||
<i class="bi bi-@(user.IsActive ? "pause" : "play") me-1"></i>@(user.IsActive ? "Deactivate" : "Activate")
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-people" style="font-size: 3rem; color: #ccc;"></i>
|
||||
<p class="text-muted mt-3">No users found.</p>
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="bi bi-person-plus me-1"></i>Add Your First User
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (Model != null && Model.TotalCount > 0)
|
||||
{
|
||||
<div class="card-footer bg-white">
|
||||
@await Html.PartialAsync("_Pagination", Model)
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ban User Modal -->
|
||||
<div class="modal fade" id="banModal" tabindex="-1" aria-labelledby="banModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h5 class="modal-title" id="banModalLabel"><i class="bi bi-slash-circle"></i> Ban User</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form asp-action="BanUser" method="post" id="banForm">
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="id" id="banUserId" />
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
Banning <strong id="banUserName"></strong> will immediately prevent them from logging in.
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="banReason" class="form-label">Reason <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="banReason" name="reason"
|
||||
placeholder="e.g. Unauthorized access attempt" required maxlength="500" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger"><i class="bi bi-slash-circle"></i> Ban User</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
function openBanModal(userId, userName) {
|
||||
document.getElementById('banUserId').value = userId;
|
||||
document.getElementById('banUserName').textContent = userName;
|
||||
document.getElementById('banReason').value = '';
|
||||
new bootstrap.Modal(document.getElementById('banModal')).show();
|
||||
}
|
||||
</script>
|
||||
}
|
||||
Reference in New Issue
Block a user