UploadPath change to new path C:\\inetpub\\cprnims-web\\wwwroot\\Content\\Images
All checks were successful
Build and Deploy CPRNIMS / build-and-deploy (push) Successful in 3m35s
All checks were successful
Build and Deploy CPRNIMS / build-and-deploy (push) Successful in 3m35s
This commit is contained in:
parent
421b2959ec
commit
7cf4852256
@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
$json = $config | ConvertTo-Json -Depth 5
|
||||
$json | Out-File -FilePath "C:\ci-output\webapi\appsettings.Production.json" -Encoding utf8
|
||||
Write-Host "Wrote appsettings.Production.json to webapi output (values masked in this log automatically)"
|
||||
Write-Host "Wrote appsettings.Production.json to webapi output (secret values not echoed)"
|
||||
exit 0
|
||||
|
||||
# ---- Generate production config for WebApps (uses Variables, not Secrets, since BaseUrl is not sensitive) ----
|
||||
@ -100,10 +100,10 @@ jobs:
|
||||
if (Test-Path "C:\inetpub\cprnims-api") {
|
||||
robocopy "C:\inetpub\cprnims-api" "C:\backups\$stamp\webapi" /MIR /R:2 /W:3 | Out-Null
|
||||
}
|
||||
# Exclude dynamic user images from backup - not part of deployable artifact
|
||||
# Exclude dynamic user images by folder NAME so the exclusion always matches
|
||||
# regardless of source/destination path direction.
|
||||
if (Test-Path "C:\inetpub\cprnims-web") {
|
||||
robocopy "C:\inetpub\cprnims-web" "C:\backups\$stamp\webapps" /MIR /R:2 /W:3 `
|
||||
/XD "C:\inetpub\cprnims-web\wwwroot\Content\Images" | Out-Null
|
||||
robocopy "C:\inetpub\cprnims-web" "C:\backups\$stamp\webapps" /MIR /R:2 /W:3 /XD Images | Out-Null
|
||||
}
|
||||
|
||||
$stamp | Out-File -FilePath "C:\backups\latest.txt" -Encoding ascii -NoNewline
|
||||
@ -142,33 +142,18 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Deploy everything EXCEPT the dynamic images folder.
|
||||
# /XD excludes the Images directory itself from mirroring (prevents deletion of subfolders).
|
||||
# We use the destination full path for /XD so only this specific Images folder is protected,
|
||||
# not any other folder named Images elsewhere in the tree.
|
||||
# Note: files sitting directly inside Images\ are protected because /XD stops robocopy
|
||||
# from treating that directory as part of the mirror scope entirely.
|
||||
robocopy "C:\ci-output\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 `
|
||||
/XD "C:\inetpub\cprnims-web\wwwroot\Content\Images"
|
||||
# /XD Images excludes the directory by NAME, which robocopy matches reliably
|
||||
# against the source tree it walks. This prevents /MIR from purging the live
|
||||
# C:\inetpub\cprnims-web\wwwroot\Content\Images folder where end users upload images.
|
||||
# NOTE: the build output contains no images, so there is nothing to re-seed -
|
||||
# the live Images folder must simply be left untouched.
|
||||
robocopy "C:\ci-output\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 /XD Images
|
||||
|
||||
$rc = $LASTEXITCODE
|
||||
Write-Host "ROBOCOPY EXIT CODE: $rc"
|
||||
if ($rc -ge 8) {
|
||||
throw "robocopy failed for WebApps with exit code $rc"
|
||||
}
|
||||
|
||||
# Separately sync only the static seed images that ARE part of the repo
|
||||
# (1.jpg, 2.jpg, 3.jpg, 404userImage.jpg) without /PURGE or /MIR,
|
||||
# so new files from the build are added but existing dynamic uploads are never deleted.
|
||||
if (Test-Path "C:\ci-output\webapps\wwwroot\Content\Images") {
|
||||
robocopy "C:\ci-output\webapps\wwwroot\Content\Images" `
|
||||
"C:\inetpub\cprnims-web\wwwroot\Content\Images" `
|
||||
/E /R:3 /W:5
|
||||
$rc2 = $LASTEXITCODE
|
||||
Write-Host "ROBOCOPY IMAGES EXIT CODE: $rc2"
|
||||
if ($rc2 -ge 8) {
|
||||
throw "robocopy failed for Images folder with exit code $rc2"
|
||||
}
|
||||
}
|
||||
exit 0
|
||||
|
||||
- name: Start app pools
|
||||
@ -196,7 +181,7 @@ jobs:
|
||||
if: failure()
|
||||
shell: pwsh
|
||||
run: |
|
||||
$stamp = Get-Content "C:\backups\latest.txt" -Raw
|
||||
$stamp = (Get-Content "C:\backups\latest.txt" -Raw).Trim()
|
||||
$backupPath = "C:\backups\$stamp"
|
||||
Write-Host "Deployment failed - rolling back to backup: $backupPath"
|
||||
|
||||
@ -208,10 +193,10 @@ jobs:
|
||||
if (Test-Path "$backupPath\webapi") {
|
||||
robocopy "$backupPath\webapi" "C:\inetpub\cprnims-api" /MIR /R:3 /W:5 | Out-Null
|
||||
}
|
||||
# Exclude dynamic images from rollback restore - preserve images uploaded after the backup was taken
|
||||
# Exclude Images by name on rollback too - the backup never contains Images,
|
||||
# so without this /MIR would DELETE the live user-uploaded images.
|
||||
if (Test-Path "$backupPath\webapps") {
|
||||
robocopy "$backupPath\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 `
|
||||
/XD "C:\inetpub\cprnims-web\wwwroot\Content\Images" | Out-Null
|
||||
robocopy "$backupPath\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 /XD Images | Out-Null
|
||||
}
|
||||
|
||||
Start-WebAppPool -Name "CPRNIMS-Api"
|
||||
|
||||
@ -119,24 +119,16 @@ namespace CPRNIMS.Domain.Services.Account
|
||||
|
||||
public async Task<UserRights> PutPostUserAccess(AccountDto itemDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _accountDbContext.Database
|
||||
.ExecuteSqlRawAsync("EXEC PutPostUserAccess @ContAccId,@AdminUserId,@UserId,@AccessTypeId,@UserAccessId,@IsActive",
|
||||
new SqlParameter("@ContAccId", itemDto.ContAccId),
|
||||
new SqlParameter("@AdminUserId", itemDto.AdminUserId),
|
||||
new SqlParameter("@UserId", itemDto.UserId),
|
||||
new SqlParameter("@AccessTypeId", itemDto.AccessTypeId),
|
||||
new SqlParameter("@UserAccessId", itemDto.UserAccessId),
|
||||
new SqlParameter("@IsActive", itemDto.IsActive));
|
||||
await _accountDbContext.Database
|
||||
.ExecuteSqlRawAsync("EXEC PutPostUserAccess @ContAccId,@AdminUserId,@UserId,@AccessTypeId,@UserAccessId,@IsActive",
|
||||
new SqlParameter("@ContAccId", itemDto.ContAccId),
|
||||
new SqlParameter("@AdminUserId", itemDto.AdminUserId),
|
||||
new SqlParameter("@UserId", itemDto.UserId),
|
||||
new SqlParameter("@AccessTypeId", itemDto.AccessTypeId),
|
||||
new SqlParameter("@UserAccessId", itemDto.UserAccessId),
|
||||
new SqlParameter("@IsActive", itemDto.IsActive));
|
||||
|
||||
return new UserRights();
|
||||
}
|
||||
catch (SqlException ex)
|
||||
{
|
||||
ex.ToString();
|
||||
throw;
|
||||
}
|
||||
return new UserRights();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ namespace CPRNIMS.Infrastructure.ViewModel.Account
|
||||
public AttachmentVM? Attachment { get; set; }
|
||||
public string? ProfilePictureStr { get; set; }
|
||||
public string? NewPassword { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string PasswordHash { get; set; }
|
||||
public string UserId { get; set; }= string.Empty;
|
||||
public string? PasswordHash { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ namespace CPRNIMS.WebApi.Controllers.Account
|
||||
if (user.AccessFailedCount > 3 || signInResult.IsLockedOut)
|
||||
{
|
||||
await _userManager.SetLockoutEnabledAsync(user, true);
|
||||
await _userManager.SetLockoutEndDateAsync(user, DateTime.Now.AddMinutes(30));
|
||||
await _userManager.SetLockoutEndDateAsync(user, DateTime.Now.AddHours(2));
|
||||
|
||||
return BadRequest(new ResponseObject
|
||||
{
|
||||
|
||||
@ -215,27 +215,10 @@ namespace CPRNIMS.WebApps.Controllers.Account
|
||||
}
|
||||
public async Task<IActionResult> GetRoles()
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _account.GetRoles(GetUser());
|
||||
var viewModels = new UserRightsVM();
|
||||
var response = await _account.GetRoles(GetUser());
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
// return Json(new { data = response });
|
||||
return new JsonResult(new { data = response });
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("Logout", "Home");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = ex.InnerException?.ToString() ?? ex.Message.ToString();
|
||||
|
||||
return Json(new { data = "No Data" });
|
||||
}
|
||||
return GetResponse(response);
|
||||
}
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetUserRights(UserRightsVM viewModel)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using CPRNIMS.Domain.UIServices.Updater;
|
||||
using CPRNIMS.WebApps.Common;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -213,7 +213,7 @@
|
||||
"GetInventoryReport": "api/InventoryReports/GetInventoryReport/"
|
||||
},
|
||||
"ImageUploadSettings": {
|
||||
"UploadPath": "C:\\WebApps\\wwwroot\\Content\\Images"
|
||||
"UploadPath": "C:\\inetpub\\cprnims-web\\wwwroot\\Content\\Images"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
function postPutAccessRights(IsNotExist) {
|
||||
let isNotExist = false;
|
||||
function postPutAccessRights(IsNotExist) {
|
||||
loader = $('#overlay, #loader').css('z-index', 1070);
|
||||
|
||||
const selectedItems = Object.values(selectedProductsMap);
|
||||
@ -9,7 +10,7 @@
|
||||
|
||||
const userRightsList = selectedItems.map(item => {
|
||||
return {
|
||||
UserAccessId: item.userAccessId,
|
||||
UserAccessId: isNotExist,
|
||||
ContAccId: item.contAccId,
|
||||
AccessTypeId: item.accessTypeId,
|
||||
IsActive: true,
|
||||
|
||||
@ -1,32 +1,3 @@
|
||||
//navigate the picture
|
||||
//document.addEventListener("DOMContentLoaded", function () {
|
||||
// var profilePictureImage = document.getElementById("profilePictureImage");
|
||||
// var profilePictureInput = document.getElementById("profilePictureInput");
|
||||
|
||||
// // Set a default image source for the profile picture
|
||||
// profilePictureImage.src = "wwwroot/Content/Images/404userImage.jpg";
|
||||
|
||||
// // Add a click event listener to the profile picture to trigger the file input
|
||||
// profilePictureImage.addEventListener("click", function () {
|
||||
// profilePictureInput.click();
|
||||
// });
|
||||
|
||||
// // Add a change event listener to the file input
|
||||
// profilePictureInput.addEventListener("change", function () {
|
||||
// var ProfilePicture = profilePictureInput.files[0];
|
||||
|
||||
// if (ProfilePicture) {
|
||||
// // Display the selected image
|
||||
// var imageURL = URL.createObjectURL(ProfilePicture);
|
||||
// profilePictureImage.src = imageURL;
|
||||
// } else {
|
||||
// // No file selected, revert to the default image
|
||||
// console.log("No file selected, using default image");
|
||||
// profilePictureImage.src = "wwwroot/Content/Images/404userImage.jpg";
|
||||
// }
|
||||
// });
|
||||
//});
|
||||
|
||||
let jsonObj = {};
|
||||
|
||||
function renderAccessDetailbtn(data, row) {
|
||||
@ -39,144 +10,13 @@ function renderAccessDetailbtn(data, row) {
|
||||
|
||||
return buttonsHtml;
|
||||
}
|
||||
////function viewUserAccessNotExist() {
|
||||
//// loader = $('#overlay, #loader');
|
||||
//// $('#viewUserAccessNotExist').modal('show');
|
||||
//// $('#viewUserAccessNotExist').css('z-index', 1060);
|
||||
//// tableElement = $('#NotAccessDataTable');
|
||||
//// tableDestroy(tableElement);
|
||||
//// var submitButton = $('#btnNotAccess');
|
||||
//// var totalSelectedLabel = $('#totalSelNotAccess');
|
||||
//// let IsNotExist = true;
|
||||
//// notAccessDataTable = tableElement.DataTable({
|
||||
//// ajax: $.extend({
|
||||
//// url: '/Account/GetUserRights',
|
||||
//// type: 'POST',
|
||||
//// data: { UserId, IsNotExist },
|
||||
//// }, beforeComplete(loader)),
|
||||
//// language: {
|
||||
//// emptyTable: "No record available"
|
||||
//// },
|
||||
//// initComplete: function () {
|
||||
//// var api = this.api();
|
||||
//// var data = api.ajax.json();
|
||||
//// if (!data || !data.data || data.data === "No Data") {
|
||||
//// $('.dataTables_empty').html("No record available");
|
||||
//// }
|
||||
//// updateSubmitBtnVisib();
|
||||
//// updateSelectedCount();
|
||||
//// },
|
||||
//// columns: colAccountDetailNot,
|
||||
//// responsive: true,
|
||||
//// error: errorHandler
|
||||
//// });
|
||||
//// function updateSubmitBtnVisib() {
|
||||
//// var isEmpty = notAccessDataTable.data().length === 0;
|
||||
//// submitButton.toggle(!isEmpty);
|
||||
//// }
|
||||
//// $('#NotAccessDataTable').on('change', '.select-NotAccess-checkbox', function () {
|
||||
//// var row = $(this).closest('tr');
|
||||
//// if ($(this).prop('checked')) {
|
||||
//// row.addClass('selected-row');
|
||||
//// } else {
|
||||
//// row.removeClass('selected-row');
|
||||
//// }
|
||||
//// updateSelectedCount();
|
||||
//// });
|
||||
|
||||
//// $('#selectAllCheckboxNotAccess').on('change', function () {
|
||||
//// var isChecked = $(this).prop('checked');
|
||||
//// $('.select-NotAccess-checkbox').prop('checked', isChecked);
|
||||
//// if (isChecked) {
|
||||
//// $('#NotAccessDataTable tbody tr').addClass('selected-row');
|
||||
//// } else {
|
||||
//// $('#NotAccessDataTable tbody tr').removeClass('selected-row');
|
||||
//// }
|
||||
//// updateSelectedCount();
|
||||
//// });
|
||||
//// function updateSelectedCount() {
|
||||
//// var totalSelected = $('.select-NotAccess-checkbox:checked').length;
|
||||
//// totalSelectedLabel.text(totalSelected);
|
||||
//// }
|
||||
////}
|
||||
////function viewUserAccess(data) {
|
||||
//// loader = $('#overlay, #loader');
|
||||
//// $('#viewUserAccess').modal('show');
|
||||
//// $('#viewUserAccess').css('z-index', 1050);
|
||||
//// tableElement = $('#AccessDataTable');
|
||||
//// tableDestroy(tableElement);
|
||||
//// var submitButton = $('#btnAccess');
|
||||
//// var totalSelectedLabel = $('#totalSelAccess');
|
||||
//// UserId = data.id;
|
||||
//// //CanvassId = data.canvassId;
|
||||
//// $('#ua-EmailAddress').val(data.email);
|
||||
//// $('#ua-FullName').val(data.fullName);
|
||||
//// $('#uan-EmailAddress').val(data.email);
|
||||
//// $('#uan-FullName').val(data.fullName);
|
||||
//// accessDataTable = tableElement.DataTable({
|
||||
//// ajax: $.extend({
|
||||
//// url: '/Account/GetUserRights',
|
||||
//// type: 'POST',
|
||||
//// data: { UserId },
|
||||
//// }, beforeComplete(loader)),
|
||||
//// language: {
|
||||
//// emptyTable: "No record available"
|
||||
//// },
|
||||
//// initComplete: function () {
|
||||
//// var api = this.api();
|
||||
//// var data = api.ajax.json();
|
||||
//// if (!data || !data.data || data.data === "No Data") {
|
||||
//// $('.dataTables_empty').html("No record available");
|
||||
//// }
|
||||
//// updateSubmitBtnVisib();
|
||||
//// updateSelectedCount();
|
||||
//// },
|
||||
//// columns: colAccountDetail,
|
||||
//// responsive: true,
|
||||
//// error: errorHandler
|
||||
//// });
|
||||
//// function updateSubmitBtnVisib() {
|
||||
//// var isEmpty = accessDataTable.data().length === 0;
|
||||
//// submitButton.toggle(!isEmpty);
|
||||
//// }
|
||||
//// $('#AccessDataTable').on('change', '.select-Access-checkbox', function () {
|
||||
//// var row = $(this).closest('tr');
|
||||
//// if ($(this).prop('checked')) {
|
||||
//// row.addClass('selected-row');
|
||||
//// } else {
|
||||
//// row.removeClass('selected-row');
|
||||
//// }
|
||||
//// updateSelectedCount();
|
||||
//// });
|
||||
|
||||
//// $('#selectAllCheckboxAccess').on('change', function () {
|
||||
//// var isChecked = $(this).prop('checked');
|
||||
//// $('.select-Access-checkbox').prop('checked', isChecked);
|
||||
//// if (isChecked) {
|
||||
//// $('#AccessDataTable tbody tr').addClass('selected-row');
|
||||
//// } else {
|
||||
//// $('#AccessDataTable tbody tr').removeClass('selected-row');
|
||||
//// }
|
||||
//// updateSelectedCount();
|
||||
//// });
|
||||
//// function updateSelectedCount() {
|
||||
//// var totalSelected = $('.select-Access-checkbox:checked').length;
|
||||
//// totalSelectedLabel.text(totalSelected);
|
||||
//// }
|
||||
////}
|
||||
////function ShowNewAccess() {
|
||||
//// viewUserAccessNotExist();
|
||||
////}
|
||||
function showUpdateUserProfile(jsonData) {
|
||||
jsonObj = jsonData;
|
||||
|
||||
// jsonData.lockoutEnabled is a boolean value (true or false)
|
||||
var lockoutEnabled = jsonData.lockoutEnabled;
|
||||
|
||||
// Get the checkbox element by its ID
|
||||
var lockoutEnabledCheckbox = document.getElementById("LockoutEnabled");
|
||||
|
||||
// Set the checkbox state based on the boolean value
|
||||
lockoutEnabledCheckbox.checked = lockoutEnabled;
|
||||
|
||||
$("#Id").val(jsonData.Id);
|
||||
@ -199,29 +39,21 @@ function showUpdateUserProfile(jsonData) {
|
||||
$("#UpdatedDate").val(jsonData.updatedDate);
|
||||
$("#CreatedDate").val(jsonData.createdDate);
|
||||
|
||||
// Assuming jsonData.profilePicture contains the base64-encoded image
|
||||
var urlContent = jsonData.url;
|
||||
|
||||
// Get the img element by its ID
|
||||
var imgElement = document.getElementById("profilePictureImage");
|
||||
|
||||
// Check if urlContent is not null, undefined, or an empty string
|
||||
if (urlContent && urlContent.trim() !== "") {
|
||||
// If not null or empty, set the src attribute of the img element to display the image
|
||||
imgElement.src = urlContent;
|
||||
} else {
|
||||
// If null or empty, set the src to the default image
|
||||
imgElement.src = "/Content/Images/404userImage.jpg";
|
||||
}
|
||||
|
||||
// Get the input element and label element
|
||||
var inputFieldUserRole = document.getElementById("PUserRole");
|
||||
var labelUserRole = document.getElementById("PUserRoleLabel");
|
||||
|
||||
// Initialize the label text with the initial input value
|
||||
labelUserRole.textContent = inputFieldUserRole.value;
|
||||
|
||||
// Listen for changes in the input field
|
||||
inputFieldUserRole.addEventListener("input", function () {
|
||||
labelUserRole.textContent = inputFieldUserRole.value;
|
||||
|
||||
@ -230,10 +62,8 @@ function showUpdateUserProfile(jsonData) {
|
||||
var inputField = document.getElementById("PFullName");
|
||||
var label = document.getElementById("PFullNameLabel");
|
||||
|
||||
// Initialize the label text with the initial input value
|
||||
label.textContent = inputField.value;
|
||||
|
||||
// Listen for changes in the input field
|
||||
inputField.addEventListener("input", function () {
|
||||
label.textContent = inputField.value;
|
||||
|
||||
@ -247,7 +77,6 @@ function updateUserProfile() {
|
||||
var loader = $('#overlay, #loader');
|
||||
var userId = jsonObj.id;
|
||||
|
||||
// Get the file input element for the profile picture
|
||||
var profilePictureInput = document.getElementById("profilePictureInput");
|
||||
|
||||
if (profilePictureInput && profilePictureInput.files.length > 0) {
|
||||
@ -256,21 +85,16 @@ function updateUserProfile() {
|
||||
reader.onload = function (event) {
|
||||
var base64Image = event.target.result;
|
||||
|
||||
// Include the base64Image in your JSON object
|
||||
var data = {
|
||||
// Other properties...
|
||||
ProfilePictureStr: base64Image
|
||||
};
|
||||
// Send the AJAX request
|
||||
sendUpdateRequest(data, loader, userId);
|
||||
};
|
||||
// Read the file as data URL
|
||||
reader.readAsDataURL(ProfilePicture);
|
||||
} else {
|
||||
// No file selected, send the request with an empty ProfilePictureStr
|
||||
var data = {
|
||||
ProfilePictureStr: ''
|
||||
// Other properties...
|
||||
};
|
||||
sendUpdateRequest(data, loader, userId);
|
||||
}
|
||||
@ -298,30 +122,27 @@ function sendUpdateRequest(data, loader, userId) {
|
||||
$.ajax({
|
||||
url: '/Account/UpdateUserProfile',
|
||||
type: 'POST',
|
||||
data: { ProfilePictureStr, userId, UserName, FullName, Company, Role, Email, PhoneNumber, NewPassword, LockoutEnabled, Address }, // Send form data including the profile picture
|
||||
data: { ProfilePictureStr, userId, UserName, FullName, Company, Role, Email, PhoneNumber, NewPassword, LockoutEnabled, Address },
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
$('#updateUserProfile').modal('hide');
|
||||
|
||||
refreshTable();
|
||||
// Close the modal after 5 seconds
|
||||
|
||||
$('#UpdateMessage').modal('show');
|
||||
setTimeout(function () {
|
||||
$('#UpdateMessage').modal('hide');
|
||||
}, 5000); // 5000 milliseconds (5 seconds)
|
||||
}, 5000);
|
||||
refreshTable();
|
||||
clearTextModal();
|
||||
} else {
|
||||
// User update was not successful, display the error message
|
||||
alert('User update failed: ' + response.response);
|
||||
}
|
||||
},
|
||||
beforeSend: function () {
|
||||
// Show the loader before making the AJAX request
|
||||
loader.show();
|
||||
},
|
||||
complete: function () {
|
||||
// Hide the loader after the AJAX request is complete (success or error)
|
||||
loader.hide();
|
||||
}
|
||||
});
|
||||
@ -363,7 +184,6 @@ function ShowModal() {
|
||||
$('#createNewUser').modal('show');
|
||||
clearTextModal();
|
||||
}
|
||||
// Function to refresh the DataTable
|
||||
function refreshTable() {
|
||||
userListTable.ajax.reload();
|
||||
}
|
||||
@ -373,63 +193,100 @@ function clearTextModal() {
|
||||
document.getElementById("userName").value = "";
|
||||
document.getElementById("email").value = "";
|
||||
document.getElementById("password").value = "";
|
||||
// Reset the select fields to their default options
|
||||
document.getElementById("company").selectedIndex = 0;
|
||||
document.getElementById("role").selectedIndex = 0;
|
||||
}
|
||||
// Function to create a new user
|
||||
|
||||
function addNewUser() {
|
||||
var loader = $('#overlay, #loader');
|
||||
var formData = $("#userRegistrationForm").serialize();
|
||||
$.ajax({
|
||||
url: '/Account/CreateAccount',
|
||||
type: 'POST',
|
||||
data: formData, // Send the form data
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
$('#createNewUser').modal('hide');
|
||||
$('#Message').modal('show');
|
||||
// Close the modal after 5 seconds
|
||||
setTimeout(function () {
|
||||
$('#Message').modal('hide');
|
||||
}, 2000);
|
||||
refreshTable();
|
||||
clearTextModal();
|
||||
} else {
|
||||
// User creation was not successful, display the error message
|
||||
console.log('User creation failed:', response.response);
|
||||
$('#createNewUser').modal('hide');
|
||||
alert('User creation failed: ' + response.response);
|
||||
}
|
||||
},
|
||||
var formData = {
|
||||
FullName: $('#fullName').val(),
|
||||
UserName: $('#userName').val(),
|
||||
Company: $('#company').val(),
|
||||
DepartmentId: parseInt($('#department').val(), 10),
|
||||
Role: $('#role').val(),
|
||||
Email: $('#email').val(),
|
||||
Password: $('#password').val()
|
||||
};
|
||||
showConfirmation({
|
||||
title: 'User Registration',
|
||||
message: 'Are you sure you want to create? This action cannot be undone.',
|
||||
type: 'warning',
|
||||
confirmText: 'Yes',
|
||||
cancelText: 'No'
|
||||
}).then((confirmed) => {
|
||||
if (confirmed) {
|
||||
$.ajax({
|
||||
url: '/Account/CreateAccount',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
$('#createNewUser').modal('hide');
|
||||
$('#Message').modal('show');
|
||||
// Close the modal after 5 seconds
|
||||
setTimeout(function () {
|
||||
$('#Message').modal('hide');
|
||||
}, 2000);
|
||||
closeModal('modalCreate');
|
||||
refreshTable();
|
||||
clearTextModal();
|
||||
} else {
|
||||
console.log('User creation failed:', response.response);
|
||||
$('#createNewUser').modal('hide');
|
||||
alert('User creation failed: ' + response.response);
|
||||
}
|
||||
},
|
||||
|
||||
beforeSend: function () {
|
||||
// Show the loader before making the AJAX request
|
||||
loader.show();
|
||||
},
|
||||
complete: function () {
|
||||
// Hide the loader after the AJAX request is complete (success or error)
|
||||
loader.hide();
|
||||
beforeSend: function () {
|
||||
loader.show();
|
||||
},
|
||||
complete: function () {
|
||||
loader.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Use this function to populate roles
|
||||
//Population
|
||||
function populateDepartment() {
|
||||
|
||||
$.ajax({
|
||||
url: "/Account/GetDepartment",
|
||||
success: function (response) {
|
||||
|
||||
if (response && response.data && Array.isArray(response.data)) {
|
||||
var $department = $('#department');
|
||||
$department.empty();
|
||||
response.data.forEach(function (item) {
|
||||
$department.append(
|
||||
$('<option>')
|
||||
.val(item.departmentId)
|
||||
.text(item.department)
|
||||
);
|
||||
});
|
||||
}
|
||||
else {
|
||||
console.error("Invalid response:", response);
|
||||
}
|
||||
},
|
||||
|
||||
error: function (xhr, status, error) {
|
||||
console.error("AJAX ERROR");
|
||||
console.log(xhr.status);
|
||||
console.log(xhr.responseText);
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
function populateRoles() {
|
||||
$.ajax({
|
||||
url: "/Account/GetRoles", // Update this with your actual controller and action
|
||||
url: "/Account/GetRoles",
|
||||
success: function (data) {
|
||||
//console.log('Received data:', data);
|
||||
|
||||
// Assuming data is an object with a property 'data' that contains the array of roles
|
||||
var roles = data.data;
|
||||
|
||||
// Ensure roles is an array before processing
|
||||
if (Array.isArray(roles)) {
|
||||
// Clear existing options in the select element
|
||||
$('#role').empty();
|
||||
|
||||
// Populate the select element with options
|
||||
roles.forEach(function (role) {
|
||||
$('#role').append($('<option>', {
|
||||
value: role,
|
||||
@ -443,25 +300,4 @@ function populateRoles() {
|
||||
}
|
||||
});
|
||||
}
|
||||
//$(document).ready(function () {
|
||||
// loader = $('#overlay, #loader');
|
||||
// //UserRights = document.getElementById("roleRights").value;
|
||||
// populateRoles();
|
||||
// userListTable = $('#UserListTable').DataTable({
|
||||
// ajax: $.extend({
|
||||
// url: '/Account/GetAllUsers',
|
||||
// type: 'GET',
|
||||
// }, beforeComplete(loader)),
|
||||
// initComplete: initCompleteCallback,
|
||||
// columns: colOnUserList,
|
||||
// columnDefs: colOnColDef,
|
||||
// language: {
|
||||
// emptyTable: "No record available"
|
||||
// },
|
||||
// rowCallback: rowAccountCallback,
|
||||
// responsive: true,
|
||||
// error: errorHandler
|
||||
// });
|
||||
//})
|
||||
|
||||
|
||||
|
||||
@ -229,11 +229,11 @@ function handleLogout() {
|
||||
* Initializes the session timeout manager
|
||||
*/
|
||||
function initialize() {
|
||||
console.log('Session timeout initialized:', {
|
||||
timeout: CONFIG.timeoutMinutes + ' minutes',
|
||||
warning: CONFIG.warningMinutes + ' minutes before timeout',
|
||||
checkInterval: CONFIG.checkIntervalSeconds + ' seconds'
|
||||
});
|
||||
//console.log('Session timeout initialized:', {
|
||||
// timeout: CONFIG.timeoutMinutes + ' minutes',
|
||||
// warning: CONFIG.warningMinutes + ' minutes before timeout',
|
||||
// checkInterval: CONFIG.checkIntervalSeconds + ' seconds'
|
||||
//});
|
||||
|
||||
// Validate configuration
|
||||
if (CONFIG.warningMinutes >= CONFIG.timeoutMinutes) {
|
||||
@ -286,7 +286,7 @@ function handleLogout() {
|
||||
clearInterval(checkInterval);
|
||||
checkInterval = null;
|
||||
}
|
||||
console.log('Session timeout manager cleaned up');
|
||||
/* console.log('Session timeout manager cleaned up');*/
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
|
||||
817
CPRNIMS.WebApps/wwwroot/css/Account/UserAccount.css
Normal file
817
CPRNIMS.WebApps/wwwroot/css/Account/UserAccount.css
Normal file
@ -0,0 +1,817 @@
|
||||
: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;
|
||||
}
|
||||
|
||||
.modal-box.shake {
|
||||
animation: modalShake .35s cubic-bezier(.36,.07,.19,.97);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@keyframes modalShake {
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: translateX(-8px);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateX(8px);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translateX(-6px);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: translateX(6px);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user