mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
started merging module and calendar pages
This commit is contained in:
18
.config/dotnet-tools.json
Normal file
18
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"dotnet-stryker": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-stryker"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"csharpier": {
|
||||||
|
"version": "0.25.0",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-csharpier"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ public class ConfigurationTests
|
|||||||
var daysOfWeek = new DayOfWeek[] { DayOfWeek.Monday };
|
var daysOfWeek = new DayOfWeek[] { DayOfWeek.Monday };
|
||||||
var management = new ConfigurationManagement();
|
var management = new ConfigurationManagement();
|
||||||
management.SetConfiguration(canvasTerm, daysOfWeek);
|
management.SetConfiguration(canvasTerm, daysOfWeek);
|
||||||
var config = management.Configuration;
|
var config = management.SemesterCalendar;
|
||||||
|
|
||||||
if(config == null) Assert.Fail();
|
if(config == null) Assert.Fail();
|
||||||
config!.StartDate.Should().Be(startAt);
|
config!.StartDate.Should().Be(startAt);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class SemesterPlannerTests
|
|||||||
public void TestCanCreatePlanner()
|
public void TestCanCreatePlanner()
|
||||||
{
|
{
|
||||||
|
|
||||||
var config = new SemesterConfiguration(
|
var config = new SemesterCalendarConfig(
|
||||||
StartDate: new DateTime(2022, 1, 1),
|
StartDate: new DateTime(2022, 1, 1),
|
||||||
EndDate: new DateTime(2022, 1, 2),
|
EndDate: new DateTime(2022, 1, 2),
|
||||||
new DayOfWeek[] { }
|
new DayOfWeek[] { }
|
||||||
@@ -22,7 +22,7 @@ public class SemesterPlannerTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNewPlannerHasCorrectNumberOfMonths()
|
public void TestNewPlannerHasCorrectNumberOfMonths()
|
||||||
{
|
{
|
||||||
var config = new SemesterConfiguration(
|
var config = new SemesterCalendarConfig(
|
||||||
StartDate: new DateTime(2022, 1, 1),
|
StartDate: new DateTime(2022, 1, 1),
|
||||||
EndDate: new DateTime(2022, 2, 1),
|
EndDate: new DateTime(2022, 2, 1),
|
||||||
new DayOfWeek[] { }
|
new DayOfWeek[] { }
|
||||||
@@ -36,7 +36,7 @@ public class SemesterPlannerTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNewPlannerHandlesTermsThatWrapYears()
|
public void TestNewPlannerHandlesTermsThatWrapYears()
|
||||||
{
|
{
|
||||||
var config = new SemesterConfiguration(
|
var config = new SemesterCalendarConfig(
|
||||||
StartDate: new DateTime(2022, 12, 1),
|
StartDate: new DateTime(2022, 12, 1),
|
||||||
EndDate: new DateTime(2023, 1, 1),
|
EndDate: new DateTime(2023, 1, 1),
|
||||||
new DayOfWeek[] { }
|
new DayOfWeek[] { }
|
||||||
@@ -50,7 +50,7 @@ public class SemesterPlannerTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSemesterGetsCorrectMonths()
|
public void TestSemesterGetsCorrectMonths()
|
||||||
{
|
{
|
||||||
var config = new SemesterConfiguration(
|
var config = new SemesterCalendarConfig(
|
||||||
StartDate: new DateTime(2022, 1, 1),
|
StartDate: new DateTime(2022, 1, 1),
|
||||||
EndDate: new DateTime(2022, 2, 1),
|
EndDate: new DateTime(2022, 2, 1),
|
||||||
new DayOfWeek[] { }
|
new DayOfWeek[] { }
|
||||||
@@ -66,7 +66,7 @@ public class SemesterPlannerTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMonthsCanWrapYears()
|
public void TestMonthsCanWrapYears()
|
||||||
{
|
{
|
||||||
var config = new SemesterConfiguration(
|
var config = new SemesterCalendarConfig(
|
||||||
StartDate: new DateTime(2022, 12, 1),
|
StartDate: new DateTime(2022, 12, 1),
|
||||||
EndDate: new DateTime(2023, 1, 1),
|
EndDate: new DateTime(2023, 1, 1),
|
||||||
new DayOfWeek[] { }
|
new DayOfWeek[] { }
|
||||||
@@ -85,7 +85,7 @@ public class SemesterPlannerTests
|
|||||||
public void TestSemesterTracksDaysOfWeek()
|
public void TestSemesterTracksDaysOfWeek()
|
||||||
{
|
{
|
||||||
DayOfWeek[] days = new DayOfWeek[] { DayOfWeek.Monday };
|
DayOfWeek[] days = new DayOfWeek[] { DayOfWeek.Monday };
|
||||||
var config = new SemesterConfiguration(
|
var config = new SemesterCalendarConfig(
|
||||||
StartDate: new DateTime(2022, 12, 1),
|
StartDate: new DateTime(2022, 12, 1),
|
||||||
EndDate: new DateTime(2023, 1, 1),
|
EndDate: new DateTime(2023, 1, 1),
|
||||||
days
|
days
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@page "/calendar"
|
@page "/calendar"
|
||||||
@using CanvasModel.EnrollmentTerms
|
@using CanvasModel.EnrollmentTerms
|
||||||
|
@using Management.Web.Shared.Module
|
||||||
@using Management.Web.Shared.Semester
|
@using Management.Web.Shared.Semester
|
||||||
|
|
||||||
@inject IConfigurationManagement configurationManagement
|
@inject IConfigurationManagement configurationManagement
|
||||||
@@ -10,17 +11,26 @@
|
|||||||
private SemesterPlanner? semester { get; set; }
|
private SemesterPlanner? semester { get; set; }
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
if (configurationManagement.Configuration != null)
|
if (configurationManagement.SemesterCalendar != null)
|
||||||
semester = new SemesterPlanner(configurationManagement.Configuration);
|
semester = new SemesterPlanner(configurationManagement.SemesterCalendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
<br>
|
<br>
|
||||||
@if (semester != null)
|
|
||||||
{
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
@if (semester != null)
|
||||||
|
{
|
||||||
@foreach (var month in semester.Months)
|
@foreach (var month in semester.Months)
|
||||||
{
|
{
|
||||||
<MonthDetail Month="month" Semester="semester" />
|
<MonthDetail Month="month" Semester="semester" />
|
||||||
<hr />
|
<hr />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<Modules />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -5,10 +5,13 @@
|
|||||||
|
|
||||||
@inject ICanvasService canvasService
|
@inject ICanvasService canvasService
|
||||||
@inject IConfigurationManagement configurationManagement
|
@inject IConfigurationManagement configurationManagement
|
||||||
@inject ProtectedSessionStorage ProtectedSessionStore
|
@inject ProtectedLocalStorage BrowserStorage
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
|
private string semesterConfigurationKey = "semesterCalendarConfiguration";
|
||||||
private IEnumerable<EnrollmentTermModel>? terms { get; set; } = null;
|
private IEnumerable<EnrollmentTermModel>? terms { get; set; } = null;
|
||||||
private ulong? selectedTermId { get; set; }
|
private ulong? selectedTermId { get; set; }
|
||||||
private EnrollmentTermModel? selectedTerm
|
private EnrollmentTermModel? selectedTerm
|
||||||
@@ -25,12 +28,27 @@
|
|||||||
readDaysFromConfig();
|
readDaysFromConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if(firstRender)
|
||||||
|
{
|
||||||
|
var storedConfiguration = await BrowserStorage.GetAsync<SemesterCalendarConfig>(semesterConfigurationKey);
|
||||||
|
if (storedConfiguration.Success) {
|
||||||
|
Console.WriteLine(JsonSerializer.Serialize(storedConfiguration.Value));
|
||||||
|
configurationManagement.SemesterCalendar = storedConfiguration.Value;
|
||||||
|
} else {
|
||||||
|
Console.WriteLine("no stored configuration");
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void readTermFromConfig()
|
private void readTermFromConfig()
|
||||||
{
|
{
|
||||||
if (terms == null || configurationManagement.Configuration == null) return;
|
if (terms == null || configurationManagement.SemesterCalendar == null) return;
|
||||||
foreach (var term in terms)
|
foreach (var term in terms)
|
||||||
{
|
{
|
||||||
var termInConfiguration = configurationManagement.Configuration.StartDate == term.StartAt;
|
var termInConfiguration = configurationManagement.SemesterCalendar.StartDate == term.StartAt;
|
||||||
if (termInConfiguration)
|
if (termInConfiguration)
|
||||||
{
|
{
|
||||||
selectedTermId = term.Id;
|
selectedTermId = term.Id;
|
||||||
@@ -40,17 +58,24 @@
|
|||||||
|
|
||||||
private void readDaysFromConfig()
|
private void readDaysFromConfig()
|
||||||
{
|
{
|
||||||
if (terms == null || configurationManagement.Configuration == null) return;
|
if (terms == null || configurationManagement.SemesterCalendar == null) return;
|
||||||
|
|
||||||
days = configurationManagement.Configuration.Days.ToList();
|
days = configurationManagement.SemesterCalendar.Days.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async void HandleSave()
|
public async void HandleSave()
|
||||||
{
|
{
|
||||||
saved = true;
|
saved = true;
|
||||||
configurationManagement.SetConfiguration(selectedTerm, days.ToArray());
|
configurationManagement.SetConfiguration(
|
||||||
await ProtectedSessionStore.SetAsync("configuration", configurationManagement.Configuration);
|
selectedTerm ?? throw new Exception("cannot save configuration without selecting term"),
|
||||||
|
days.ToArray()
|
||||||
|
);
|
||||||
|
await BrowserStorage.SetAsync(
|
||||||
|
semesterConfigurationKey,
|
||||||
|
configurationManagement.SemesterCalendar ?? throw new Exception("Semester Calendar configuration not properly configured")
|
||||||
|
);
|
||||||
|
|
||||||
|
Console.WriteLine(JsonSerializer.Serialize(await BrowserStorage.GetAsync<SemesterCalendarConfig>(semesterConfigurationKey)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<PageTitle>Index</PageTitle>
|
<PageTitle>Index</PageTitle>
|
||||||
@@ -61,7 +86,7 @@
|
|||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
<lablel for="termselect">Select Term:</lablel>
|
<label for="termselect">Select Term:</label>
|
||||||
<select id="termselect" class="form-select" @bind="selectedTermId">
|
<select id="termselect" class="form-select" @bind="selectedTermId">
|
||||||
@foreach (var term in terms)
|
@foreach (var term in terms)
|
||||||
{
|
{
|
||||||
@@ -103,7 +128,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (configurationManagement.Configuration is not null)
|
@if (configurationManagement.SemesterCalendar is not null)
|
||||||
{
|
{
|
||||||
<div>Config complete</div>
|
<div>Config complete</div>
|
||||||
}
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
@page "/modules"
|
|
||||||
@using Management.Web.Shared.Module
|
|
||||||
@using System.Linq
|
|
||||||
@inject IModuleManager moduleManager
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private bool showNewModule { get; set; } = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
<PageTitle>Weather forecast</PageTitle>
|
|
||||||
|
|
||||||
@if (!showNewModule)
|
|
||||||
{
|
|
||||||
<button class="btn btn-secondary" @onclick="() => showNewModule = true">New Module</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<button class="btn btn-secondary" @onclick="() => showNewModule = false">Hide New Module</button>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (showNewModule)
|
|
||||||
{
|
|
||||||
<NewModule OnSubmit="() => showNewModule = false" />
|
|
||||||
}
|
|
||||||
|
|
||||||
@foreach (var i in moduleManager.Modules.Select((_value, i) => i))
|
|
||||||
{
|
|
||||||
<hr>
|
|
||||||
<ModuleDetail ModuleIndex="i" />
|
|
||||||
}
|
|
||||||
@@ -28,13 +28,24 @@
|
|||||||
<div class="bg-light border rounded m-3 p-3">
|
<div class="bg-light border rounded m-3 p-3">
|
||||||
<NewAssignment ModuleIndex="ModuleIndex" OnSubmit="() => showAddAssignment = false" />
|
<NewAssignment ModuleIndex="ModuleIndex" OnSubmit="() => showAddAssignment = false" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<h5>Assignments</h5>
|
<h5>Assignments</h5>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
@foreach (var assignment in module.Assignments)
|
@foreach (var assignment in module.Assignments)
|
||||||
{
|
{
|
||||||
<div>@assignment.name</div>
|
<div class="col-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-title">
|
||||||
|
@assignment.name
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
60
Management.Web/Shared/Module/Modules.razor
Normal file
60
Management.Web/Shared/Module/Modules.razor
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
@using Management.Web.Shared.Module
|
||||||
|
@using System.Linq
|
||||||
|
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||||
|
|
||||||
|
@inject IModuleManager moduleManager
|
||||||
|
@inject ProtectedLocalStorage BrowserStorage
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool showNewModule { get; set; } = false;
|
||||||
|
private string moduleStorageKey = "module storage key";
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
await BrowserStorage.SetAsync(moduleStorageKey, moduleManager.Modules);
|
||||||
|
|
||||||
|
}
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if(firstRender)
|
||||||
|
{
|
||||||
|
var storedModules = await BrowserStorage.GetAsync<IEnumerable<CourseModule>>(moduleStorageKey);
|
||||||
|
if (storedModules.Success) {
|
||||||
|
moduleManager.Modules = storedModules.Value ?? throw new Exception("stored modules was null, it shouldn't have been");
|
||||||
|
} else {
|
||||||
|
Console.WriteLine("no stored modules");
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!showNewModule)
|
||||||
|
{
|
||||||
|
<button class="btn btn-secondary" @onclick="() => showNewModule = true">New Module</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button class="btn btn-secondary" @onclick="() => showNewModule = false">Hide New Module</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (showNewModule)
|
||||||
|
{
|
||||||
|
<NewModule OnSubmit="() => showNewModule = false" />
|
||||||
|
}
|
||||||
|
|
||||||
|
@foreach (var i in moduleManager.Modules.Select((_value, i) => i))
|
||||||
|
{
|
||||||
|
<hr>
|
||||||
|
<ModuleDetail ModuleIndex="i" />
|
||||||
|
}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="text-center">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
@onclick="Save"
|
||||||
|
>
|
||||||
|
Save Modules
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
@@ -14,11 +14,6 @@
|
|||||||
<span class="oi oi-home" aria-hidden="true"></span> Home
|
<span class="oi oi-home" aria-hidden="true"></span> Home
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="/modules">
|
|
||||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Module Management
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="/calendar">
|
<NavLink class="nav-link" href="/calendar">
|
||||||
<span class="oi oi-plus" aria-hidden="true"></span> Calendar
|
<span class="oi oi-plus" aria-hidden="true"></span> Calendar
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public class SemesterPlanner
|
|||||||
|
|
||||||
public IEnumerable<CalendarMonth> Months { get; }
|
public IEnumerable<CalendarMonth> Months { get; }
|
||||||
public IEnumerable<DayOfWeek> Days { get; }
|
public IEnumerable<DayOfWeek> Days { get; }
|
||||||
public SemesterPlanner(SemesterConfiguration configuration)
|
public SemesterPlanner(SemesterCalendarConfig configuration)
|
||||||
{
|
{
|
||||||
FirstDay = configuration.StartDate;
|
FirstDay = configuration.StartDate;
|
||||||
LastDay = configuration.EndDate;
|
LastDay = configuration.EndDate;
|
||||||
|
|||||||
@@ -10,14 +10,15 @@ public class ConfigurationManagement : IConfigurationManagement
|
|||||||
var start = canvasTerm.StartAt ?? throw new Exception($"Canvas Term must have a start date. Term: {canvasTerm.Name}");
|
var start = canvasTerm.StartAt ?? throw new Exception($"Canvas Term must have a start date. Term: {canvasTerm.Name}");
|
||||||
var end = canvasTerm.EndAt ?? throw new Exception($"Canvas Term must have a end date. Term: {canvasTerm.Name}");
|
var end = canvasTerm.EndAt ?? throw new Exception($"Canvas Term must have a end date. Term: {canvasTerm.Name}");
|
||||||
|
|
||||||
Configuration = new SemesterConfiguration(
|
SemesterCalendar = new SemesterCalendarConfig(
|
||||||
StartDate: start,
|
StartDate: start,
|
||||||
EndDate: end,
|
EndDate: end,
|
||||||
Days: daysOfWeek
|
Days: daysOfWeek
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SemesterConfiguration? Configuration { get; private set; } = null;
|
public SemesterCalendarConfig? SemesterCalendar { get; set; } = null;
|
||||||
|
public IModuleManager ModuleManager {get; private set;} = new ModuleManager();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using CanvasModel.EnrollmentTerms;
|
|||||||
|
|
||||||
public interface IConfigurationManagement
|
public interface IConfigurationManagement
|
||||||
{
|
{
|
||||||
SemesterConfiguration? Configuration { get; }
|
SemesterCalendarConfig? SemesterCalendar { get; set; }
|
||||||
|
|
||||||
void SetConfiguration(EnrollmentTermModel canvasTerm, DayOfWeek[] daysOfWeek);
|
void SetConfiguration(EnrollmentTermModel canvasTerm, DayOfWeek[] daysOfWeek);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
public interface IModuleManager
|
public interface IModuleManager
|
||||||
{
|
{
|
||||||
IEnumerable<CourseModule> Modules { get; }
|
IEnumerable<CourseModule> Modules { get; set; }
|
||||||
public void AddModule(CourseModule newModule);
|
public void AddModule(CourseModule newModule);
|
||||||
public void AddAssignment(int moduleIndex, LocalAssignment assignment);
|
public void AddAssignment(int moduleIndex, LocalAssignment assignment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
public class ModuleManager : IModuleManager
|
public class ModuleManager : IModuleManager
|
||||||
{
|
{
|
||||||
public IEnumerable<CourseModule> Modules { get; internal set; } = new CourseModule[] { };
|
public IEnumerable<CourseModule> Modules { get; set; } = new CourseModule[] { };
|
||||||
|
|
||||||
public void AddAssignment(int moduleIndex, LocalAssignment assignment)
|
public void AddAssignment(int moduleIndex, LocalAssignment assignment)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ public record CourseModule(
|
|||||||
[property: Required]
|
[property: Required]
|
||||||
[property: StringLength(50, ErrorMessage = "Name too long (50 character limit).")]
|
[property: StringLength(50, ErrorMessage = "Name too long (50 character limit).")]
|
||||||
string Name,
|
string Name,
|
||||||
IEnumerable<LocalAssignment>? Assignments
|
IEnumerable<LocalAssignment>? Assignments = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
[JsonInclude]
|
||||||
public IEnumerable<LocalAssignment> Assignments = Assignments ?? new LocalAssignment[] { };
|
public IEnumerable<LocalAssignment> Assignments = Assignments ?? new LocalAssignment[] { };
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
public record SemesterConfiguration(
|
public record SemesterCalendarConfig(
|
||||||
DateTime StartDate,
|
DateTime StartDate,
|
||||||
DateTime EndDate,
|
DateTime EndDate,
|
||||||
IEnumerable<DayOfWeek> Days
|
IEnumerable<DayOfWeek> Days
|
||||||
|
|||||||
Reference in New Issue
Block a user