wip - admin tasks

This commit is contained in:
Frede Hundewadt 2022-06-26 18:22:38 +02:00
parent 2948b91c1f
commit e42c4ffe0d
18 changed files with 381 additions and 39 deletions

View file

@ -20,38 +20,48 @@
<div class="list-group">
<div class="list-group-item bg-dark bg-opacity-75 text-white">
<div class="row">
<div class="col-sm-1">
Land
</div>
<div class="col-sm-1">
Nr.
</div>
<div class="col">
Navn
</div>
<div class="col">
Land
Telefonnr.
</div>
<div class="col">
Email
</div>
<div class="col">
Telefonnr.
</div>
</div>
</div>
@foreach (var user in UserList)
{
<a class="list-group-item list-group-item-action" href="/admin/users/@user.UserId">
<div class="row">
<div class="col-sm-1">
@user.CountryCode
</div>
<div class="col-sm-1">
@user.SalesRep
</div>
<div class="col">
@user.FullName
</div>
<div class="col">
@user.CountryCode
@user.PhoneNumber
</div>
<div class="col">
@user.Email
</div>
<div class="col">
@user.PhoneNUmber
</div>
</div>
</a>
}
</div>
}
else
{
<AppSpinner/>
}

View file

@ -6,5 +6,5 @@ namespace Wonky.Client.Components;
public partial class UserTableComponent
{
[Parameter] public List<AdminUserListView> UserList { get; set; }
[Parameter] public List<AdminAdviserListView> UserList { get; set; }
}

View file

@ -4,7 +4,7 @@ namespace Wonky.Client.HttpRepository;
public interface ITaskItemHttpRepository
{
Task<List<TaskItemView>> GetTaskList();
Task<List<TaskItemView>?> GetTaskList();
Task CreateTaskItem(TaskItemView taskItem);
Task<TaskItemView> GetTaskItem(string taskItemId);
Task<TaskItemView?> GetTaskItem(string taskItemId);
}

View file

@ -4,5 +4,8 @@ namespace Wonky.Client.HttpRepository;
public interface IUserHttpRepository
{
Task<List<AdminUserListView>?> GetSalesReps();
Task<List<AdminAdviserListView>> GetAdvisers();
Task<AdviserInfoView> GetAdviserInfo(string userId);
Task UpdateAdviser(string userId, AdviserUpdateDto model);
Task ResetUserPassword(string userId, string newPasswd, string confirmPasswd);
}

View file

@ -30,7 +30,7 @@ public class TaskItemHttpRepository : ITaskItemHttpRepository
_apiConfig = configuration.Value;
}
public async Task<List<TaskItemView>> GetTaskList()
public async Task<List<TaskItemView>?> GetTaskList()
{
return await _client.GetFromJsonAsync<List<TaskItemView>>($"{_apiConfig.TaskUri}");
}
@ -40,7 +40,7 @@ public class TaskItemHttpRepository : ITaskItemHttpRepository
await _client.PostAsJsonAsync($"{_apiConfig.TaskUri}", taskItem);
}
public async Task<TaskItemView> GetTaskItem(string taskItemId)
public async Task<TaskItemView?> GetTaskItem(string taskItemId)
{
return await _client.GetFromJsonAsync<TaskItemView>($"{_apiConfig.TaskUri}/{taskItemId}");
}

View file

@ -1,8 +1,10 @@
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Wonky.Entity.Configuration;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
namespace Wonky.Client.HttpRepository;
@ -29,12 +31,32 @@ public class UserHttpRepository : IUserHttpRepository
_api = configuration.Value;
}
public async Task<List<AdminUserListView>?> GetSalesReps()
public async Task<List<AdminAdviserListView>> GetAdvisers()
{
var response = await _client.GetAsync(_api.AdminSalesRepUri);
var content = await response.Content.ReadAsStringAsync();
return !response.IsSuccessStatusCode
? new List<AdminUserListView>()
: JsonSerializer.Deserialize<List<AdminUserListView>>(content, _options);
return await _client.GetFromJsonAsync<List<AdminAdviserListView>>(_api.AdminAdviserUri);
}
public async Task<AdviserInfoView> GetAdviserInfo(string userId)
{
return await _client.GetFromJsonAsync<AdviserInfoView>($"{_api.AdminAdviserUri}/{userId}");
// var response = await _client.GetAsync($"{_api.AdminAdviserUri}/{userId}");
// var content = await response.Content.ReadAsStringAsync();
// _logger.LogInformation($"GetAdviserInfo => {content}", content);
// return new AdviserInfoView();
// if (!response.IsSuccessStatusCode)
// return new AdviserInfoView();
// var result = JsonSerializer.Deserialize<ApiResponse>(content);
// return JsonSerializer.Deserialize<AdviserInfoView>(result.Message);
}
public async Task UpdateAdviser(string userId, AdviserUpdateDto model)
{
await _client.PutAsJsonAsync($"{_api.AdminAdviserUri}/{userId}", model, _options);
}
public async Task ResetUserPassword(string userId, string newPasswd, string confirmPasswd)
{
var passwd = new Dictionary<string, string> {{"newPassword", newPasswd}, {"confirmPassword", confirmPasswd}};
await _client.PostAsJsonAsync($"{_api.AdminPasswdUri}/{userId}", passwd, _options);
}
}

View file

@ -0,0 +1,30 @@
@*
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
*@
@using Wonky.Client.Components
@page "/sales-reports"
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<h3>Rapport Arkiv</h3>
</div>
</div>
</div>
<div class="card-body">
<ReportTableComponent ReportList="_reports"></ReportTableComponent>
</div>
</div>

View file

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Components;
using Toolbelt.Blazor;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Entity.Views;
namespace Wonky.Client.Pages;
public partial class AdminSalesReportList : IDisposable
{
[Parameter] public string UserId { get; set; } = "";
[Inject] private IReportHttpRepository _reportRepo { get; set; }
[Inject] private HttpInterceptorService _interceptor { get; set; }
private List<NgSalesReportListView> _reports { get; set; }
protected override async Task OnInitializedAsync()
{
_interceptor.RegisterEvent();
_interceptor.RegisterBeforeSendEvent();
_reports = await _reportRepo.GetReports();
}
public void Dispose()
{
_interceptor.DisposeEvent();
}
}

View file

@ -1,7 +1,8 @@
@using Wonky.Client.Components
@page "/admin/users"
<div class="card">
<div class="card-heder">
<div class="card-header">
<h3>Sælgere</h3>
</div>
<div class="card-body">

View file

@ -5,17 +5,22 @@ using Wonky.Entity.DTO;
namespace Wonky.Client.Pages;
public partial class AdminUserList
public partial class AdminUserList : IDisposable
{
[Inject] private HttpInterceptorService _interceptor { get; set; }
[Inject] private IUserHttpRepository _userRepo { get; set; }
private List<AdminUserListView>? _salesReps { get; set; } = new();
private List<AdminAdviserListView>? _salesReps { get; set; } = new();
protected override async Task OnInitializedAsync()
{
_interceptor.RegisterEvent();
_interceptor.RegisterBeforeSendEvent();
_salesReps = await _userRepo.GetSalesReps();
_salesReps = await _userRepo.GetAdvisers();
}
public void Dispose()
{
_interceptor.DisposeEvent();
}
}

View file

@ -0,0 +1,104 @@
@page "/admin/users/{UserId}"
<div class="card">
<div class="card-header">
<h3>Sælger info</h3>
</div>
<div class="card-body">
@if (_adviserInfo != null)
{
<EditForm EditContext="_editContext" OnValidSubmit="UpdateAdviser">
<DataAnnotationsValidator/>
<div class="row">
<div class="col">
<table class="table">
<tbody>
<tr class="align-middle">
<th scope="col">
Fornavn
</th>
<td>
<InputText id="firstName" class="form-control" @bind-Value="_updateInfo.FirstName"/>
<ValidationMessage For="@(() => _updateInfo.FirstName)"></ValidationMessage>
</td>
<th scope="col">
Efternavn
</th>
<td>
<InputText id="lastName" class="form-control" @bind-Value="_updateInfo.LastName"/>
<ValidationMessage For="@(() => _updateInfo.LastName)"></ValidationMessage>
</td>
</tr>
<tr class="align-middle">
<th scope="col">
Email
</th>
<td>
<InputText id="email" class="form-control" @bind-Value="_updateInfo.Email"/>
<ValidationMessage For="@(() => _updateInfo.Email)"></ValidationMessage>
</td>
<th scope="col">
Mobilnummer
</th>
<td>
<InputText id="phoneNumber" class="form-control" @bind-Value="_updateInfo.PhoneNumber"/>
<ValidationMessage For="@(() => _updateInfo.PhoneNumber)"></ValidationMessage>
</td>
</tr>
<tr class="align-middle">
<th scope="col">
Sælgernr.
</th>
<td>
@_adviserInfo.Adviser
</td>
<th scope="col">
Landekode
</th>
<td>
@_updateInfo.CountryCode
</td>
</tr>
<tr class="align-middle">
<th scope="col">
Spærret
</th>
<td colspan="3">
<InputCheckbox id="lockoutEnabled" class="form-check" @bind-Value="_updateInfo.LockoutEnabled"/>
<ValidationMessage For="@(() => _updateInfo.LockoutEnabled)"></ValidationMessage>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col">
<a class="btn btn-primary" href="/admin/users">Tilbage</a>
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Gem</button>
</div>
</div>
</EditForm>
<form class="mt-5">
<h3>NULSTIL ADGANGSKODE</h3>
<div class="row mb-3">
<label for="newPasswd" class="col-md-2 col-form-label">Ny</label>
<div class="col-md-4">
<input id="newPasswd" type="password" class="form-control"/>
</div>
</div>
<div class="row mb-3">
<label for="verifyPasswd" class="col-md-2 col-form-label">Bekræft</label>
<div class="col-md-4">
<input id="verifyPasswd" type="password" class="form-control"/>
</div>
</div>
</form>
}
else
{
<AppSpinner/>
}
</div>
</div>

View file

@ -0,0 +1,65 @@
using System.Text.Json;
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Entity.DTO;
namespace Wonky.Client.Pages;
public partial class AdminUserView : IDisposable
{
[Parameter] public string UserId { get; set; } = "";
[Inject] private HttpInterceptorService _interceptor { get; set; }
[Inject] private IUserHttpRepository _userRepo { get; set; }
[Inject] private ILogger<AdminUserView> _logger { get; set; }
[Inject] private NavigationManager _navigator { get; set; }
[Inject] private IToastService _toast { get; set; }
private AdviserInfoView _adviserInfo { get; set; } = new();
private EditContext _editContext { get; set; }
private AdviserUpdateDto _updateInfo { get; set; } = new();
private readonly JsonSerializerOptions? _options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
protected override async Task OnParametersSetAsync()
{
_interceptor.RegisterEvent();
_interceptor.RegisterBeforeSendEvent();
_adviserInfo = await _userRepo.GetAdviserInfo(UserId);
_updateInfo.Email = _adviserInfo.Email;
_updateInfo.CountryCode = _adviserInfo.CountryCode;
_updateInfo.FirstName = _adviserInfo.FirstName;
_updateInfo.LastName = _adviserInfo.LastName;
_updateInfo.PhoneNumber = _adviserInfo.PhoneNumber;
_updateInfo.LockoutEnabled = _adviserInfo.LockoutEnabled;
}
protected override void OnInitialized()
{
_editContext = new EditContext(_updateInfo);
}
private async Task UpdateAdviser()
{
_toast.ShowInfo("Sender data til server ...");
await _userRepo.UpdateAdviser(UserId, _updateInfo);
_navigator.NavigateTo("/admin/users");
}
private async Task SetPassword()
{
}
public void Dispose()
{
_interceptor.DisposeEvent();
}
}

View file

@ -34,21 +34,28 @@
<AuthorizeView>
<NotAuthorized>
<div class="nav-item px-3">
<NavLink class="nav-link ps-2" href="login">
<NavLink class="nav-link ps-2" href="login" Match="NavLinkMatch.All">
<span class="oi oi-account-login"></span> Log ind
</NavLink>
</div>
</NotAuthorized>
</AuthorizeView>
<AuthorizeView Roles="Adviser,Admin">
<AuthorizeView Roles="Admin">
<div class="nav-item px-3">
<NavLink class="nav-link ps-2" href="/admin/users">
<span class="oi oi-people" aria-hidden="true"></span> Sælgere
</NavLink>
</div>
</AuthorizeView>
<AuthorizeView Roles="Adviser">
<Authorized>
<div class="nav-item px-3">
<NavLink class="nav-link ps-2" href="/home" Match="NavLinkMatch.All">
<NavLink class="nav-link ps-2" href="/home">
<span class="oi oi-calendar" aria-hidden="true"></span> ToDo
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link ps-2" href="/activity-today" Match="NavLinkMatch.All">
<NavLink class="nav-link ps-2" href="/activity-today">
<span class="oi oi-dashboard" aria-hidden="true"></span> Aktivitet
</NavLink>
</div>

View file

@ -1,8 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "None",
"System": "Information",
"Default": "Debug",
"System": "Debug",
"Microsoft": "Information"
},
"Debug": {
@ -32,13 +32,16 @@
"tokenUri": "token",
"userInfoUri": "api/auth/userinfo",
"catalogUri": "api/v2/crm/catalog",
"activityUri": "api/v2/crm/salesReps/sales",
"reportUri": "api/v2/crm/salesReps/reports",
"taskUri": "api/v2/crm/salesRps/tasks",
"activityUri": "api/v2/crm/advisers/sales",
"reportUri": "api/v2/crm/advisers/reports",
"taskUri": "api/v2/crm/advisers/tasks",
"companyUri": "api/v2/crm/companies",
"inventoryUri": "history/inventory",
"productUri": "history/products",
"syncUri": "history/sync",
"adminSalesRepUri": "api/v2/admin/users/salesreps"
"adminAdviserUri": "api/v2/admin/users/advisers",
"adminPasswdUri": "api/v2/admin/users/passwd",
"adminReportUri": "reports",
"adminCompanyUri": "companies"
}
}

View file

@ -34,7 +34,8 @@ public class ApiConfig
public string InventoryUri { get; set; } = "";
public string ProductUri { get; set; } = "";
public string SyncUri { get; set; } = "";
public string AdminSalesRepUri { get; set; } = "";
public string AdminAdviserUri { get; set; } = "";
public string AdminPasswdUri { get; set; } = "";

View file

@ -1,10 +1,11 @@
namespace Wonky.Entity.DTO;
public class AdminUserListView
public class AdminAdviserListView
{
public string UserId { get; set; } = "";
public string SalesRep { get; set; } = "";
public string FullName { get; set; } = "";
public string Email { get; set; } = "";
public string CountryCode { get; set; } = "";
public string PhoneNUmber { get; set; } = "";
public string PhoneNumber { get; set; } = "";
}

View file

@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
namespace Wonky.Entity.DTO;
public class AdviserInfoView
{
[Required(ErrorMessage = "Fornavn skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 50 tegn.")]
public string FirstName { get; set; } = "";
[Required(ErrorMessage = "Efternavn skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 50 tegn.")]
public string LastName { get; set; } = "";
[Required(ErrorMessage = "Landekode skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 3 tegn.")]
public string CountryCode { get; set; } = "";
[Required(ErrorMessage = "Email skal angives.")]
[MaxLength(50, ErrorMessage = "Der kan højst bruges 80 tegn.")]
public string Email { get; set; } = "";
[MaxLength(20, ErrorMessage = "Der kan højst bruges 20 tegn.")]
public string PhoneNumber { get; set; } = "";
[Required(ErrorMessage = "Sælger identifikation skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 20 tegn.")]
public string Adviser { get; set; } = "";
public string CountryName { get; set; } = "";
public bool LockoutEnabled { get; set; }
public bool EmailConfirmed { get; set; }
public bool IsAdviser { get; set; }
public bool IsAdmin { get; set; }
}

View file

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
namespace Wonky.Entity.DTO;
public class AdviserUpdateDto
{
[Required(ErrorMessage = "Fornavn skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 50 tegn.")]
public string FirstName { get; set; } = "";
[Required(ErrorMessage = "Efternavn skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 50 tegn.")]
public string LastName { get; set; } = "";
[Required(ErrorMessage = "Landekode skal angives.")]
[MaxLength(50,ErrorMessage = "Der kan højst bruges 3 tegn.")]
public string CountryCode { get; set; } = "";
[Required(ErrorMessage = "Email skal angives.")]
[MaxLength(50, ErrorMessage = "Der kan højst bruges 80 tegn.")]
public string Email { get; set; } = "";
[MaxLength(20, ErrorMessage = "Der kan højst bruges 20 tegn.")]
public string PhoneNumber { get; set; } = "";
[Required] public bool LockoutEnabled { get; set; }
}