18b61bed52
- Widen AdminCandidateEditorModal to size lg for better readability - Rename "Clip-Compilation" section to "Clip / Compilation", update copy to reflect single clips too, drop upload hint and Clip-Plattform field, rename label to "Link" - Fix NativeSelect dropdown clipping inside overflow-y-auto modals by teleporting the menu to body with fixed positioning, flip-up logic, and dynamic maxHeight capped to viewport - Add ClipAdminMenuVisible setting (backend domain, contracts, endpoint, migration) with matching frontend types, defaults, form wiring, and toggle in the Clip-Workflow modal — hides the Clips nav item from the admin sidebar when disabled Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
170 lines
5.9 KiB
C#
170 lines
5.9 KiB
C#
using Backend.Contracts;
|
|
using Backend.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Backend.Endpoints;
|
|
|
|
public static partial class AdminSeasonManagementEndpoints
|
|
{
|
|
private static async Task<IResult> GetSeasonDetail(int seasonId, AwardsDbContext db)
|
|
{
|
|
var season = await db.Seasons
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(item => item.Id == seasonId);
|
|
|
|
if (season is null)
|
|
{
|
|
return Results.NotFound();
|
|
}
|
|
|
|
var candidates = await db.Candidates
|
|
.AsNoTracking()
|
|
.Where(item => item.SeasonId == seasonId)
|
|
.OrderBy(item => item.DisplayName)
|
|
.Select(item => new AdminCandidateItemDto(
|
|
item.Id,
|
|
item.CategoryId,
|
|
item.DisplayName,
|
|
item.ChannelSlug,
|
|
item.Platform,
|
|
item.AcceptanceStatus,
|
|
item.AcceptanceNote,
|
|
item.ClipCompilationUrl,
|
|
item.ClipCompilationTitle,
|
|
item.ClipCompilationPlatform,
|
|
item.ClipEmbedStatus))
|
|
.ToArrayAsync();
|
|
|
|
var candidateCounts = candidates
|
|
.GroupBy(item => item.CategoryId)
|
|
.ToDictionary(grouping => grouping.Key, grouping => grouping.Count());
|
|
|
|
var categoryRows = await db.Categories
|
|
.AsNoTracking()
|
|
.Where(item => item.SeasonId == seasonId)
|
|
.OrderBy(item => item.SortOrder)
|
|
.ThenBy(item => item.Name)
|
|
.Select(category => new
|
|
{
|
|
category.Id,
|
|
category.GroupName,
|
|
category.Name,
|
|
category.Slug,
|
|
category.Description,
|
|
category.SortOrder,
|
|
category.MaxNomineesPerUser,
|
|
})
|
|
.ToArrayAsync();
|
|
|
|
var categories = categoryRows
|
|
.Select(category => new AdminCategoryItemDto(
|
|
category.Id,
|
|
category.GroupName,
|
|
category.Name,
|
|
category.Slug,
|
|
category.Description,
|
|
category.SortOrder,
|
|
category.MaxNomineesPerUser,
|
|
candidateCounts.TryGetValue(category.Id, out var count) ? count : 0))
|
|
.ToArray();
|
|
|
|
var pendingNominations = await db.Nominations
|
|
.AsNoTracking()
|
|
.Where(item => item.SeasonId == seasonId && item.Status == "pending")
|
|
.OrderByDescending(item => item.CreatedAt)
|
|
.Select(item => new AdminNominationReviewItemDto(
|
|
item.Id,
|
|
item.CategoryId,
|
|
item.Category.Name,
|
|
item.SubmittedByTwitchId,
|
|
item.CandidateText ?? string.Empty,
|
|
item.StreamUrl,
|
|
item.Status,
|
|
item.CreatedAt,
|
|
item.CandidateId,
|
|
item.CandidateId != null ? item.Candidate!.DisplayName : null,
|
|
item.ReviewNote,
|
|
item.ReviewedByTwitchId,
|
|
item.ReviewedAt))
|
|
.ToArrayAsync();
|
|
|
|
var reviewedNominations = await db.Nominations
|
|
.AsNoTracking()
|
|
.Where(item => item.SeasonId == seasonId && item.Status != "pending")
|
|
.OrderByDescending(item => item.ReviewedAt ?? item.CreatedAt)
|
|
.Select(item => new AdminNominationReviewItemDto(
|
|
item.Id,
|
|
item.CategoryId,
|
|
item.Category.Name,
|
|
item.SubmittedByTwitchId,
|
|
item.CandidateText ?? (item.CandidateId != null ? item.Candidate!.DisplayName : string.Empty),
|
|
item.StreamUrl,
|
|
item.Status,
|
|
item.CreatedAt,
|
|
item.CandidateId,
|
|
item.CandidateId != null ? item.Candidate!.DisplayName : null,
|
|
item.ReviewNote,
|
|
item.ReviewedByTwitchId,
|
|
item.ReviewedAt))
|
|
.ToArrayAsync();
|
|
|
|
var resultItems = await db.Results
|
|
.AsNoTracking()
|
|
.Where(item => item.SeasonId == seasonId)
|
|
.OrderBy(item => item.Category.SortOrder)
|
|
.ThenBy(item => item.Category.Name)
|
|
.Select(item => new AdminAwardResultItemDto(
|
|
item.Id,
|
|
item.CategoryId,
|
|
item.Category.Name,
|
|
item.CandidateId,
|
|
item.Candidate.DisplayName,
|
|
item.Candidate.ChannelSlug,
|
|
item.Candidate.Platform))
|
|
.ToArrayAsync();
|
|
|
|
var clipSubmissions = await db.ClipSubmissions
|
|
.AsNoTracking()
|
|
.Where(item => item.SeasonId == seasonId)
|
|
.OrderByDescending(item => item.CreatedAt)
|
|
.Select(item => new AdminClipSubmissionItemDto(
|
|
item.Id,
|
|
item.CategoryId,
|
|
item.CandidateId,
|
|
item.SubmittedByTwitchId,
|
|
item.ClipUrl,
|
|
item.Title,
|
|
item.Creator,
|
|
item.Platform,
|
|
item.Status,
|
|
item.CreatedAt,
|
|
item.ReviewNote,
|
|
item.ReviewedByTwitchId,
|
|
item.ReviewedAt))
|
|
.ToArrayAsync();
|
|
|
|
return Results.Ok(new AdminSeasonDetailResponse(
|
|
season.Id,
|
|
season.Year,
|
|
season.Name,
|
|
NormalizeSeasonStreamUrl(season.ShowStreamUrl),
|
|
season.CurrentPhase,
|
|
season.IsCurrent,
|
|
season.IsCommunityOnly,
|
|
season.NominationStartsAt,
|
|
season.NominationEndsAt,
|
|
season.VotingStartsAt,
|
|
season.VotingEndsAt,
|
|
season.ReviewStartsAt,
|
|
season.ReviewEndsAt,
|
|
season.ShowDate,
|
|
season.ShowStartsAt,
|
|
categories,
|
|
candidates,
|
|
pendingNominations,
|
|
reviewedNominations,
|
|
resultItems,
|
|
clipSubmissions));
|
|
}
|
|
}
|