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 = $config | ConvertTo-Json -Depth 5
|
||||||
$json | Out-File -FilePath "C:\ci-output\webapi\appsettings.Production.json" -Encoding utf8
|
$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
|
exit 0
|
||||||
|
|
||||||
# ---- Generate production config for WebApps (uses Variables, not Secrets, since BaseUrl is not sensitive) ----
|
# ---- 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") {
|
if (Test-Path "C:\inetpub\cprnims-api") {
|
||||||
robocopy "C:\inetpub\cprnims-api" "C:\backups\$stamp\webapi" /MIR /R:2 /W:3 | Out-Null
|
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") {
|
if (Test-Path "C:\inetpub\cprnims-web") {
|
||||||
robocopy "C:\inetpub\cprnims-web" "C:\backups\$stamp\webapps" /MIR /R:2 /W:3 `
|
robocopy "C:\inetpub\cprnims-web" "C:\backups\$stamp\webapps" /MIR /R:2 /W:3 /XD Images | Out-Null
|
||||||
/XD "C:\inetpub\cprnims-web\wwwroot\Content\Images" | Out-Null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$stamp | Out-File -FilePath "C:\backups\latest.txt" -Encoding ascii -NoNewline
|
$stamp | Out-File -FilePath "C:\backups\latest.txt" -Encoding ascii -NoNewline
|
||||||
@ -142,33 +142,18 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
# Deploy everything EXCEPT the dynamic images folder.
|
# Deploy everything EXCEPT the dynamic images folder.
|
||||||
# /XD excludes the Images directory itself from mirroring (prevents deletion of subfolders).
|
# /XD Images excludes the directory by NAME, which robocopy matches reliably
|
||||||
# We use the destination full path for /XD so only this specific Images folder is protected,
|
# against the source tree it walks. This prevents /MIR from purging the live
|
||||||
# not any other folder named Images elsewhere in the tree.
|
# C:\inetpub\cprnims-web\wwwroot\Content\Images folder where end users upload images.
|
||||||
# Note: files sitting directly inside Images\ are protected because /XD stops robocopy
|
# NOTE: the build output contains no images, so there is nothing to re-seed -
|
||||||
# from treating that directory as part of the mirror scope entirely.
|
# the live Images folder must simply be left untouched.
|
||||||
robocopy "C:\ci-output\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 `
|
robocopy "C:\ci-output\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 /XD Images
|
||||||
/XD "C:\inetpub\cprnims-web\wwwroot\Content\Images"
|
|
||||||
|
|
||||||
$rc = $LASTEXITCODE
|
$rc = $LASTEXITCODE
|
||||||
Write-Host "ROBOCOPY EXIT CODE: $rc"
|
Write-Host "ROBOCOPY EXIT CODE: $rc"
|
||||||
if ($rc -ge 8) {
|
if ($rc -ge 8) {
|
||||||
throw "robocopy failed for WebApps with exit code $rc"
|
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
|
exit 0
|
||||||
|
|
||||||
- name: Start app pools
|
- name: Start app pools
|
||||||
@ -196,7 +181,7 @@ jobs:
|
|||||||
if: failure()
|
if: failure()
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$stamp = Get-Content "C:\backups\latest.txt" -Raw
|
$stamp = (Get-Content "C:\backups\latest.txt" -Raw).Trim()
|
||||||
$backupPath = "C:\backups\$stamp"
|
$backupPath = "C:\backups\$stamp"
|
||||||
Write-Host "Deployment failed - rolling back to backup: $backupPath"
|
Write-Host "Deployment failed - rolling back to backup: $backupPath"
|
||||||
|
|
||||||
@ -208,10 +193,10 @@ jobs:
|
|||||||
if (Test-Path "$backupPath\webapi") {
|
if (Test-Path "$backupPath\webapi") {
|
||||||
robocopy "$backupPath\webapi" "C:\inetpub\cprnims-api" /MIR /R:3 /W:5 | Out-Null
|
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") {
|
if (Test-Path "$backupPath\webapps") {
|
||||||
robocopy "$backupPath\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 `
|
robocopy "$backupPath\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 /XD Images | Out-Null
|
||||||
/XD "C:\inetpub\cprnims-web\wwwroot\Content\Images" | Out-Null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Start-WebAppPool -Name "CPRNIMS-Api"
|
Start-WebAppPool -Name "CPRNIMS-Api"
|
||||||
|
|||||||
@ -118,8 +118,6 @@ namespace CPRNIMS.Domain.Services.Account
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserRights> PutPostUserAccess(AccountDto itemDto)
|
public async Task<UserRights> PutPostUserAccess(AccountDto itemDto)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await _accountDbContext.Database
|
await _accountDbContext.Database
|
||||||
.ExecuteSqlRawAsync("EXEC PutPostUserAccess @ContAccId,@AdminUserId,@UserId,@AccessTypeId,@UserAccessId,@IsActive",
|
.ExecuteSqlRawAsync("EXEC PutPostUserAccess @ContAccId,@AdminUserId,@UserId,@AccessTypeId,@UserAccessId,@IsActive",
|
||||||
@ -132,11 +130,5 @@ namespace CPRNIMS.Domain.Services.Account
|
|||||||
|
|
||||||
return new UserRights();
|
return new UserRights();
|
||||||
}
|
}
|
||||||
catch (SqlException ex)
|
|
||||||
{
|
|
||||||
ex.ToString();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ namespace CPRNIMS.Infrastructure.ViewModel.Account
|
|||||||
public AttachmentVM? Attachment { get; set; }
|
public AttachmentVM? Attachment { get; set; }
|
||||||
public string? ProfilePictureStr { get; set; }
|
public string? ProfilePictureStr { get; set; }
|
||||||
public string? NewPassword { get; set; }
|
public string? NewPassword { get; set; }
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }= string.Empty;
|
||||||
public string PasswordHash { get; set; }
|
public string? PasswordHash { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,7 +112,7 @@ namespace CPRNIMS.WebApi.Controllers.Account
|
|||||||
if (user.AccessFailedCount > 3 || signInResult.IsLockedOut)
|
if (user.AccessFailedCount > 3 || signInResult.IsLockedOut)
|
||||||
{
|
{
|
||||||
await _userManager.SetLockoutEnabledAsync(user, true);
|
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
|
return BadRequest(new ResponseObject
|
||||||
{
|
{
|
||||||
|
|||||||
@ -215,27 +215,10 @@ namespace CPRNIMS.WebApps.Controllers.Account
|
|||||||
}
|
}
|
||||||
public async Task<IActionResult> GetRoles()
|
public async Task<IActionResult> GetRoles()
|
||||||
{
|
{
|
||||||
try
|
var viewModels = new UserRightsVM();
|
||||||
{
|
|
||||||
var response = await _account.GetRoles(GetUser());
|
var response = await _account.GetRoles(GetUser());
|
||||||
|
|
||||||
if (response != null)
|
return GetResponse(response);
|
||||||
{
|
|
||||||
// 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" });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetUserRights(UserRightsVM viewModel)
|
public async Task<IActionResult> GetUserRights(UserRightsVM viewModel)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using CPRNIMS.Domain.UIServices.Updater;
|
using CPRNIMS.Domain.UIServices.Updater;
|
||||||
using CPRNIMS.WebApps.Common;
|
using CPRNIMS.WebApps.Common;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -213,7 +213,7 @@
|
|||||||
"GetInventoryReport": "api/InventoryReports/GetInventoryReport/"
|
"GetInventoryReport": "api/InventoryReports/GetInventoryReport/"
|
||||||
},
|
},
|
||||||
"ImageUploadSettings": {
|
"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);
|
loader = $('#overlay, #loader').css('z-index', 1070);
|
||||||
|
|
||||||
const selectedItems = Object.values(selectedProductsMap);
|
const selectedItems = Object.values(selectedProductsMap);
|
||||||
@ -9,7 +10,7 @@
|
|||||||
|
|
||||||
const userRightsList = selectedItems.map(item => {
|
const userRightsList = selectedItems.map(item => {
|
||||||
return {
|
return {
|
||||||
UserAccessId: item.userAccessId,
|
UserAccessId: isNotExist,
|
||||||
ContAccId: item.contAccId,
|
ContAccId: item.contAccId,
|
||||||
AccessTypeId: item.accessTypeId,
|
AccessTypeId: item.accessTypeId,
|
||||||
IsActive: true,
|
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 = {};
|
let jsonObj = {};
|
||||||
|
|
||||||
function renderAccessDetailbtn(data, row) {
|
function renderAccessDetailbtn(data, row) {
|
||||||
@ -39,144 +10,13 @@ function renderAccessDetailbtn(data, row) {
|
|||||||
|
|
||||||
return buttonsHtml;
|
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) {
|
function showUpdateUserProfile(jsonData) {
|
||||||
jsonObj = jsonData;
|
jsonObj = jsonData;
|
||||||
|
|
||||||
// jsonData.lockoutEnabled is a boolean value (true or false)
|
|
||||||
var lockoutEnabled = jsonData.lockoutEnabled;
|
var lockoutEnabled = jsonData.lockoutEnabled;
|
||||||
|
|
||||||
// Get the checkbox element by its ID
|
|
||||||
var lockoutEnabledCheckbox = document.getElementById("LockoutEnabled");
|
var lockoutEnabledCheckbox = document.getElementById("LockoutEnabled");
|
||||||
|
|
||||||
// Set the checkbox state based on the boolean value
|
|
||||||
lockoutEnabledCheckbox.checked = lockoutEnabled;
|
lockoutEnabledCheckbox.checked = lockoutEnabled;
|
||||||
|
|
||||||
$("#Id").val(jsonData.Id);
|
$("#Id").val(jsonData.Id);
|
||||||
@ -199,29 +39,21 @@ function showUpdateUserProfile(jsonData) {
|
|||||||
$("#UpdatedDate").val(jsonData.updatedDate);
|
$("#UpdatedDate").val(jsonData.updatedDate);
|
||||||
$("#CreatedDate").val(jsonData.createdDate);
|
$("#CreatedDate").val(jsonData.createdDate);
|
||||||
|
|
||||||
// Assuming jsonData.profilePicture contains the base64-encoded image
|
|
||||||
var urlContent = jsonData.url;
|
var urlContent = jsonData.url;
|
||||||
|
|
||||||
// Get the img element by its ID
|
|
||||||
var imgElement = document.getElementById("profilePictureImage");
|
var imgElement = document.getElementById("profilePictureImage");
|
||||||
|
|
||||||
// Check if urlContent is not null, undefined, or an empty string
|
|
||||||
if (urlContent && urlContent.trim() !== "") {
|
if (urlContent && urlContent.trim() !== "") {
|
||||||
// If not null or empty, set the src attribute of the img element to display the image
|
|
||||||
imgElement.src = urlContent;
|
imgElement.src = urlContent;
|
||||||
} else {
|
} else {
|
||||||
// If null or empty, set the src to the default image
|
|
||||||
imgElement.src = "/Content/Images/404userImage.jpg";
|
imgElement.src = "/Content/Images/404userImage.jpg";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the input element and label element
|
|
||||||
var inputFieldUserRole = document.getElementById("PUserRole");
|
var inputFieldUserRole = document.getElementById("PUserRole");
|
||||||
var labelUserRole = document.getElementById("PUserRoleLabel");
|
var labelUserRole = document.getElementById("PUserRoleLabel");
|
||||||
|
|
||||||
// Initialize the label text with the initial input value
|
|
||||||
labelUserRole.textContent = inputFieldUserRole.value;
|
labelUserRole.textContent = inputFieldUserRole.value;
|
||||||
|
|
||||||
// Listen for changes in the input field
|
|
||||||
inputFieldUserRole.addEventListener("input", function () {
|
inputFieldUserRole.addEventListener("input", function () {
|
||||||
labelUserRole.textContent = inputFieldUserRole.value;
|
labelUserRole.textContent = inputFieldUserRole.value;
|
||||||
|
|
||||||
@ -230,10 +62,8 @@ function showUpdateUserProfile(jsonData) {
|
|||||||
var inputField = document.getElementById("PFullName");
|
var inputField = document.getElementById("PFullName");
|
||||||
var label = document.getElementById("PFullNameLabel");
|
var label = document.getElementById("PFullNameLabel");
|
||||||
|
|
||||||
// Initialize the label text with the initial input value
|
|
||||||
label.textContent = inputField.value;
|
label.textContent = inputField.value;
|
||||||
|
|
||||||
// Listen for changes in the input field
|
|
||||||
inputField.addEventListener("input", function () {
|
inputField.addEventListener("input", function () {
|
||||||
label.textContent = inputField.value;
|
label.textContent = inputField.value;
|
||||||
|
|
||||||
@ -247,7 +77,6 @@ function updateUserProfile() {
|
|||||||
var loader = $('#overlay, #loader');
|
var loader = $('#overlay, #loader');
|
||||||
var userId = jsonObj.id;
|
var userId = jsonObj.id;
|
||||||
|
|
||||||
// Get the file input element for the profile picture
|
|
||||||
var profilePictureInput = document.getElementById("profilePictureInput");
|
var profilePictureInput = document.getElementById("profilePictureInput");
|
||||||
|
|
||||||
if (profilePictureInput && profilePictureInput.files.length > 0) {
|
if (profilePictureInput && profilePictureInput.files.length > 0) {
|
||||||
@ -256,21 +85,16 @@ function updateUserProfile() {
|
|||||||
reader.onload = function (event) {
|
reader.onload = function (event) {
|
||||||
var base64Image = event.target.result;
|
var base64Image = event.target.result;
|
||||||
|
|
||||||
// Include the base64Image in your JSON object
|
|
||||||
var data = {
|
var data = {
|
||||||
// Other properties...
|
// Other properties...
|
||||||
ProfilePictureStr: base64Image
|
ProfilePictureStr: base64Image
|
||||||
};
|
};
|
||||||
// Send the AJAX request
|
|
||||||
sendUpdateRequest(data, loader, userId);
|
sendUpdateRequest(data, loader, userId);
|
||||||
};
|
};
|
||||||
// Read the file as data URL
|
|
||||||
reader.readAsDataURL(ProfilePicture);
|
reader.readAsDataURL(ProfilePicture);
|
||||||
} else {
|
} else {
|
||||||
// No file selected, send the request with an empty ProfilePictureStr
|
|
||||||
var data = {
|
var data = {
|
||||||
ProfilePictureStr: ''
|
ProfilePictureStr: ''
|
||||||
// Other properties...
|
|
||||||
};
|
};
|
||||||
sendUpdateRequest(data, loader, userId);
|
sendUpdateRequest(data, loader, userId);
|
||||||
}
|
}
|
||||||
@ -298,30 +122,27 @@ function sendUpdateRequest(data, loader, userId) {
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/Account/UpdateUserProfile',
|
url: '/Account/UpdateUserProfile',
|
||||||
type: 'POST',
|
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) {
|
success: function (response) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
$('#updateUserProfile').modal('hide');
|
$('#updateUserProfile').modal('hide');
|
||||||
|
|
||||||
refreshTable();
|
refreshTable();
|
||||||
// Close the modal after 5 seconds
|
|
||||||
$('#UpdateMessage').modal('show');
|
$('#UpdateMessage').modal('show');
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$('#UpdateMessage').modal('hide');
|
$('#UpdateMessage').modal('hide');
|
||||||
}, 5000); // 5000 milliseconds (5 seconds)
|
}, 5000);
|
||||||
refreshTable();
|
refreshTable();
|
||||||
clearTextModal();
|
clearTextModal();
|
||||||
} else {
|
} else {
|
||||||
// User update was not successful, display the error message
|
|
||||||
alert('User update failed: ' + response.response);
|
alert('User update failed: ' + response.response);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeSend: function () {
|
beforeSend: function () {
|
||||||
// Show the loader before making the AJAX request
|
|
||||||
loader.show();
|
loader.show();
|
||||||
},
|
},
|
||||||
complete: function () {
|
complete: function () {
|
||||||
// Hide the loader after the AJAX request is complete (success or error)
|
|
||||||
loader.hide();
|
loader.hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -363,7 +184,6 @@ function ShowModal() {
|
|||||||
$('#createNewUser').modal('show');
|
$('#createNewUser').modal('show');
|
||||||
clearTextModal();
|
clearTextModal();
|
||||||
}
|
}
|
||||||
// Function to refresh the DataTable
|
|
||||||
function refreshTable() {
|
function refreshTable() {
|
||||||
userListTable.ajax.reload();
|
userListTable.ajax.reload();
|
||||||
}
|
}
|
||||||
@ -373,18 +193,33 @@ function clearTextModal() {
|
|||||||
document.getElementById("userName").value = "";
|
document.getElementById("userName").value = "";
|
||||||
document.getElementById("email").value = "";
|
document.getElementById("email").value = "";
|
||||||
document.getElementById("password").value = "";
|
document.getElementById("password").value = "";
|
||||||
// Reset the select fields to their default options
|
|
||||||
document.getElementById("company").selectedIndex = 0;
|
document.getElementById("company").selectedIndex = 0;
|
||||||
document.getElementById("role").selectedIndex = 0;
|
document.getElementById("role").selectedIndex = 0;
|
||||||
}
|
}
|
||||||
// Function to create a new user
|
|
||||||
function addNewUser() {
|
function addNewUser() {
|
||||||
var loader = $('#overlay, #loader');
|
var loader = $('#overlay, #loader');
|
||||||
var formData = $("#userRegistrationForm").serialize();
|
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({
|
$.ajax({
|
||||||
url: '/Account/CreateAccount',
|
url: '/Account/CreateAccount',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: formData, // Send the form data
|
data: formData,
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
$('#createNewUser').modal('hide');
|
$('#createNewUser').modal('hide');
|
||||||
@ -393,10 +228,10 @@ function addNewUser() {
|
|||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$('#Message').modal('hide');
|
$('#Message').modal('hide');
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
closeModal('modalCreate');
|
||||||
refreshTable();
|
refreshTable();
|
||||||
clearTextModal();
|
clearTextModal();
|
||||||
} else {
|
} else {
|
||||||
// User creation was not successful, display the error message
|
|
||||||
console.log('User creation failed:', response.response);
|
console.log('User creation failed:', response.response);
|
||||||
$('#createNewUser').modal('hide');
|
$('#createNewUser').modal('hide');
|
||||||
alert('User creation failed: ' + response.response);
|
alert('User creation failed: ' + response.response);
|
||||||
@ -404,32 +239,54 @@ function addNewUser() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
beforeSend: function () {
|
beforeSend: function () {
|
||||||
// Show the loader before making the AJAX request
|
|
||||||
loader.show();
|
loader.show();
|
||||||
},
|
},
|
||||||
complete: function () {
|
complete: function () {
|
||||||
// Hide the loader after the AJAX request is complete (success or error)
|
|
||||||
loader.hide();
|
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() {
|
function populateRoles() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/Account/GetRoles", // Update this with your actual controller and action
|
url: "/Account/GetRoles",
|
||||||
success: function (data) {
|
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;
|
var roles = data.data;
|
||||||
|
|
||||||
// Ensure roles is an array before processing
|
|
||||||
if (Array.isArray(roles)) {
|
if (Array.isArray(roles)) {
|
||||||
// Clear existing options in the select element
|
|
||||||
$('#role').empty();
|
$('#role').empty();
|
||||||
|
|
||||||
// Populate the select element with options
|
|
||||||
roles.forEach(function (role) {
|
roles.forEach(function (role) {
|
||||||
$('#role').append($('<option>', {
|
$('#role').append($('<option>', {
|
||||||
value: role,
|
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
|
* Initializes the session timeout manager
|
||||||
*/
|
*/
|
||||||
function initialize() {
|
function initialize() {
|
||||||
console.log('Session timeout initialized:', {
|
//console.log('Session timeout initialized:', {
|
||||||
timeout: CONFIG.timeoutMinutes + ' minutes',
|
// timeout: CONFIG.timeoutMinutes + ' minutes',
|
||||||
warning: CONFIG.warningMinutes + ' minutes before timeout',
|
// warning: CONFIG.warningMinutes + ' minutes before timeout',
|
||||||
checkInterval: CONFIG.checkIntervalSeconds + ' seconds'
|
// checkInterval: CONFIG.checkIntervalSeconds + ' seconds'
|
||||||
});
|
//});
|
||||||
|
|
||||||
// Validate configuration
|
// Validate configuration
|
||||||
if (CONFIG.warningMinutes >= CONFIG.timeoutMinutes) {
|
if (CONFIG.warningMinutes >= CONFIG.timeoutMinutes) {
|
||||||
@ -286,7 +286,7 @@ function handleLogout() {
|
|||||||
clearInterval(checkInterval);
|
clearInterval(checkInterval);
|
||||||
checkInterval = null;
|
checkInterval = null;
|
||||||
}
|
}
|
||||||
console.log('Session timeout manager cleaned up');
|
/* console.log('Session timeout manager cleaned up');*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize when DOM is ready
|
// 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