Added tag/creator status update functionality on the API and UI layers.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
using JSMR.Application.Circles.Queries.Search;
|
using JSMR.Application.Circles.Queries.Search;
|
||||||
|
using JSMR.Application.Creators.Commands.UpdateCreatorStatus;
|
||||||
using JSMR.Application.Creators.Queries.Search;
|
using JSMR.Application.Creators.Queries.Search;
|
||||||
|
using JSMR.Application.Tags.Commands.SetEnglishName;
|
||||||
|
using JSMR.Application.Tags.Commands.UpdateTagStatus;
|
||||||
using JSMR.Application.Tags.Queries.Search;
|
using JSMR.Application.Tags.Queries.Search;
|
||||||
using JSMR.Application.Users;
|
using JSMR.Application.Users;
|
||||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||||
@@ -56,6 +59,8 @@ public static class WebApplicationExtensions
|
|||||||
app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
|
app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
|
||||||
|
|
||||||
app.MapSearchEndpoints();
|
app.MapSearchEndpoints();
|
||||||
|
app.MapTagCommandEndpoints();
|
||||||
|
app.MapCreatorCommandEndpoints();
|
||||||
app.MapAuthenticationEndpoints();
|
app.MapAuthenticationEndpoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +110,39 @@ public static class WebApplicationExtensions
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void MapTagCommandEndpoints(this WebApplication app)
|
||||||
|
{
|
||||||
|
app.MapPost("/api/tags/update-status", async (
|
||||||
|
UpdateTagStatusRequest request,
|
||||||
|
UpdateTagStatusHandler handler,
|
||||||
|
CancellationToken ct) =>
|
||||||
|
{
|
||||||
|
var result = await handler.HandleAsync(request, ct);
|
||||||
|
return Results.Ok(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapPost("/api/tags/set-english-name", async (
|
||||||
|
SetTagEnglishNameRequest request,
|
||||||
|
SetTagEnglishNameHandler handler,
|
||||||
|
CancellationToken ct) =>
|
||||||
|
{
|
||||||
|
var result = await handler.HandleAsync(request, ct);
|
||||||
|
return Results.Ok(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MapCreatorCommandEndpoints(this WebApplication app)
|
||||||
|
{
|
||||||
|
app.MapPost("/api/creators/update-status", async (
|
||||||
|
UpdateCreatorStatusRequest request,
|
||||||
|
UpdateCreatorStatusHandler handler,
|
||||||
|
CancellationToken ct) =>
|
||||||
|
{
|
||||||
|
var result = await handler.HandleAsync(request, ct);
|
||||||
|
return Results.Ok(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static void MapAuthenticationEndpoints(this WebApplication app)
|
private static void MapAuthenticationEndpoints(this WebApplication app)
|
||||||
{
|
{
|
||||||
app.MapPost("/auth/login", async (LoginRequest req, IUserRepository users, HttpContext http) =>
|
app.MapPost("/auth/login", async (LoginRequest req, IUserRepository users, HttpContext http) =>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
public record CreatorSearchItem
|
public record CreatorSearchItem
|
||||||
{
|
{
|
||||||
public int CreatorId { get; init; }
|
public int CreatorId { get; set; }
|
||||||
public required string Name { get; init; }
|
public required string Name { get; set; }
|
||||||
public bool Favorite { get; init; }
|
public bool Favorite { get; set; }
|
||||||
public bool Blacklisted { get; init; }
|
public bool Blacklisted { get; set; }
|
||||||
public int VoiceWorkCount { get; init; }
|
public int VoiceWorkCount { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using JSMR.Application.Circles.Queries.Search;
|
using JSMR.Application.Circles.Queries.Search;
|
||||||
|
using JSMR.Application.Creators.Commands.UpdateCreatorStatus;
|
||||||
using JSMR.Application.Creators.Queries.Search;
|
using JSMR.Application.Creators.Queries.Search;
|
||||||
using JSMR.Application.Scanning;
|
using JSMR.Application.Scanning;
|
||||||
using JSMR.Application.Tags.Commands.SetEnglishName;
|
using JSMR.Application.Tags.Commands.SetEnglishName;
|
||||||
@@ -24,6 +25,7 @@ public static class ApplicationServiceCollectionExtensions
|
|||||||
services.AddScoped<UpdateTagStatusHandler>();
|
services.AddScoped<UpdateTagStatusHandler>();
|
||||||
|
|
||||||
services.AddScoped<SearchCreatorsHandler>();
|
services.AddScoped<SearchCreatorsHandler>();
|
||||||
|
services.AddScoped<UpdateCreatorStatusHandler>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
public record TagSearchItem
|
public record TagSearchItem
|
||||||
{
|
{
|
||||||
public int TagId { get; init; }
|
public int TagId { get; set; }
|
||||||
public required string Name { get; init; }
|
public required string Name { get; set; }
|
||||||
public bool Favorite { get; init; }
|
public bool Favorite { get; set; }
|
||||||
public bool Blacklisted { get; init; }
|
public bool Blacklisted { get; set; }
|
||||||
public string? EnglishName { get; init; }
|
public string? EnglishName { get; set; }
|
||||||
public int VoiceWorkCount { get; init; }
|
public int VoiceWorkCount { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@using JSMR.UI.Blazor.Components
|
@using AntDesign
|
||||||
|
@using JSMR.UI.Blazor.Components
|
||||||
@using JSMR.UI.Blazor.Services
|
@using JSMR.UI.Blazor.Services
|
||||||
|
|
||||||
@inject SessionState Session
|
@inject SessionState Session
|
||||||
@@ -6,6 +7,8 @@
|
|||||||
|
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
<AntContainer />
|
||||||
|
|
||||||
<MudLayout>
|
<MudLayout>
|
||||||
<MudAppBar Elevation="1" Dense="@_dense">
|
<MudAppBar Elevation="1" Dense="@_dense">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="MudBlazor.Color.Inherit" Edge="Edge.Start" OnClick="@ToggleDrawer" />
|
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="MudBlazor.Color.Inherit" Edge="Edge.Start" OnClick="@ToggleDrawer" />
|
||||||
@@ -40,6 +43,7 @@
|
|||||||
<MudMainContent Class="pt-18 px-8">
|
<MudMainContent Class="pt-18 px-8">
|
||||||
@Body
|
@Body
|
||||||
</MudMainContent>
|
</MudMainContent>
|
||||||
|
|
||||||
@* Required *@
|
@* Required *@
|
||||||
<MudThemeProvider @ref="_mudThemeProvider" @bind-IsDarkMode="_isDarkMode" />
|
<MudThemeProvider @ref="_mudThemeProvider" @bind-IsDarkMode="_isDarkMode" />
|
||||||
<MudPopoverProvider />
|
<MudPopoverProvider />
|
||||||
@@ -49,10 +53,10 @@
|
|||||||
|
|
||||||
@* Needed for snackbars *@
|
@* Needed for snackbars *@
|
||||||
<MudSnackbarProvider />
|
<MudSnackbarProvider />
|
||||||
|
|
||||||
</MudLayout>
|
</MudLayout>
|
||||||
|
|
||||||
<RadzenComponents @rendermode="RenderMode.InteractiveAuto" />
|
<RadzenComponents @rendermode="RenderMode.InteractiveAuto" />
|
||||||
<AntContainer />
|
|
||||||
|
|
||||||
@code{
|
@code{
|
||||||
private bool _open = false;
|
private bool _open = false;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
@page "/creators"
|
@page "/creators"
|
||||||
@inject VoiceWorksClient Client
|
@inject VoiceWorksClient Client
|
||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
|
@using AntDesign
|
||||||
@using JSMR.Application.Common.Search
|
@using JSMR.Application.Common.Search
|
||||||
|
@using JSMR.Application.Creators.Commands.UpdateCreatorStatus
|
||||||
|
@using JSMR.Application.Creators.Contracts
|
||||||
@using JSMR.Application.Creators.Queries.Search
|
@using JSMR.Application.Creators.Queries.Search
|
||||||
@using JSMR.Application.Creators.Queries.Search.Contracts
|
@using JSMR.Application.Creators.Queries.Search.Contracts
|
||||||
@using JSMR.UI.Blazor.Components
|
@using JSMR.UI.Blazor.Components
|
||||||
@@ -50,20 +53,29 @@
|
|||||||
<AntDesign.Tag Color="AntDesign.TagColor.RedInverse">Blacklisted</AntDesign.Tag>
|
<AntDesign.Tag Color="AntDesign.TagColor.RedInverse">Blacklisted</AntDesign.Tag>
|
||||||
}
|
}
|
||||||
</AntDesign.PropertyColumn>
|
</AntDesign.PropertyColumn>
|
||||||
@* <AntDesign.ActionColumn HeaderStyle="width: 2em">
|
<AntDesign.ActionColumn HeaderStyle="width: 5em;" Style="text-align: center">
|
||||||
<AntDesign.Dropdown Trigger="@(new AntDesign.Trigger[] { AntDesign.Trigger.Click })">
|
<Dropdown Trigger="@([Trigger.Click])">
|
||||||
<Overlay>
|
<Overlay>
|
||||||
<AntDesign.Menu>
|
<Menu Selectable="false">
|
||||||
<AntDesign.MenuItem>
|
@if (!context.Favorite)
|
||||||
<span>Some Menu Item</span>
|
{
|
||||||
</AntDesign.MenuItem>
|
<MenuItem OnClick="(e) => SetStatus(context, CreatorStatus.Favorite)">Set as Favorite</MenuItem>
|
||||||
</AntDesign.Menu>
|
}
|
||||||
|
@if (!context.Blacklisted)
|
||||||
|
{
|
||||||
|
<MenuItem OnClick="(e) => SetStatus(context, CreatorStatus.Blacklisted)">Set as Blacklisted</MenuItem>
|
||||||
|
}
|
||||||
|
@if (context.Favorite || context.Blacklisted)
|
||||||
|
{
|
||||||
|
<MenuItem OnClick="(e) => SetStatus(context, CreatorStatus.Neutral)">Set as Neutral</MenuItem>
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<AntDesign.Button Shape="AntDesign.ButtonShape.Circle" Icon="@AntDesign.IconType.Outline.More" Type="AntDesign.ButtonType.Default"></AntDesign.Button>
|
<Button Icon="Ellipsis"></Button>
|
||||||
</ChildContent>
|
</ChildContent>
|
||||||
</AntDesign.Dropdown>
|
</Dropdown>
|
||||||
</AntDesign.ActionColumn> *@
|
</AntDesign.ActionColumn>
|
||||||
</ColumnDefinitions>
|
</ColumnDefinitions>
|
||||||
</AntDesign.Table>
|
</AntDesign.Table>
|
||||||
<JPagination2 PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" />
|
<JPagination2 PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" />
|
||||||
@@ -119,6 +131,9 @@
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected NavigationManager NavigationManager { get; set; } = default!;
|
protected NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
INotificationService NotificationService { get; set; } = default!;
|
||||||
|
|
||||||
public string? Keywords { get; set; }
|
public string? Keywords { get; set; }
|
||||||
public int PageNumber { get; set; } = 1;
|
public int PageNumber { get; set; } = 1;
|
||||||
public int PageSize { get; set; } = 100;
|
public int PageSize { get; set; } = 100;
|
||||||
@@ -259,4 +274,31 @@
|
|||||||
|
|
||||||
NavigationManager.NavigateTo(uri);
|
NavigationManager.NavigateTo(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SetStatus(CreatorSearchItem item, CreatorStatus status)
|
||||||
|
{
|
||||||
|
UpdateCreatorStatusRequest request = new(
|
||||||
|
CreatorId: item.CreatorId,
|
||||||
|
CreatorStatus: status
|
||||||
|
);
|
||||||
|
|
||||||
|
UpdateCreatorStatusResponse? response = await Client.UpdateCreatorStatusAsync(request);
|
||||||
|
|
||||||
|
if (response is not null)
|
||||||
|
{
|
||||||
|
item.Favorite = response.CreatorStatus is CreatorStatus.Favorite;
|
||||||
|
item.Blacklisted = response.CreatorStatus is CreatorStatus.Blacklisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
var config = new NotificationConfig()
|
||||||
|
{
|
||||||
|
Message = $"Creator Status Update",
|
||||||
|
Description = $"Creator '{item.Name}' set to {status.ToString()}.",
|
||||||
|
Placement = NotificationPlacement.Top
|
||||||
|
};
|
||||||
|
|
||||||
|
await NotificationService.Open(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
@inject IJSRuntime JS
|
@inject IJSRuntime JS
|
||||||
@using AntDesign
|
@using AntDesign
|
||||||
@using JSMR.Application.Common.Search
|
@using JSMR.Application.Common.Search
|
||||||
|
@using JSMR.Application.Tags.Commands.UpdateTagStatus
|
||||||
|
@using JSMR.Application.Tags.Contracts
|
||||||
@using JSMR.Application.Tags.Queries.Search
|
@using JSMR.Application.Tags.Queries.Search
|
||||||
@using JSMR.Application.Tags.Queries.Search.Contracts
|
@using JSMR.Application.Tags.Queries.Search.Contracts
|
||||||
@using JSMR.UI.Blazor.Components
|
@using JSMR.UI.Blazor.Components
|
||||||
@@ -13,10 +15,6 @@
|
|||||||
<PageTitle>Tags</PageTitle>
|
<PageTitle>Tags</PageTitle>
|
||||||
|
|
||||||
<div class="fdsfds">
|
<div class="fdsfds">
|
||||||
@* <h1>Tags</h1> *@
|
|
||||||
|
|
||||||
@* <MudTextField T="string" Value="Keywords" ValueChanged="OnKeywordsChanged" Immediate="true" DebounceInterval="500" Label="Filter" Variant="MudBlazor.Variant.Text" Adornment="@Adornment.Start" AdornmentIcon="@Icons.Material.Outlined.Search" />
|
|
||||||
*@
|
|
||||||
<AntDesign.Card Title=@("Tags") Class="ant-blurred-card">
|
<AntDesign.Card Title=@("Tags") Class="ant-blurred-card">
|
||||||
<Extra>
|
<Extra>
|
||||||
<AntDesign.Input TValue="string" Value="Keywords" ValueChanged="OnKeywordsChanged" DebounceMilliseconds="500" Placeholder="Filter">
|
<AntDesign.Input TValue="string" Value="Keywords" ValueChanged="OnKeywordsChanged" DebounceMilliseconds="500" Placeholder="Filter">
|
||||||
@@ -53,14 +51,34 @@
|
|||||||
<AntDesign.Tag Color="AntDesign.TagColor.RedInverse">Blacklisted</AntDesign.Tag>
|
<AntDesign.Tag Color="AntDesign.TagColor.RedInverse">Blacklisted</AntDesign.Tag>
|
||||||
}
|
}
|
||||||
</AntDesign.PropertyColumn>
|
</AntDesign.PropertyColumn>
|
||||||
|
<AntDesign.ActionColumn HeaderStyle="width: 5em;" Style="text-align: center">
|
||||||
|
<Dropdown Trigger="@([Trigger.Click])">
|
||||||
|
<Overlay>
|
||||||
|
<Menu Selectable="false">
|
||||||
|
@if (!context.Favorite)
|
||||||
|
{
|
||||||
|
<MenuItem OnClick="(e) => SetStatus(context, TagStatus.Favorite)">Set as Favorite</MenuItem>
|
||||||
|
}
|
||||||
|
@if (!context.Blacklisted)
|
||||||
|
{
|
||||||
|
<MenuItem OnClick="(e) => SetStatus(context, TagStatus.Blacklisted)">Set as Blacklisted</MenuItem>
|
||||||
|
}
|
||||||
|
@if (context.Favorite || context.Blacklisted)
|
||||||
|
{
|
||||||
|
<MenuItem OnClick="(e) => SetStatus(context, TagStatus.Neutral)">Set as Neutral</MenuItem>
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
</Overlay>
|
||||||
|
<ChildContent>
|
||||||
|
<Button Icon="Ellipsis"></Button>
|
||||||
|
</ChildContent>
|
||||||
|
</Dropdown>
|
||||||
|
</AntDesign.ActionColumn>
|
||||||
</ColumnDefinitions>
|
</ColumnDefinitions>
|
||||||
</AntDesign.Table>
|
</AntDesign.Table>
|
||||||
<JPagination2 PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" />
|
<JPagination2 PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" />
|
||||||
</Body>
|
</Body>
|
||||||
</AntDesign.Card>
|
</AntDesign.Card>
|
||||||
|
|
||||||
@* <Pagination Current="PageNumber" OnChange="OnPaginationChange" PageSize="PageSize" Total="TotalItems" ShowTotal="ShowTotal"></Pagination>
|
|
||||||
<JPagination PageNumber="PageNumber" PageNumberChanged="OnPageNumberChanged" PageSize="PageSize" PageSizeChanged="OnPageSizeChanged" TotalItems="TotalItems" /> *@
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -124,6 +142,9 @@
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected NavigationManager NavigationManager { get; set; } = default!;
|
protected NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
INotificationService NotificationService { get; set; } = default!;
|
||||||
|
|
||||||
public string? Keywords { get; set; }
|
public string? Keywords { get; set; }
|
||||||
public int PageNumber { get; set; } = 1;
|
public int PageNumber { get; set; } = 1;
|
||||||
public int PageSize { get; set; } = 100;
|
public int PageSize { get; set; } = 100;
|
||||||
@@ -280,4 +301,31 @@
|
|||||||
|
|
||||||
NavigationManager.NavigateTo(uri);
|
NavigationManager.NavigateTo(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SetStatus(TagSearchItem item, TagStatus status)
|
||||||
|
{
|
||||||
|
UpdateTagStatusRequest request = new(
|
||||||
|
TagId: item.TagId,
|
||||||
|
TagStatus: status
|
||||||
|
);
|
||||||
|
|
||||||
|
UpdateTagStatusResponse? response = await Client.UpdateTagStatusAsync(request);
|
||||||
|
|
||||||
|
if (response is not null)
|
||||||
|
{
|
||||||
|
item.Favorite = response.TagStatus is TagStatus.Favorite;
|
||||||
|
item.Blacklisted = response.TagStatus is TagStatus.Blacklisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
var config = new NotificationConfig()
|
||||||
|
{
|
||||||
|
Message = $"Tag Status Update",
|
||||||
|
Description = $"Tag '{item.Name}' set to {status.ToString()}.",
|
||||||
|
Placement = NotificationPlacement.Top
|
||||||
|
};
|
||||||
|
|
||||||
|
await NotificationService.Open(config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using JSMR.Application.Circles.Queries.Search;
|
using JSMR.Application.Circles.Queries.Search;
|
||||||
|
using JSMR.Application.Creators.Commands.UpdateCreatorStatus;
|
||||||
using JSMR.Application.Creators.Queries.Search;
|
using JSMR.Application.Creators.Queries.Search;
|
||||||
|
using JSMR.Application.Tags.Commands.UpdateTagStatus;
|
||||||
using JSMR.Application.Tags.Queries.Search;
|
using JSMR.Application.Tags.Queries.Search;
|
||||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
@@ -41,4 +43,16 @@ public class VoiceWorksClient(HttpClient http)
|
|||||||
using var resp = await http.PostAsJsonAsync("/api/tags/search", request, ct);
|
using var resp = await http.PostAsJsonAsync("/api/tags/search", request, ct);
|
||||||
return await resp.Content.ReadFromJsonAsync<SearchTagsResponse>(JsonOptions, cancellationToken: ct);
|
return await resp.Content.ReadFromJsonAsync<SearchTagsResponse>(JsonOptions, cancellationToken: ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UpdateTagStatusResponse?> UpdateTagStatusAsync(UpdateTagStatusRequest request, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
using var resp = await http.PostAsJsonAsync("/api/tags/update-status", request, ct);
|
||||||
|
return await resp.Content.ReadFromJsonAsync<UpdateTagStatusResponse>(JsonOptions, cancellationToken: ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UpdateCreatorStatusResponse?> UpdateCreatorStatusAsync(UpdateCreatorStatusRequest request, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
using var resp = await http.PostAsJsonAsync("/api/creators/update-status", request, ct);
|
||||||
|
return await resp.Content.ReadFromJsonAsync<UpdateCreatorStatusResponse>(JsonOptions, cancellationToken: ct);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user