a0bdd2b5b4
Replace all corruption variants with HTML entities across 226 view files: - 3-char UTF-8-as-Win1252 sequences (ae-corruption) - Standalone smart/curly quotes that break C# Razor expressions - Partially re-corrupted variants where the 3rd byte was normalised to ASCII tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the script itself never contains a literal non-ASCII character; supports -DryRun .githooks/pre-commit: blocks commits containing the ae-corruption byte signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the hook is repo-committed and active for all future work on this machine. Build clean; 225 unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
333 lines
23 KiB
Plaintext
333 lines
23 KiB
Plaintext
@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="Accountant">Accountant</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-4">
|
|
<label asp-for="LaborCostPerHour" class="form-label">Labor Cost Rate</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">$</span>
|
|
<input asp-for="LaborCostPerHour" type="number" step="0.01" min="0" max="10000" class="form-control" placeholder="Use company default" />
|
|
<span class="input-group-text">/hr</span>
|
|
</div>
|
|
<span asp-validation-for="LaborCostPerHour" class="text-danger"></span>
|
|
<small class="text-muted">Used for internal job costing only — never shown to customers. Overrides the company default when set. Leave blank to use the shop-wide rate.</small>
|
|
</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 alert-permanent" 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 class="form-text text-muted small">Create, edit, and update job status; manage job items, worker assignments, and time entries</div>
|
|
</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 class="form-text text-muted small">Add and adjust powder stock, record transactions, manage reorder levels</div>
|
|
</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 class="form-text text-muted small">Create and edit customer records, pricing tiers, tax exemption, and credit limits</div>
|
|
</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 class="form-text text-muted small">Build quotes with line items and AI photo quoting; convert accepted quotes to jobs</div>
|
|
</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 class="form-text text-muted small">Accept or reject submitted quotes on behalf of the company</div>
|
|
</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 class="form-text text-muted small">Create, edit, and delete oven schedule batches and calendar events</div>
|
|
</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 class="form-text text-muted small">View the oven scheduler and job calendar (read-only, no editing)</div>
|
|
</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 class="form-text text-muted small">Add and edit catalog items, service pricing, and pricing tier configurations</div>
|
|
</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 class="form-text text-muted small">Browse the service catalog and inventory items (read-only, no editing)</div>
|
|
</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 class="form-text text-muted small">Add and update equipment records, track status changes and operating costs</div>
|
|
</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 class="form-text text-muted small">Add and edit vendor/supplier records, payment terms, and contact information</div>
|
|
</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 class="form-text text-muted small">Schedule and record equipment maintenance tasks, assign technicians, log parts used</div>
|
|
</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 class="form-text text-muted small">Create invoices, record payments, send to customers, issue voids and write-offs</div>
|
|
</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 class="form-text text-muted small">Access financial and operational reports, analytics dashboards, and data exports</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input asp-for="CanManageBills" class="form-check-input permission-checkbox" />
|
|
<label asp-for="CanManageBills" class="form-check-label">Can Manage Bills & AP</label>
|
|
<div class="form-text text-muted small">Enter and pay vendor bills, manage purchase orders and accounts payable</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-check">
|
|
<input asp-for="CanManageAccounting" class="form-check-input permission-checkbox" />
|
|
<label asp-for="CanManageAccounting" class="form-check-label">Can Manage Accounting</label>
|
|
<div class="form-text text-muted small">Chart of accounts, bank reconciliations, journal entries</div>
|
|
</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-outline-secondary">Cancel</a>
|
|
}
|
|
else
|
|
{
|
|
<a asp-action="Index" class="btn btn-outline-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");
|
|
|
|
const roleDefaults = {
|
|
'Viewer': ['CanViewCalendar', 'CanViewProducts'],
|
|
'Worker': ['CanManageJobs', 'CanViewCalendar', 'CanViewProducts'],
|
|
'Accountant': ['CanManageInvoices', 'CanViewReports', 'CanManageVendors', 'CanManageBills', 'CanManageAccounting'],
|
|
'Manager': ['CanManageJobs', 'CanManageInventory', 'CanManageCustomers', 'CanCreateQuotes', 'CanApproveQuotes',
|
|
'CanManageCalendar', 'CanViewCalendar', 'CanManageProducts', 'CanViewProducts',
|
|
'CanManageEquipment', 'CanManageVendors', 'CanManageMaintenance', 'CanManageInvoices',
|
|
'CanViewReports', 'CanManageBills']
|
|
};
|
|
|
|
// On page load: only lock CompanyAdmin; preserve saved permission values for other roles.
|
|
function initPermissionLock() {
|
|
if (isSuperAdmin) return;
|
|
const isCompanyAdmin = roleSelect.value === 'CompanyAdmin';
|
|
adminAlert.style.display = isCompanyAdmin ? 'block' : 'none';
|
|
if (isCompanyAdmin) {
|
|
permissionCheckboxes.forEach(cb => { cb.checked = true; cb.disabled = true; });
|
|
}
|
|
}
|
|
|
|
// On role change: apply role defaults so the admin gets a sensible starting point.
|
|
function applyRoleDefaults() {
|
|
const role = roleSelect.value;
|
|
const isCompanyAdmin = role === 'CompanyAdmin';
|
|
adminAlert.style.display = isCompanyAdmin ? 'block' : 'none';
|
|
const defaults = roleDefaults[role] || [];
|
|
permissionCheckboxes.forEach(checkbox => {
|
|
if (isCompanyAdmin) {
|
|
checkbox.checked = true;
|
|
checkbox.disabled = true;
|
|
} else {
|
|
checkbox.disabled = false;
|
|
checkbox.checked = defaults.includes(checkbox.id);
|
|
}
|
|
});
|
|
}
|
|
|
|
initPermissionLock();
|
|
roleSelect.addEventListener('change', applyRoleDefaults);
|
|
});
|
|
</script>
|
|
}
|