diff --git a/0_Framework/Application/SecretKeys.cs b/0_Framework/Application/SecretKeys.cs new file mode 100644 index 00000000..02a9934d --- /dev/null +++ b/0_Framework/Application/SecretKeys.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace _0_Framework.Application; + +public static class SecretKeys +{ + + public static string ProgramManagerInternalApi => "JOb09$Ic3NJd0siLCJtYWMiOiI2%dmODJmNDV"; +} \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/Account/CreateAccount.cs b/AccountManagement.Application.Contracts/Account/CreateAccount.cs index da243966..87c9e150 100644 --- a/AccountManagement.Application.Contracts/Account/CreateAccount.cs +++ b/AccountManagement.Application.Contracts/Account/CreateAccount.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using AccountManagement.Application.Contracts.Role; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Rendering; namespace AccountManagement.Application.Contracts.Account; @@ -35,4 +36,20 @@ public class CreateAccount public string Email { get; set; } public string VerifyCode { get; set; } public string IsActiveString { get; set; } + + /// + /// آیا کاربر در پروگرام منیجر فعالیت مبکند؟ + /// + public bool IsProgramManagerUser { get; set; } + /// + /// لیست نقش های پروگرام منیجر + /// + public List UserRoles { get; set; } + + /// + /// لیست نقشهای موجود در پروگرام منیجر + /// + public SelectList RoleList { get; set; } + + } \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/Account/EditAccount.cs b/AccountManagement.Application.Contracts/Account/EditAccount.cs index b322c34b..41afe474 100644 --- a/AccountManagement.Application.Contracts/Account/EditAccount.cs +++ b/AccountManagement.Application.Contracts/Account/EditAccount.cs @@ -1,6 +1,9 @@ -namespace AccountManagement.Application.Contracts.Account; +using System.Collections.Generic; + +namespace AccountManagement.Application.Contracts.Account; public class EditAccount : CreateAccount { public long Id { get; set; } + } \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/ProgramManagerApiResult/ApiResponse.cs b/AccountManagement.Application.Contracts/ProgramManagerApiResult/ApiResponse.cs new file mode 100644 index 00000000..b5c7afc4 --- /dev/null +++ b/AccountManagement.Application.Contracts/ProgramManagerApiResult/ApiResponse.cs @@ -0,0 +1,11 @@ +namespace AccountManagement.Application.Contracts.ProgramManagerApiResult; + +public record ApiResponse +{ + public bool isSuccess { get; set; } + public string errorMessage { get; set; } + + + + public ErrorType ErrorType { get; set; } +} \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/ProgramManagerApiResult/CreateProgramManagerRole.cs b/AccountManagement.Application.Contracts/ProgramManagerApiResult/CreateProgramManagerRole.cs new file mode 100644 index 00000000..4e4785f8 --- /dev/null +++ b/AccountManagement.Application.Contracts/ProgramManagerApiResult/CreateProgramManagerRole.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace AccountManagement.Application.Contracts.ProgramManagerApiResult; + + +public record CreateProgramManagerRole +{ + /// + /// نام نقش + /// + public string RoleName { get; set; } + + /// + /// کدهای دسترسی + /// + public List Permissions { get; set; } + + /// + /// آی دی اکانت گزارشگیر + /// + public long? GozareshgirRoleId { get; set; } +}; \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/ProgramManagerApiResult/CreateProgramManagerUser.cs b/AccountManagement.Application.Contracts/ProgramManagerApiResult/CreateProgramManagerUser.cs new file mode 100644 index 00000000..64b3dfc6 --- /dev/null +++ b/AccountManagement.Application.Contracts/ProgramManagerApiResult/CreateProgramManagerUser.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +namespace AccountManagement.Application.Contracts.ProgramManagerApiResult; + +public record CreateProgramManagerUser(string FullName, string UserName, string Password, string Mobile, string Email, long? AccountId, List Roles); + +public record EditUserCommand(string FullName, string UserName, string Mobile, long AccountId, List Roles, bool IsActive); \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/ProgramManagerApiResult/ErrorType.cs b/AccountManagement.Application.Contracts/ProgramManagerApiResult/ErrorType.cs new file mode 100644 index 00000000..f8b62acf --- /dev/null +++ b/AccountManagement.Application.Contracts/ProgramManagerApiResult/ErrorType.cs @@ -0,0 +1,11 @@ +namespace AccountManagement.Application.Contracts.ProgramManagerApiResult; + +public enum ErrorType +{ + None, + BadRequest, + NotFound, + Unauthorized, + Validation, + InternalServerError +} \ No newline at end of file diff --git a/AccountManagement.Application.Contracts/ProgramManagerApiResult/RoleResponse.cs b/AccountManagement.Application.Contracts/ProgramManagerApiResult/RoleResponse.cs new file mode 100644 index 00000000..2c3f48d0 --- /dev/null +++ b/AccountManagement.Application.Contracts/ProgramManagerApiResult/RoleResponse.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace AccountManagement.Application.Contracts.ProgramManagerApiResult; + +public class RoleResponse +{ + public bool isSuccess { get; set; } + public RolesData data { get; set; } +} + +public class RolesData +{ + public List role { get; set; } +} + +public class RoleList +{ + public int id { get; set; } + public string roleName { get; set; } + public int gozareshgirRoleId { get; set; } + public List permissions { get; set; } +} diff --git a/AccountManagement.Application.Contracts/ProgramManagerApiResult/SingleUserResponseResult.cs b/AccountManagement.Application.Contracts/ProgramManagerApiResult/SingleUserResponseResult.cs new file mode 100644 index 00000000..00a28e67 --- /dev/null +++ b/AccountManagement.Application.Contracts/ProgramManagerApiResult/SingleUserResponseResult.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace AccountManagement.Application.Contracts.ProgramManagerApiResult; + +public record SingleUserResponseResult +{ + public bool isSuccess { get; set; } + public SingleUserData Data { get; set; } +}; + +public record SingleUserData + +{ + public long id { get; set; } + /// + /// نام و نام خانوادگی + /// + public string fullName { get; set; } + + /// + /// نام کاربری + /// + public string userName { get; set; } + + + + /// + /// مسیر عکس پروفایل + /// + public string profilePhotoPath { get; set; } + + /// + /// شماره موبایل + /// + public string mobile { get; set; } + + + /// + /// فعال/غیر فعال بودن یوزر + /// + public bool isActive { get; set; } + + /// + /// گذرواژه + /// + public string password { get; set; } + + /// + /// ای دی اکانت کاربر در گزارشگیر + /// + public long? accountId { get; set; } + + public List Roles { get; set; } +} diff --git a/AccountManagement.Application.Contracts/Role/CreateRole.cs b/AccountManagement.Application.Contracts/Role/CreateRole.cs index 9231b0df..c82cec2e 100644 --- a/AccountManagement.Application.Contracts/Role/CreateRole.cs +++ b/AccountManagement.Application.Contracts/Role/CreateRole.cs @@ -9,6 +9,10 @@ namespace AccountManagement.Application.Contracts.Role [Required(ErrorMessage = ValidationMessages.IsRequired)] public string Name { get; set; } public List Permissions { get; set; } + /// + /// لیست پرمیشن های پروگرام منیجر + /// + public List PmPermissions { get; set; } } } diff --git a/AccountManagement.Application/AccountApplication.cs b/AccountManagement.Application/AccountApplication.cs index 74880b13..42185fe5 100644 --- a/AccountManagement.Application/AccountApplication.cs +++ b/AccountManagement.Application/AccountApplication.cs @@ -5,6 +5,7 @@ using AccountManagement.Application.Contracts.Account; using AccountManagement.Domain.AccountAgg; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -18,12 +19,18 @@ using Microsoft.AspNetCore.Mvc.Rendering; using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; using Company.Domain.WorkshopAgg; using System.Security.Claims; +using System.Text; using _0_Framework.Exceptions; using AccountManagement.Domain.PositionAgg; using AccountManagement.Domain.SubAccountAgg; using AccountManagement.Domain.SubAccountPermissionSubtitle1Agg; using AccountManagement.Domain.SubAccountRoleAgg; using Company.Domain.WorkshopSubAccountAgg; +using Newtonsoft.Json; +using Microsoft.EntityFrameworkCore; +using Company.Domain._common; +using AccountManagement.Domain.InternalApiCaller; +using AccountManagement.Application.Contracts.ProgramManagerApiResult; //using AccountManagement.Domain.RoleAgg; @@ -39,15 +46,16 @@ public class AccountApplication : IAccountApplication private readonly ISmsService _smsService; private readonly ICameraAccountRepository _cameraAccountRepository; private readonly IPositionRepository _positionRepository; - private readonly IAccountLeftworkRepository _accountLeftworkRepository; + private readonly IAccountLeftworkRepository _accountLeftworkRepository; private readonly IWorkshopRepository _workshopRepository; private readonly ISubAccountRepository _subAccountRepository; private readonly ISubAccountRoleRepository _subAccountRoleRepository; private readonly IWorkshopSubAccountRepository _workshopSubAccountRepository; private readonly ISubAccountPermissionSubtitle1Repository _accountPermissionSubtitle1Repository; + private readonly IUnitOfWork _unitOfWork; - public AccountApplication(IAccountRepository accountRepository, IPasswordHasher passwordHasher, - IFileUploader fileUploader, IAuthHelper authHelper, IRoleRepository roleRepository, IWorker worker, ISmsService smsService, ICameraAccountRepository cameraAccountRepository, IPositionRepository positionRepository, IAccountLeftworkRepository accountLeftworkRepository, IWorkshopRepository workshopRepository, ISubAccountRepository subAccountRepository, ISubAccountRoleRepository subAccountRoleRepository, IWorkshopSubAccountRepository workshopSubAccountRepository, ISubAccountPermissionSubtitle1Repository accountPermissionSubtitle1Repository) + public AccountApplication(IAccountRepository accountRepository, IPasswordHasher passwordHasher, + IFileUploader fileUploader, IAuthHelper authHelper, IRoleRepository roleRepository, IWorker worker, ISmsService smsService, ICameraAccountRepository cameraAccountRepository, IPositionRepository positionRepository, IAccountLeftworkRepository accountLeftworkRepository, IWorkshopRepository workshopRepository, ISubAccountRepository subAccountRepository, ISubAccountRoleRepository subAccountRoleRepository, IWorkshopSubAccountRepository workshopSubAccountRepository, ISubAccountPermissionSubtitle1Repository accountPermissionSubtitle1Repository, IUnitOfWork unitOfWork) { _authHelper = authHelper; _roleRepository = roleRepository; @@ -60,10 +68,11 @@ public class AccountApplication : IAccountApplication _subAccountRoleRepository = subAccountRoleRepository; _workshopSubAccountRepository = workshopSubAccountRepository; _accountPermissionSubtitle1Repository = accountPermissionSubtitle1Repository; + _unitOfWork = unitOfWork; _fileUploader = fileUploader; _passwordHasher = passwordHasher; _accountRepository = accountRepository; - + } public OperationResult EditClient(EditClientAccount command) @@ -92,7 +101,7 @@ public class AccountApplication : IAccountApplication var path = $"profilePhotos"; var picturePath = _fileUploader.Upload(command.ProfilePhoto, path); - editAccount.EditClient(command.Fullname,command.Username,command.Mobile,picturePath,command.Email,command.NationalCode); + editAccount.EditClient(command.Fullname, command.Username, command.Mobile, picturePath, command.Email, command.NationalCode); _accountRepository.SaveChanges(); return opreation.Succcedded(); } @@ -133,15 +142,56 @@ public class AccountApplication : IAccountApplication var password = _passwordHasher.Hash(command.Password); var roleName = _roleRepository.GetDetails(command.RoleId); var path = $"profilePhotos"; + var picturePath = ""; if (_fileUploader != null) { - var picturePath = _fileUploader.Upload(command.ProfilePhoto, path); - var account = new Account(command.Fullname, command.Username, password, command.Mobile, command.RoleId, - picturePath, roleName.Name,"true","false"); - _accountRepository.Create(account); + picturePath = _fileUploader.Upload(command.ProfilePhoto, path); + + } + var account = new Account(command.Fullname, command.Username, password, command.Mobile, command.RoleId, + picturePath, roleName.Name, "true", "false"); + + _unitOfWork.BeginAccountContext(); + + + _accountRepository.Create(account); + _accountRepository.SaveChanges(); + + if (command.IsProgramManagerUser) + { + var parameters = new CreateProgramManagerUser( + command.Fullname, + command.Username, + password, + command.Mobile, + command.Email, + account.id, + command.UserRoles + ); + + var url = "api/user/create"; + var key = SecretKeys.ProgramManagerInternalApi; + + var response = InternalApiCaller.PostAsync( + url, + key, + parameters + ); + + if (!response.Success) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Error); + } + + if (!response.Result.isSuccess) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Result.errorMessage); + } } - _accountRepository.SaveChanges(); + _unitOfWork.CommitAccountContext(); return operation.Succcedded(); } @@ -155,8 +205,8 @@ public class AccountApplication : IAccountApplication return opreation.Failed("پر کردن تمامی فیلدها الزامی است"); if (_accountRepository.Exists(x => x.Username == command.Username)) return opreation.Failed("نام کاربری تکراری است"); - if (_accountRepository.Exists(x => x.Mobile == command.Mobile && x.IsActiveString =="true")) - + if (_accountRepository.Exists(x => x.Mobile == command.Mobile && x.IsActiveString == "true")) + return opreation.Failed("مقادیر وارد شده تکراری است"); //var nationalCodeValidation = command.NationalCode.NationalCodeValid(); @@ -173,11 +223,11 @@ public class AccountApplication : IAccountApplication // break; //} var password = _passwordHasher.Hash(command.Password); - var register =new Account(command.Fullname,command.Username, password, command.Mobile, command.NationalCode); + var register = new Account(command.Fullname, command.Username, password, command.Mobile, command.NationalCode); _accountRepository.Create(register); _accountRepository.SaveChanges(); - return opreation.Succcedded(register.id,message: "ثبت نام شما با موفقیت انجام شد"); + return opreation.Succcedded(register.id, message: "ثبت نام شما با موفقیت انجام شد"); } public OperationResult Edit(EditAccount command) @@ -194,8 +244,97 @@ public class AccountApplication : IAccountApplication var roleName = _roleRepository.GetDetails(command.RoleId); var path = $"profilePhotos"; var picturePath = _fileUploader.Upload(command.ProfilePhoto, path); + _unitOfWork.BeginAccountContext(); account.Edit(command.Fullname, command.Username, command.Mobile, command.RoleId, picturePath, roleName.Name); _accountRepository.SaveChanges(); + var key = SecretKeys.ProgramManagerInternalApi; + + var apiResult = InternalApiCaller.GetAsync( + $"api/user/{account.id}", + key + ); + + + + + //اگر کاربر در پروگرام منیجر قبلا ایجاد شده + if (apiResult.Success && apiResult.Result.Data.accountId == account.id) + { + if (!command.UserRoles.Any()) + return operation.Failed("حداقل یک نقش باید انتخاب شود"); + + var parameters = new EditUserCommand( + command.Fullname, + command.Username, + command.Mobile, + account.id, + command.UserRoles, + command.IsProgramManagerUser + ); + var url = "api/user/edit"; + var response = InternalApiCaller.PostAsync( + url, + key, + parameters + ); + + if (!response.Success) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Error); + + } + + if (!response.Result.isSuccess) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Error); + } + + } + else //اگر کاربر قبلا ایجاد نشده + { + //اگر تیک فعالیت در پروگرام منیجر روشن بود + if (command.IsProgramManagerUser) + { + if (!command.UserRoles.Any()) + return operation.Failed("حداقل یک نقش باید انتخاب شود"); + var parameters = new CreateProgramManagerUser( + command.Fullname, + command.Username, + account.Password, + command.Mobile, + command.Email, + account.id, + command.UserRoles + ); + + var url = "api/user/Create"; + + + var response = InternalApiCaller.PostAsync( + url, + key, + parameters + ); + + if (!response.Success) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Error); + + } + + if (!response.Result.isSuccess) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Error); + } + } + + } + + _unitOfWork.CommitAccountContext(); return operation.Succcedded(); } @@ -210,18 +349,18 @@ public class AccountApplication : IAccountApplication long idAutoriz = 0; var operation = new OperationResult(); if (string.IsNullOrWhiteSpace(command.Password)) - return operation.Failed(ApplicationMessages.EmptyPassword); + return operation.Failed(ApplicationMessages.EmptyPassword); if (string.IsNullOrWhiteSpace(command.Username)) - return operation.Failed(ApplicationMessages.EmptyUsername); + return operation.Failed(ApplicationMessages.EmptyUsername); - var account = _accountRepository.GetBy(command.Username); + var account = _accountRepository.GetBy(command.Username); var cameraAccount = _cameraAccountRepository.GetBy(command.Username); - SubAccount subAccount = _subAccountRepository.GetBy(command.Username); - if (account == null && cameraAccount == null && subAccount == null) - return operation.Failed(ApplicationMessages.WrongUserPass); + SubAccount subAccount = _subAccountRepository.GetBy(command.Username); + if (account == null && cameraAccount == null && subAccount == null) + return operation.Failed(ApplicationMessages.WrongUserPass); - if (account != null) + if (account != null) { (bool Verified, bool NeedUpgrade) result = _passwordHasher.Check(account.Password, command.Password); if (!result.Verified) @@ -245,8 +384,8 @@ public class AccountApplication : IAccountApplication if (account.ClientAriaPermission == "true" && account.AdminAreaPermission == "false" && account.IsActiveString == "true") { - var clientPermissions = _accountPermissionSubtitle1Repository.GetAllPermissionCodes(); - authViewModel.Permissions = clientPermissions; + var clientPermissions = _accountPermissionSubtitle1Repository.GetAllPermissionCodes(); + authViewModel.Permissions = clientPermissions; var workshopList = _workshopRepository.GetWorkshopsByClientAccountId(account.id).Select(x => new WorkshopClaim { PersonnelCount = x.PersonnelCount, @@ -255,14 +394,14 @@ public class AccountApplication : IAccountApplication Slug = _passwordHasher.SlugHasher(x.Id) }).OrderByDescending(x => x.PersonnelCount).ToList(); authViewModel.WorkshopList = workshopList; - if (workshopList.Any()) - { - var workshop = workshopList.First(); - authViewModel.WorkshopName = workshop.Name; - authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.Id); + if (workshopList.Any()) + { + var workshop = workshopList.First(); + authViewModel.WorkshopName = workshop.Name; + authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.Id); authViewModel.WorkshopId = workshop.Id; } - } + } _authHelper.Signin(authViewModel); @@ -281,7 +420,7 @@ public class AccountApplication : IAccountApplication var mobile = string.IsNullOrWhiteSpace(cameraAccount.Mobile) ? " " : cameraAccount.Mobile; var authViewModel = new CameraAuthViewModel(cameraAccount.id, cameraAccount.WorkshopId, - cameraAccount.Username, mobile, cameraAccount.WorkshopName, cameraAccount.AccountId,cameraAccount.IsActiveSting); + cameraAccount.Username, mobile, cameraAccount.WorkshopName, cameraAccount.AccountId, cameraAccount.IsActiveSting); if (cameraAccount.IsActiveSting == "true") { _authHelper.CameraSignIn(authViewModel); @@ -291,41 +430,41 @@ public class AccountApplication : IAccountApplication { idAutoriz = 0; } - + } - if (subAccount != null) - { - (bool Verified, bool NeedUpgrade) result = _passwordHasher.Check(subAccount.Password, command.Password); - if (!result.Verified) - return operation.Failed(ApplicationMessages.WrongUserPass); - var role = _subAccountRoleRepository.Get(subAccount.SubAccountRoleId); + if (subAccount != null) + { + (bool Verified, bool NeedUpgrade) result = _passwordHasher.Check(subAccount.Password, command.Password); + if (!result.Verified) + return operation.Failed(ApplicationMessages.WrongUserPass); + var role = _subAccountRoleRepository.Get(subAccount.SubAccountRoleId); - var permissions = role.RolePermissions.Select(x => x.PermissionCode).ToList(); - var authViewModel = new AuthViewModel(subAccount.AccountId, subAccount.SubAccountRoleId, subAccount.FullName - , subAccount.Username, subAccount.PhoneNumber, "", permissions, role.Title, "false", - "true", 0, subAccount.id); - var workshopList = _workshopSubAccountRepository.GetWorkshopsBySubAccountId(subAccount.id); - authViewModel.WorkshopList = workshopList.Select(x => new WorkshopClaim() - { - Slug = _passwordHasher.SlugHasher(x.WorkshopId), - Name = x.WorkshopName, - PersonnelCount = x.PersonnelCount, - Id = x.WorkshopId - }).ToList(); + var permissions = role.RolePermissions.Select(x => x.PermissionCode).ToList(); + var authViewModel = new AuthViewModel(subAccount.AccountId, subAccount.SubAccountRoleId, subAccount.FullName + , subAccount.Username, subAccount.PhoneNumber, "", permissions, role.Title, "false", + "true", 0, subAccount.id); + var workshopList = _workshopSubAccountRepository.GetWorkshopsBySubAccountId(subAccount.id); + authViewModel.WorkshopList = workshopList.Select(x => new WorkshopClaim() + { + Slug = _passwordHasher.SlugHasher(x.WorkshopId), + Name = x.WorkshopName, + PersonnelCount = x.PersonnelCount, + Id = x.WorkshopId + }).ToList(); - if (workshopList.Any()) - { - var workshop = workshopList.First(); - authViewModel.WorkshopName = workshop.WorkshopName; - authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.WorkshopId); + if (workshopList.Any()) + { + var workshop = workshopList.First(); + authViewModel.WorkshopName = workshop.WorkshopName; + authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.WorkshopId); authViewModel.WorkshopId = workshop.WorkshopId; - } - _authHelper.Signin(authViewModel); - idAutoriz = 2; - } + } + _authHelper.Signin(authViewModel); + idAutoriz = 2; + } - return operation.Succcedded(idAutoriz); + return operation.Succcedded(idAutoriz); } public OperationResult LoginWithMobile(long id) { @@ -356,24 +495,24 @@ public class AccountApplication : IAccountApplication if (account.ClientAriaPermission == "true" && account.AdminAreaPermission == "false" && account.IsActiveString == "true") { - var clientPermissions = _accountPermissionSubtitle1Repository.GetAllPermissionCodes(); - authViewModel.Permissions = clientPermissions; - var workshopList = _workshopRepository.GetWorkshopsByClientAccountId(account.id).Select(x => new WorkshopClaim - { - PersonnelCount = x.PersonnelCount, - Id = x.Id, - Name = x.WorkshopFullName, - Slug = _passwordHasher.SlugHasher(x.Id) - }).OrderByDescending(x => x.PersonnelCount).ToList(); - authViewModel.WorkshopList = workshopList; - if (workshopList.Any()) - { - var workshop = workshopList.First(); - authViewModel.WorkshopName = workshop.Name; - authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.Id); + var clientPermissions = _accountPermissionSubtitle1Repository.GetAllPermissionCodes(); + authViewModel.Permissions = clientPermissions; + var workshopList = _workshopRepository.GetWorkshopsByClientAccountId(account.id).Select(x => new WorkshopClaim + { + PersonnelCount = x.PersonnelCount, + Id = x.Id, + Name = x.WorkshopFullName, + Slug = _passwordHasher.SlugHasher(x.Id) + }).OrderByDescending(x => x.PersonnelCount).ToList(); + authViewModel.WorkshopList = workshopList; + if (workshopList.Any()) + { + var workshop = workshopList.First(); + authViewModel.WorkshopName = workshop.Name; + authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.Id); authViewModel.WorkshopId = workshop.Id; - } - } + } + } _authHelper.Signin(authViewModel); long idAutoriz = 0; @@ -429,7 +568,7 @@ public class AccountApplication : IAccountApplication return _accountRepository.GetByUserNameAndId(id, username); } - public async Task SetVerifyCode(string phone, long id) + public async Task SetVerifyCode(string phone, long id) { var operation = new OperationResult(); var account = _accountRepository.Get(id); @@ -443,11 +582,11 @@ public class AccountApplication : IAccountApplication _smsService.LoginSend(phone, r); //TimeSpan delay = TimeSpan.FromSeconds(30); - + await _accountRepository.RemoveCode(id); - + return operation.Succcedded(); - + } @@ -498,67 +637,67 @@ public class AccountApplication : IAccountApplication .Select(x => x.Code) .ToList(); - + _authHelper.SignOut(); var authViewModel = new AuthViewModel(account.id, account.RoleId, account.Fullname - , account.Username, account.Mobile, account.ProfilePhoto, permissions, account.RoleName, "false", "true",null); - var workshopList = _workshopRepository.GetWorkshopsByClientAccountId(account.id).Select(x => new WorkshopClaim - { - PersonnelCount = x.PersonnelCount, - Id = x.Id, - Name = x.WorkshopFullName, - Slug = _passwordHasher.SlugHasher(x.Id) - }).OrderByDescending(x => x.PersonnelCount).ToList(); + , account.Username, account.Mobile, account.ProfilePhoto, permissions, account.RoleName, "false", "true", null); + var workshopList = _workshopRepository.GetWorkshopsByClientAccountId(account.id).Select(x => new WorkshopClaim + { + PersonnelCount = x.PersonnelCount, + Id = x.Id, + Name = x.WorkshopFullName, + Slug = _passwordHasher.SlugHasher(x.Id) + }).OrderByDescending(x => x.PersonnelCount).ToList(); - authViewModel.WorkshopList = workshopList; + authViewModel.WorkshopList = workshopList; - var clientPermissions = _accountPermissionSubtitle1Repository.GetAllPermissionCodes(); + var clientPermissions = _accountPermissionSubtitle1Repository.GetAllPermissionCodes(); authViewModel.Permissions = clientPermissions; - if (authViewModel.WorkshopList.Any()) - { - var workshop = authViewModel.WorkshopList.First(); - authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.Id); - authViewModel.WorkshopName = workshop.Name; + if (authViewModel.WorkshopList.Any()) + { + var workshop = authViewModel.WorkshopList.First(); + authViewModel.WorkshopSlug = _passwordHasher.SlugHasher(workshop.Id); + authViewModel.WorkshopName = workshop.Name; authViewModel.WorkshopId = workshop.Id; - } - _authHelper.Signin(authViewModel); + } + _authHelper.Signin(authViewModel); return operation.Succcedded(2); } public OperationResult DirectCameraLogin(long cameraAccountId) { - var prAcc = _authHelper.CurrentAccountInfo(); - var operation = new OperationResult(); - var cameraAccount = _cameraAccountRepository.GetById(cameraAccountId); - if (cameraAccount == null) - return operation.Failed("این اکانت وجود ندارد"); + var prAcc = _authHelper.CurrentAccountInfo(); + var operation = new OperationResult(); + var cameraAccount = _cameraAccountRepository.GetById(cameraAccountId); + if (cameraAccount == null) + return operation.Failed("این اکانت وجود ندارد"); - _authHelper.SignOut(); + _authHelper.SignOut(); - var mobile = string.IsNullOrWhiteSpace(cameraAccount.Mobile) ? " " : cameraAccount.Mobile; - var authViewModel = new CameraAuthViewModel(cameraAccount.id, cameraAccount.WorkshopId, - cameraAccount.Username, mobile, cameraAccount.WorkshopName, cameraAccount.AccountId, cameraAccount.IsActiveSting); - if (cameraAccount.IsActiveSting == "true") - { - _authHelper.CameraSignIn(authViewModel); - - } - else - { - return operation.Failed("این اکانت غیر فعال شده است"); - } - return operation.Succcedded(2); + var mobile = string.IsNullOrWhiteSpace(cameraAccount.Mobile) ? " " : cameraAccount.Mobile; + var authViewModel = new CameraAuthViewModel(cameraAccount.id, cameraAccount.WorkshopId, + cameraAccount.Username, mobile, cameraAccount.WorkshopName, cameraAccount.AccountId, cameraAccount.IsActiveSting); + if (cameraAccount.IsActiveSting == "true") + { + _authHelper.CameraSignIn(authViewModel); + + } + else + { + return operation.Failed("این اکانت غیر فعال شده است"); + } + return operation.Succcedded(2); } - + public AccountLeftWorkViewModel WorkshopList(long accountId) { string fullname = this._accountRepository.GetById(accountId).Fullname; - List source =_accountLeftworkRepository.WorkshopList(accountId); + List source = _accountLeftworkRepository.WorkshopList(accountId); List userWorkshopIds = source.Select(x => x.WorkshopId).ToList(); List allWorkshops = this._accountLeftworkRepository.GetAllWorkshops(); List accountSelectList = this._accountRepository.GetAdminAccountSelectList(); @@ -641,65 +780,65 @@ public class AccountApplication : IAccountApplication #region Pooya public OperationResult IsPhoneNumberAndPasswordValid(long accountId, string phoneNumber, string password, string rePassword) { - OperationResult op = new(); + OperationResult op = new(); - var entity = _accountRepository.Get(accountId); + var entity = _accountRepository.Get(accountId); - if (entity == null) - return op.Failed(ApplicationMessages.RecordNotFound); + if (entity == null) + return op.Failed(ApplicationMessages.RecordNotFound); - if (!string.IsNullOrWhiteSpace(rePassword) || !string.IsNullOrWhiteSpace(password)) - { - if (rePassword != password) - return op.Failed("تکرار رمز عبور با رمز عبور مطابقت ندارد"); + if (!string.IsNullOrWhiteSpace(rePassword) || !string.IsNullOrWhiteSpace(password)) + { + if (rePassword != password) + return op.Failed("تکرار رمز عبور با رمز عبور مطابقت ندارد"); - if (password.Length < 8) - return op.Failed("رمز عبور نمی تواند کمتر از 8 کاراکتر باشد"); - } + if (password.Length < 8) + return op.Failed("رمز عبور نمی تواند کمتر از 8 کاراکتر باشد"); + } - if ((string.IsNullOrWhiteSpace(phoneNumber) || entity.Mobile == phoneNumber) && string.IsNullOrWhiteSpace(rePassword)) - return op.Failed("چیزی برای تغییر وجود ندارد"); + if ((string.IsNullOrWhiteSpace(phoneNumber) || entity.Mobile == phoneNumber) && string.IsNullOrWhiteSpace(rePassword)) + return op.Failed("چیزی برای تغییر وجود ندارد"); - if (!string.IsNullOrWhiteSpace(phoneNumber) && entity.Mobile != phoneNumber) - { - phoneNumber = phoneNumber.Trim(); - if (phoneNumber.Length != 11) - return op.Failed("شماره تلفن همراه به درستی وارد نشده است"); - if (_accountRepository.Exists(x => x.Mobile == phoneNumber && x.id != accountId) || - _subAccountRepository.Exists(x => x.PhoneNumber == phoneNumber) || - _cameraAccountRepository.Exists(x => x.Mobile == phoneNumber)) - return op.Failed("قبلا یک حساب با این شماره ثبت شده است"); - } + if (!string.IsNullOrWhiteSpace(phoneNumber) && entity.Mobile != phoneNumber) + { + phoneNumber = phoneNumber.Trim(); + if (phoneNumber.Length != 11) + return op.Failed("شماره تلفن همراه به درستی وارد نشده است"); + if (_accountRepository.Exists(x => x.Mobile == phoneNumber && x.id != accountId) || + _subAccountRepository.Exists(x => x.PhoneNumber == phoneNumber) || + _cameraAccountRepository.Exists(x => x.Mobile == phoneNumber)) + return op.Failed("قبلا یک حساب با این شماره ثبت شده است"); + } - return op.Succcedded(); - } + return op.Succcedded(); + } public OperationResult ChangePasswordAndPhoneNumber(AccountChangePasswordAndPhoneNumber command) { - OperationResult op = new(); - command.PhoneNumber = command.PhoneNumber.Trim(); - var entity = _accountRepository.Get(command.AccountId); - if (entity == null) - return op.Failed(ApplicationMessages.RecordNotFound); - var validationResult = IsPhoneNumberAndPasswordValid(command.AccountId, command.PhoneNumber, command.Password, command.RePassword); - if (validationResult.IsSuccedded == false) - return validationResult; + OperationResult op = new(); + command.PhoneNumber = command.PhoneNumber.Trim(); + var entity = _accountRepository.Get(command.AccountId); + if (entity == null) + return op.Failed(ApplicationMessages.RecordNotFound); + var validationResult = IsPhoneNumberAndPasswordValid(command.AccountId, command.PhoneNumber, command.Password, command.RePassword); + if (validationResult.IsSuccedded == false) + return validationResult; - if (!string.IsNullOrWhiteSpace(command.RePassword)) - { + if (!string.IsNullOrWhiteSpace(command.RePassword)) + { - entity.ChangePassword(_passwordHasher.Hash(command.Password)); - } + entity.ChangePassword(_passwordHasher.Hash(command.Password)); + } - if (!string.IsNullOrWhiteSpace(command.PhoneNumber)) - { - entity.Edit(entity.Fullname, entity.Username, command.PhoneNumber, entity.RoleId, entity.ProfilePhoto, entity.RoleName); - } - _accountRepository.SaveChanges(); - return op.Succcedded(); - } + if (!string.IsNullOrWhiteSpace(command.PhoneNumber)) + { + entity.Edit(entity.Fullname, entity.Username, command.PhoneNumber, entity.RoleId, entity.ProfilePhoto, entity.RoleName); + } + _accountRepository.SaveChanges(); + return op.Succcedded(); + } //public UserClaimsResponseDTO GetClaimsForSignIn(Login command) //{ // var operation = new OperationResult(); @@ -815,12 +954,12 @@ public class AccountApplication : IAccountApplication } (bool Verified, bool NeedUpgrade) result = _passwordHasher.Check(cameraAccount.Password, request.Password); - + if (!result.Verified) throw new BadRequestException(ApplicationMessages.WrongUserPass); var mobile = string.IsNullOrWhiteSpace(cameraAccount.Mobile) ? " " : cameraAccount.Mobile; - + var authViewModel = new CameraAuthViewModel(cameraAccount.id, cameraAccount.WorkshopId, cameraAccount.Username, mobile, cameraAccount.WorkshopName, cameraAccount.AccountId, cameraAccount.IsActiveSting); diff --git a/AccountManagement.Application/RoleApplication.cs b/AccountManagement.Application/RoleApplication.cs index dda743ee..51d05489 100644 --- a/AccountManagement.Application/RoleApplication.cs +++ b/AccountManagement.Application/RoleApplication.cs @@ -2,16 +2,23 @@ using AccountManagement.Application.Contracts.Role; using AccountManagement.Domain.RoleAgg; using System.Collections.Generic; +using System.Linq; +using AccountManagement.Application.Contracts.ProgramManagerApiResult; +using AccountManagement.Domain.InternalApiCaller; +using Company.Domain._common; +using AccountManagement.Application.Contracts.Ticket; namespace AccountManagement.Application; public class RoleApplication : IRoleApplication { private readonly IRoleRepository _roleRepository; + private readonly IUnitOfWork _unitOfWork; - public RoleApplication(IRoleRepository roleRepository) + public RoleApplication(IRoleRepository roleRepository, IUnitOfWork unitOfWork) { _roleRepository = roleRepository; + _unitOfWork = unitOfWork; } public OperationResult Create(CreateRole command) @@ -19,18 +26,51 @@ public class RoleApplication : IRoleApplication var operation = new OperationResult(); if (_roleRepository.Exists(x => x.Name == command.Name)) return operation.Failed(ApplicationMessages.DuplicatedRecord); - var permissions = new List(); - foreach (var code in command.Permissions) - { - if (code > 0) - { - permissions.Add(new Permission(code)); - } - } - //command.Permissions.ForEach(code => permissions.Add(new Permission(code))); + var permissions = command.Permissions.Where(x => x > 0).Select(x => new Permission(x)).ToList(); var role = new Role(command.Name, permissions); + _unitOfWork.BeginAccountContext(); + _roleRepository.Create(role); _roleRepository.SaveChanges(); + + var pmPermissions = command.PmPermissions.Where(x => x > 0).ToList(); + if (pmPermissions.Any()) + { + var parameters = new CreateProgramManagerRole + { + RoleName = command.Name, + Permissions = pmPermissions, + GozareshgirRoleId = role.id + + }; + + var url = "api/role"; + var key = SecretKeys.ProgramManagerInternalApi; + + var response = InternalApiCaller.PostAsync( + url, + key, + parameters + ); + + + if (!response.Success) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed("ارتباط با اپلیکیش پروگرام منیجر برقرار نشد"); + } + + if (!response.Result.isSuccess) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Result.errorMessage); + } + } + + + + //command.Permissions.ForEach(code => permissions.Add(new Permission(code))); + _unitOfWork.CommitAccountContext(); return operation.Succcedded(); } @@ -47,17 +87,129 @@ public class RoleApplication : IRoleApplication //var permissions = new List(); //command.Permissions.ForEach(code => permissions.Add(new Permission(code))); - var permissions = new List(); - foreach (var code in command.Permissions) - { - if (code > 0) - { - permissions.Add(new Permission(code)); - } - } + var permissions = command.Permissions.Where(x => x > 0).Select(x => new Permission(x)).ToList(); + + + _unitOfWork.BeginAccountContext(); role.Edit(command.Name, permissions); _roleRepository.SaveChanges(); + var key = SecretKeys.ProgramManagerInternalApi; + var pmPermissions = command.PmPermissions.Where(x => x > 0).ToList(); + + + //یافتن نقش در پروگرام منیجر + var apiResult = InternalApiCaller.GetAsync( + "api/role", + key, + new Dictionary + { + { "RoleName", "" }, + + { "GozareshgirRoleId", command.Id} + } + ); + + + + if (apiResult.Success) + { + + if (apiResult.Result.isSuccess) + { + //اگر این نقش در پروگرام منیجر وجود داشت ویرایش کن + if (apiResult.Result.data.role.Any()) + { + var parameters = new CreateProgramManagerRole + { + RoleName = command.Name, + Permissions = pmPermissions, + GozareshgirRoleId = role.id + + }; + + var url = "api/role/edit"; + + + var response = InternalApiCaller.PostAsync( + url, + key, + parameters + ); + + + if (!response.Success) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed("ارتباط با اپلیکیش پروگرام منیجر برقرار نشد"); + } + + if (!response.Result.isSuccess) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Result.errorMessage); + } + } + else //اگر نقش در پروگرام منیجر وجود نداشت + { + + //اگر تیک پرمیشن های پروگرام منیجر زده شده + //این نقش را سمت پروگرام منیجر بساز + if (pmPermissions.Any()) + { + var parameters = new CreateProgramManagerRole + { + RoleName = command.Name, + Permissions = pmPermissions, + GozareshgirRoleId = role.id + + }; + + var url = "api/role"; + + + var response = InternalApiCaller.PostAsync( + url, + key, + parameters + ); + + + if (!response.Success) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed("ارتباط با اپلیکیش پروگرام منیجر برقرار نشد"); + } + + if (!response.Result.isSuccess) + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed(response.Result.errorMessage); + } + } + } + + } + else + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed("ارتباط با اپلیکیش پروگرام منیجر برقرار نشد"); + + } + + } + else + { + _unitOfWork.RollbackAccountContext(); + return operation.Failed("ارتباط با اپلیکیش پروگرام منیجر برقرار نشد"); + } + + + + + + + return operation.Succcedded(); } diff --git a/AccountManagement.Domain/InternalApiCaller/InternalApiCaller.cs b/AccountManagement.Domain/InternalApiCaller/InternalApiCaller.cs new file mode 100644 index 00000000..98ab3c1d --- /dev/null +++ b/AccountManagement.Domain/InternalApiCaller/InternalApiCaller.cs @@ -0,0 +1,155 @@ +using Newtonsoft.Json; +using System.Net.Http; +using System.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; + +namespace AccountManagement.Domain.InternalApiCaller; + +public class ApiResult +{ + public bool Success { get; set; } + public T Result { get; set; } + public string Error { get; set; } +} + +public static class InternalApiCaller +{ + private static string _baseUrl = ""; + + public static void SetBaseUrl(string baseUrl) + { + _baseUrl = baseUrl.TrimEnd('/'); // حذف / اضافی + } + /// + ///api post متد + /// + /// + /// + /// + /// + /// + /// + public static ApiResult PostAsync( + string url, + string internalKey, + TRequest body + ) + { + try + { + var client = new HttpClient(); + + // ساخت URL نهایی + var finalUrl = $"{_baseUrl}/{url.TrimStart('/')}"; + + var request = new HttpRequestMessage(HttpMethod.Post, finalUrl); + + request.Headers.Add("X-INTERNAL-KEY", internalKey); + + var json = JsonConvert.SerializeObject(body); + request.Content = new StringContent(json, Encoding.UTF8, "application/json"); + + var response = client.SendAsync(request).GetAwaiter().GetResult(); + + if (!response.IsSuccessStatusCode) + { + return new ApiResult + { + Success = false, + Error = $"HTTP Error: {response.StatusCode}" + }; + } + + var text = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + var deserialized = JsonConvert.DeserializeObject(text); + + return new ApiResult + { + Success = true, + Result = deserialized + }; + } + catch (Exception ex) + { + return new ApiResult + { + Success = false, + Error = ex.Message + }; + } + } + + + + + /// + /// Api Get متد + /// + /// + /// + /// + /// + /// + public static ApiResult GetAsync( + string url, + string internalKey, + Dictionary parameters = null + ) + { + try + { + if (parameters != null && parameters.Any()) + { + var query = string.Join("&", + parameters + .Where(p => p.Value != null) + .Select(p => $"{p.Key}={p.Value}") + ); + + url += url.Contains("?") ? "&" + query : "?" + query; + } + + // ساخت URL نهایی + var finalUrl = $"{_baseUrl}/{url.TrimStart('/')}"; + + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Get, finalUrl); + + request.Headers.Add("X-INTERNAL-KEY", internalKey); + + var response = client.SendAsync(request).GetAwaiter().GetResult(); + + if (!response.IsSuccessStatusCode) + { + return new ApiResult + { + Success = false, + Error = $"HTTP Error: {response.StatusCode}" + }; + } + + var text = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + var deserialized = JsonConvert.DeserializeObject(text); + + return new ApiResult + { + Success = true, + Result = deserialized + }; + } + catch (Exception ex) + { + return new ApiResult + { + Success = false, + Error = ex.Message + }; + } + } + + + +} diff --git a/Company.Domain/Company.Domain.csproj b/Company.Domain/Company.Domain.csproj index 09e112b7..924cc212 100644 --- a/Company.Domain/Company.Domain.csproj +++ b/Company.Domain/Company.Domain.csproj @@ -16,6 +16,7 @@ + diff --git a/Company.Domain/_common/IUnitOfWork.cs b/Company.Domain/_common/IUnitOfWork.cs new file mode 100644 index 00000000..1c9b4cbf --- /dev/null +++ b/Company.Domain/_common/IUnitOfWork.cs @@ -0,0 +1,8 @@ +namespace Company.Domain._common; + +public interface IUnitOfWork +{ + void BeginAccountContext(); + void CommitAccountContext(); + void RollbackAccountContext(); +} \ No newline at end of file diff --git a/CompanyManagment.EFCore/_common/UnitOfWork.cs b/CompanyManagment.EFCore/_common/UnitOfWork.cs new file mode 100644 index 00000000..ddd47484 --- /dev/null +++ b/CompanyManagment.EFCore/_common/UnitOfWork.cs @@ -0,0 +1,31 @@ +using AccountMangement.Infrastructure.EFCore; +using Company.Domain._common; +using Microsoft.EntityFrameworkCore.Storage; + +namespace CompanyManagment.EFCore._common; + +public class UnitOfWork : IUnitOfWork +{ + private readonly AccountContext _context; + private IDbContextTransaction _transaction; + + public UnitOfWork(AccountContext context) + { + _context = context; + } + + public void BeginAccountContext() + { + _transaction = _context.Database.BeginTransaction(); + } + + public void CommitAccountContext() + { + _transaction?.Commit(); + } + + public void RollbackAccountContext() + { + _transaction?.Rollback(); + } +} diff --git a/PersonalContractingParty.Config/PersonalBootstrapper.cs b/PersonalContractingParty.Config/PersonalBootstrapper.cs index 76c5b25e..a9ee5eae 100644 --- a/PersonalContractingParty.Config/PersonalBootstrapper.cs +++ b/PersonalContractingParty.Config/PersonalBootstrapper.cs @@ -233,6 +233,8 @@ using CompanyManagment.App.Contracts.FinancialInvoice; using _0_Framework.Application.FaceEmbedding; using _0_Framework.Infrastructure; using _0_Framework.InfraStructure; +using Company.Domain._common; +using CompanyManagment.EFCore._common; namespace PersonalContractingParty.Config; @@ -545,6 +547,13 @@ public class PersonalBootstrapper services.AddTransient(); services.AddTransient(); + #endregion + + + #region UnitOfWork + + services.AddTransient(); + #endregion //=========End Of Main==================================== diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Create.cshtml b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Create.cshtml index 113c9545..4819c826 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Create.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Create.cshtml @@ -1,6 +1,53 @@ @model AccountManagement.Application.Contracts.Account.CreateAccount @{ + } - + + + + +
+ + + + +
+ + + +
+ + + +
+ + +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + @@ -1096,22 +1206,36 @@ \ No newline at end of file diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Edit.cshtml b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Edit.cshtml index eda0fe89..cfcf1bab 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Edit.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Edit.cshtml @@ -1,5 +1,62 @@ @model AccountManagement.Application.Contracts.Account.EditAccount @{ + + + string disable = "disabled=\"disabled\""; + } + + +
+ + + + +
+ + + +
+ + + +
+ + +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
@@ -1160,22 +1266,35 @@ \ No newline at end of file diff --git a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml index 5ca79e88..92dca681 100644 --- a/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml +++ b/ServiceHost/Areas/Admin/Pages/Accounts/Account/Index.cshtml @@ -553,6 +553,16 @@ + + +