add zombie siege, feedback changes

This commit is contained in:
Tomasi - Developing 2024-11-28 13:16:41 +01:00
parent 35e83c4735
commit 6c33a2725d
51 changed files with 2861 additions and 68 deletions

View File

@ -0,0 +1,108 @@
using Application.DataTransferObjects.ZombieSiegeParticipant;
using Application.Errors;
using Application.Interfaces;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Api.Controllers.v1
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("1.0")]
[Authorize]
public class ZombieSiegeParticipantsController(IZombieSiegeParticipantRepository zombieSiegeParticipantRepository, ILogger<ZombieSiegeParticipantsController> logger) : ControllerBase
{
[HttpGet("{zombieSiegeParticipantId:guid}")]
public async Task<ActionResult<ZombieSiegeParticipantDto>> GetZombieSiegeParticipant(
Guid zombieSiegeParticipantId, CancellationToken cancellationToken)
{
try
{
var zombieSiegeParticipantResult =
await zombieSiegeParticipantRepository.GetZombieSiegeParticipantAsync(zombieSiegeParticipantId,
cancellationToken);
return zombieSiegeParticipantResult.IsFailure
? BadRequest(zombieSiegeParticipantResult.Error)
: Ok(zombieSiegeParticipantResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("Player/{playerId:guid}")]
public async Task<ActionResult<List<ZombieSiegeParticipantDto>>> GetPlayerZombieSiegeParticipants(Guid playerId, [FromQuery] int last,
CancellationToken cancellationToken)
{
try
{
var playerZombieSiegeParticipantsResult =
await zombieSiegeParticipantRepository.GetPlayerZombieSiegeParticipantsAsync(playerId, last,
cancellationToken);
return playerZombieSiegeParticipantsResult.IsFailure
? BadRequest(playerZombieSiegeParticipantsResult.Error)
: Ok(playerZombieSiegeParticipantsResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost]
public async Task<ActionResult<bool>> InsertZombieSiegeParticipants(
List<CreateZombieSiegeParticipantDto> createZombieSiegeParticipants, CancellationToken cancellationToken)
{
try
{
if (!ModelState.IsValid) return UnprocessableEntity(ModelState);
var createResult =
await zombieSiegeParticipantRepository.InsertZombieSiegeParticipantsAsync(
createZombieSiegeParticipants, cancellationToken);
return createResult.IsFailure
? BadRequest(createResult.Error)
: Ok(createResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPut("{zombieSiegeParticipantId:guid}")]
public async Task<ActionResult<ZombieSiegeParticipantDto>> UpdateZombieSiegeParticipant(
Guid zombieSiegeParticipantId, UpdateZombieSiegeParticipantDto updateZombieSiegeParticipant,
CancellationToken cancellationToken)
{
try
{
if (!ModelState.IsValid) return UnprocessableEntity(ModelState);
if (zombieSiegeParticipantId != updateZombieSiegeParticipant.Id)
return Conflict(ZombieSiegeParticipantErrors.IdConflict);
var updateResult =
await zombieSiegeParticipantRepository.UpdateZombieSiegeParticipantAsync(
updateZombieSiegeParticipant, cancellationToken);
return updateResult.IsFailure
? BadRequest(updateResult.Error)
: Ok(updateResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
}

View File

@ -0,0 +1,143 @@
using Application.DataTransferObjects.ZombieSiege;
using Application.Errors;
using Application.Interfaces;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Api.Controllers.v1
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("1.0")]
[Authorize]
public class ZombieSiegesController(IZombieSiegeRepository zombieSiegeRepository, ILogger<ZombieSiegesController> logger, IClaimTypeService claimTypeService) : ControllerBase
{
[HttpGet("{zombieSiegeId:guid}")]
public async Task<ActionResult<ZombieSiegeDto>> GetZombieSiege(Guid zombieSiegeId,
CancellationToken cancellationToken)
{
try
{
var zombieSiegeResult =
await zombieSiegeRepository.GetZombieSiegeAsync(zombieSiegeId, cancellationToken);
return zombieSiegeResult.IsFailure
? BadRequest(zombieSiegeResult.Error)
: Ok(zombieSiegeResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("Alliance/{allianceId:guid}")]
public async Task<ActionResult<List<ZombieSiegeDto>>> GetAllianceZombieSieges(Guid allianceId, [FromQuery] int take,
CancellationToken cancellationToken)
{
try
{
var allianceZombieSiegesResult =
await zombieSiegeRepository.GetAllianceZombieSiegesAsync(allianceId, take, cancellationToken);
if (allianceZombieSiegesResult.IsFailure) return BadRequest(allianceZombieSiegesResult.Error);
return allianceZombieSiegesResult.Value.Count > 0
? Ok(allianceZombieSiegesResult.Value)
: NoContent();
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("[action]/{zombieSiegeId:guid}")]
public async Task<ActionResult<ZombieSiegeDetailDto>> GetZombieSiegeDetail(Guid zombieSiegeId,
CancellationToken cancellationToken)
{
try
{
var zombieSiegeDetailResult =
await zombieSiegeRepository.GetZombieSiegeDetailAsync(zombieSiegeId, cancellationToken);
return zombieSiegeDetailResult.IsFailure
? BadRequest(zombieSiegeDetailResult.Error)
: Ok(zombieSiegeDetailResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost]
public async Task<ActionResult<ZombieSiegeDto>> CreateZombieSiege(CreateZombieSiegeDto createZombieSiegeDto,
CancellationToken cancellationToken)
{
try
{
if (!ModelState.IsValid) return UnprocessableEntity(ModelState);
var createResult =
await zombieSiegeRepository.CreateZombieSiegeAsync(createZombieSiegeDto, claimTypeService.GetFullName(User), cancellationToken);
return createResult.IsFailure
? BadRequest(createResult.Error)
: CreatedAtAction(nameof(GetZombieSiege),
new { zombieSiegeId = createResult.Value.Id }, createResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPut("{zombieSiegeId:guid}")]
public async Task<ActionResult<ZombieSiegeDto>> UpdateZombieSiege(Guid zombieSiegeId, UpdateZombieSiegeDto updateZombieSiegeDto,
CancellationToken cancellationToken)
{
try
{
if (!ModelState.IsValid) return UnprocessableEntity(ModelState);
if (zombieSiegeId != updateZombieSiegeDto.Id) return Conflict(ZombieSiegeErrors.IdConflict);
var updateResult = await zombieSiegeRepository.UpdateZombieSiegeAsync(updateZombieSiegeDto,
claimTypeService.GetFullName(User), cancellationToken);
return updateResult.IsFailure
? BadRequest(updateResult.Error)
: Ok(updateResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpDelete("{zombieSiegeId:guid}")]
public async Task<ActionResult<bool>> DeleteZombieSiege(Guid zombieSiegeId, CancellationToken cancellationToken)
{
try
{
var deleteResult = await zombieSiegeRepository.DeleteZombieSiegeAsync(zombieSiegeId, cancellationToken);
return deleteResult.IsFailure
? BadRequest(deleteResult.Error)
: Ok(deleteResult.Value);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
}

View File

@ -11,6 +11,7 @@
<Folder Include="Classes\" /> <Folder Include="Classes\" />
<Folder Include="DataTransferObjects\MarshalGuardParticipant\" /> <Folder Include="DataTransferObjects\MarshalGuardParticipant\" />
<Folder Include="DataTransferObjects\CustomEventParticipant\" /> <Folder Include="DataTransferObjects\CustomEventParticipant\" />
<Folder Include="DataTransferObjects\ZombieSiegeParticipant\" />
<Folder Include="DataTransferObjects\Rank\" /> <Folder Include="DataTransferObjects\Rank\" />
<Folder Include="DataTransferObjects\Note\" /> <Folder Include="DataTransferObjects\Note\" />
</ItemGroup> </ItemGroup>

View File

@ -28,6 +28,8 @@ public static class ApplicationDependencyInjection
services.AddScoped<IVsDuelParticipantRepository, VsDuelParticipantRepository>(); services.AddScoped<IVsDuelParticipantRepository, VsDuelParticipantRepository>();
services.AddScoped<IUserRepository, UserRepository>(); services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IDesertStormParticipantRepository, DesertStormParticipantRepository>(); services.AddScoped<IDesertStormParticipantRepository, DesertStormParticipantRepository>();
services.AddScoped<IZombieSiegeRepository, ZombieSiegeRepository>();
services.AddScoped<IZombieSiegeParticipantRepository, ZombieSiegeParticipantRepository>();
services.AddTransient<IJwtService, JwtService>(); services.AddTransient<IJwtService, JwtService>();

View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Application.DataTransferObjects.ZombieSiege;
public class CreateZombieSiegeDto
{
[Required]
public Guid AllianceId { get; set; }
[Required]
public int AllianceSize { get; set; }
[Required]
public int Level { get; set; }
[Required]
public required string EventDate { get; set; }
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
namespace Application.DataTransferObjects.ZombieSiege;
public class UpdateZombieSiegeDto
{
[Required]
public Guid Id { get; set; }
[Required]
public Guid AllianceId { get; set; }
[Required]
public int AllianceSize { get; set; }
[Required]
public int Level { get; set; }
[Required]
public required string EventDate { get; set; }
}

View File

@ -0,0 +1,8 @@
using Application.DataTransferObjects.ZombieSiegeParticipant;
namespace Application.DataTransferObjects.ZombieSiege;
public class ZombieSiegeDetailDto : ZombieSiegeDto
{
public ICollection<ZombieSiegeParticipantDto> ZombieSiegeParticipants { get; set; } = [];
}

View File

@ -0,0 +1,22 @@
namespace Application.DataTransferObjects.ZombieSiege;
public class ZombieSiegeDto
{
public Guid Id { get; set; }
public int Level { get; set; }
public int TotalLevel20Players { get; set; }
public Guid AllianceId { get; set; }
public DateTime EventDate { get; set; }
public required string CreatedBy { get; set; }
public DateTime? ModifiedOn { get; set; }
public string? ModifiedBy { get; set; }
public int AllianceSize { get; set; }
}

View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
namespace Application.DataTransferObjects.ZombieSiegeParticipant;
public class CreateZombieSiegeParticipantDto
{
[Required]
public Guid PlayerId { get; set; }
[Required]
public Guid ZombieSiegeId { get; set; }
[Required]
public int SurvivedWaves { get; set; }
}

View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Application.DataTransferObjects.ZombieSiegeParticipant;
public class UpdateZombieSiegeParticipantDto
{
[Required]
public Guid Id { get; set; }
[Required]
public Guid PlayerId { get; set; }
[Required]
public Guid ZombieSiegeId { get; set; }
[Required]
public int SurvivedWaves { get; set; }
}

View File

@ -0,0 +1,14 @@
namespace Application.DataTransferObjects.ZombieSiegeParticipant;
public class ZombieSiegeParticipantDto
{
public Guid Id { get; set; }
public Guid PlayerId { get; set; }
public required string PlayerName { get; set; }
public Guid ZombieSiegeId { get; set; }
public int SurvivedWaves { get; set; }
}

View File

@ -0,0 +1,9 @@
namespace Application.Errors;
public static class ZombieSiegeErrors
{
public static readonly Error NotFound = new("Error.ZombieSiege.NotFound",
"The zombie siege with the specified identifier was not found");
public static readonly Error IdConflict = new("Error.ZombieSiege.IdConflict", "There is a conflict with the id's");
}

View File

@ -0,0 +1,9 @@
namespace Application.Errors;
public static class ZombieSiegeParticipantErrors
{
public static readonly Error NotFound = new("Error.ZombieSiegeParticipant.NotFound",
"The zombie siege participant with the specified identifier was not found");
public static readonly Error IdConflict = new("Error.ZombieSiegeParticipant.IdConflict", "There is a conflict with the id's");
}

View File

@ -0,0 +1,15 @@
using Application.Classes;
using Application.DataTransferObjects.ZombieSiegeParticipant;
namespace Application.Interfaces;
public interface IZombieSiegeParticipantRepository
{
Task<Result<ZombieSiegeParticipantDto>> GetZombieSiegeParticipantAsync(Guid zombieSiegeParticipantId, CancellationToken cancellationToken);
Task<Result<bool>> InsertZombieSiegeParticipantsAsync(List<CreateZombieSiegeParticipantDto> createZombieSiegeParticipants, CancellationToken cancellationToken);
Task<Result<List<ZombieSiegeParticipantDto>>> GetPlayerZombieSiegeParticipantsAsync(Guid playerId, int last, CancellationToken cancellationToken);
Task<Result<ZombieSiegeParticipantDto>> UpdateZombieSiegeParticipantAsync(UpdateZombieSiegeParticipantDto updateZombieSiegeParticipantDto, CancellationToken cancellationToken);
}

View File

@ -0,0 +1,19 @@
using Application.Classes;
using Application.DataTransferObjects.ZombieSiege;
namespace Application.Interfaces;
public interface IZombieSiegeRepository
{
Task<Result<ZombieSiegeDto>> GetZombieSiegeAsync(Guid zombieSiegeId, CancellationToken cancellationToken);
Task<Result<ZombieSiegeDetailDto>> GetZombieSiegeDetailAsync(Guid zombieSiegeId, CancellationToken cancellationToken);
Task<Result<List<ZombieSiegeDto>>> GetAllianceZombieSiegesAsync(Guid allianceId, int take, CancellationToken cancellationToken);
Task<Result<ZombieSiegeDto>> CreateZombieSiegeAsync(CreateZombieSiegeDto createZombieSiegeDto, string createdBy, CancellationToken cancellationToken);
Task<Result<ZombieSiegeDto>> UpdateZombieSiegeAsync(UpdateZombieSiegeDto updateZombieSiegeDto, string modifiedBy, CancellationToken cancellationToken);
Task<Result<bool>> DeleteZombieSiegeAsync(Guid zombieSiegeId, CancellationToken cancellationToken);
}

View File

@ -0,0 +1,19 @@
using Application.DataTransferObjects.ZombieSiegeParticipant;
using AutoMapper;
using Database.Entities;
namespace Application.Profiles;
public class ZombieSiegeParticipantProfile : Profile
{
public ZombieSiegeParticipantProfile()
{
CreateMap<ZombieSiegeParticipant, ZombieSiegeParticipantDto>()
.ForMember(des => des.PlayerName, opt => opt.MapFrom(src => src.Player.PlayerName));
CreateMap<CreateZombieSiegeParticipantDto, ZombieSiegeParticipant>()
.ForMember(des => des.Id, opt => opt.MapFrom(src => Guid.CreateVersion7()));
CreateMap<UpdateZombieSiegeParticipantDto, ZombieSiegeParticipant>();
}
}

View File

@ -0,0 +1,27 @@
using Application.DataTransferObjects.ZombieSiege;
using AutoMapper;
using Database.Entities;
namespace Application.Profiles;
public class ZombieSiegeProfile : Profile
{
public ZombieSiegeProfile()
{
CreateMap<ZombieSiege, ZombieSiegeDto>()
.ForMember(des => des.TotalLevel20Players,
opt => opt.MapFrom(src => src.ZombieSiegeParticipants.Count(p => p.SurvivedWaves == 20)));
CreateMap<ZombieSiege, ZombieSiegeDetailDto>()
.ForMember(des => des.TotalLevel20Players,
opt => opt.MapFrom(src => src.ZombieSiegeParticipants.Count(p => p.SurvivedWaves == 20)));
CreateMap<CreateZombieSiegeDto, ZombieSiege>()
.ForMember(des => des.Id, opt => opt.MapFrom(src => Guid.CreateVersion7()))
.ForMember(des => des.EventDate, opt => opt.MapFrom(src => DateTime.Parse(src.EventDate)));
CreateMap<UpdateZombieSiegeDto, ZombieSiege>()
.ForMember(des => des.EventDate, opt => opt.MapFrom(src => DateTime.Parse(src.EventDate)))
.ForMember(des => des.ModifiedOn, opt => opt.MapFrom(src => DateTime.Now));
}
}

View File

@ -108,6 +108,12 @@ public class PlayerRepository(ApplicationContext context, IMapper mapper, ILogge
if (vsDuels.Count > 0) context.MarshalGuardParticipants.RemoveRange(marshalGuards); if (vsDuels.Count > 0) context.MarshalGuardParticipants.RemoveRange(marshalGuards);
var zombieSieges = await context.ZombieSiegeParticipants
.Where(zombieSiege => zombieSiege.PlayerId == playerToDelete.Id)
.ToListAsync(cancellationToken);
if (zombieSieges.Count > 0) context.ZombieSiegeParticipants.RemoveRange(zombieSieges);
context.Players.Remove(playerToDelete); context.Players.Remove(playerToDelete);
try try

View File

@ -0,0 +1,83 @@
using Application.Classes;
using Application.DataTransferObjects.ZombieSiegeParticipant;
using Application.Errors;
using Application.Interfaces;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Database;
using Database.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Application.Repositories;
public class ZombieSiegeParticipantRepository(ApplicationContext context, IMapper mapper, ILogger<ZombieSiegeParticipantRepository> logger) : IZombieSiegeParticipantRepository
{
public async Task<Result<ZombieSiegeParticipantDto>> GetZombieSiegeParticipantAsync(Guid zombieSiegeParticipantId, CancellationToken cancellationToken)
{
var zombieSiegeParticipantById = await context.ZombieSiegeParticipants
.ProjectTo<ZombieSiegeParticipantDto>(mapper.ConfigurationProvider)
.AsNoTracking()
.FirstOrDefaultAsync(zombieSiegeParticipant => zombieSiegeParticipant.Id == zombieSiegeParticipantId,
cancellationToken);
return zombieSiegeParticipantById is null
? Result.Failure<ZombieSiegeParticipantDto>(ZombieSiegeParticipantErrors.NotFound)
: Result.Success(zombieSiegeParticipantById);
}
public async Task<Result<bool>> InsertZombieSiegeParticipantsAsync(List<CreateZombieSiegeParticipantDto> createZombieSiegeParticipants, CancellationToken cancellationToken)
{
var zombieSiegeParticipants = mapper.Map<List<ZombieSiegeParticipant>>(createZombieSiegeParticipants);
try
{
await context.ZombieSiegeParticipants.AddRangeAsync(zombieSiegeParticipants, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
return Result.Success(true);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return Result.Failure<bool>(GeneralErrors.DatabaseError);
}
}
public async Task<Result<List<ZombieSiegeParticipantDto>>> GetPlayerZombieSiegeParticipantsAsync(Guid playerId, int last, CancellationToken cancellationToken)
{
var playerZombieSieges = await context.ZombieSiegeParticipants
.Where(zombieSiegeParticipant => zombieSiegeParticipant.PlayerId == playerId)
.OrderByDescending(zombieSiegeParticipant => zombieSiegeParticipant.ZombieSiege.EventDate)
.ProjectTo<ZombieSiegeParticipantDto>(mapper.ConfigurationProvider)
.Take(last)
.AsNoTracking()
.ToListAsync(cancellationToken);
return Result.Success(playerZombieSieges);
}
public async Task<Result<ZombieSiegeParticipantDto>> UpdateZombieSiegeParticipantAsync(UpdateZombieSiegeParticipantDto updateZombieSiegeParticipantDto,
CancellationToken cancellationToken)
{
var zombieSiegeParticipantToUpdate = await context.ZombieSiegeParticipants
.FirstOrDefaultAsync(
zombieSiegeParticipant => zombieSiegeParticipant.Id == updateZombieSiegeParticipantDto.Id,
cancellationToken);
if (zombieSiegeParticipantToUpdate is null)
return Result.Failure<ZombieSiegeParticipantDto>(ZombieSiegeErrors.NotFound);
mapper.Map(updateZombieSiegeParticipantDto, zombieSiegeParticipantToUpdate);
try
{
await context.SaveChangesAsync(cancellationToken);
return Result.Success(mapper.Map<ZombieSiegeParticipantDto>(zombieSiegeParticipantToUpdate));
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return Result.Failure<ZombieSiegeParticipantDto>(GeneralErrors.DatabaseError);
}
}
}

View File

@ -0,0 +1,116 @@
using Application.Classes;
using Application.DataTransferObjects.ZombieSiege;
using Application.Errors;
using Application.Interfaces;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Database;
using Database.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Application.Repositories;
public class ZombieSiegeRepository(ApplicationContext context, IMapper mapper, ILogger<ZombieSiegeRepository> logger) : IZombieSiegeRepository
{
public async Task<Result<ZombieSiegeDto>> GetZombieSiegeAsync(Guid zombieSiegeId, CancellationToken cancellationToken)
{
var zombieSiegeById = await context.ZombieSieges
.ProjectTo<ZombieSiegeDto>(mapper.ConfigurationProvider)
.AsNoTracking()
.FirstOrDefaultAsync(zombieSiege => zombieSiege.Id == zombieSiegeId, cancellationToken);
return zombieSiegeById is null
? Result.Failure<ZombieSiegeDto>(ZombieSiegeErrors.NotFound)
: Result.Success(zombieSiegeById);
}
public async Task<Result<ZombieSiegeDetailDto>> GetZombieSiegeDetailAsync(Guid zombieSiegeId, CancellationToken cancellationToken)
{
var zombieSiegeDetail = await context.ZombieSieges
.ProjectTo<ZombieSiegeDetailDto>(mapper.ConfigurationProvider)
.AsNoTracking()
.FirstOrDefaultAsync(zombieSiege => zombieSiege.Id == zombieSiegeId, cancellationToken);
return zombieSiegeDetail is null
? Result.Failure<ZombieSiegeDetailDto>(ZombieSiegeErrors.NotFound)
: Result.Success(zombieSiegeDetail);
}
public async Task<Result<List<ZombieSiegeDto>>> GetAllianceZombieSiegesAsync(Guid allianceId, int take, CancellationToken cancellationToken)
{
var allianceZombieSieges = await context.ZombieSieges
.Where(zombieSiege => zombieSiege.AllianceId == allianceId)
.OrderByDescending(zombieSiege => zombieSiege.EventDate)
.ProjectTo<ZombieSiegeDto>(mapper.ConfigurationProvider)
.AsNoTracking()
.Take(take)
.ToListAsync(cancellationToken);
return Result.Success(allianceZombieSieges);
}
public async Task<Result<ZombieSiegeDto>> CreateZombieSiegeAsync(CreateZombieSiegeDto createZombieSiegeDto, string createdBy,
CancellationToken cancellationToken)
{
var newZombieSiege = mapper.Map<ZombieSiege>(createZombieSiegeDto);
newZombieSiege.CreatedBy = createdBy;
try
{
await context.ZombieSieges.AddAsync(newZombieSiege, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
return Result.Success(mapper.Map<ZombieSiegeDto>(newZombieSiege));
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return Result.Failure<ZombieSiegeDto>(GeneralErrors.DatabaseError);
}
}
public async Task<Result<ZombieSiegeDto>> UpdateZombieSiegeAsync(UpdateZombieSiegeDto updateZombieSiegeDto, string modifiedBy,
CancellationToken cancellationToken)
{
var zombieSiegeToUpdate = await context.ZombieSieges
.FirstOrDefaultAsync(zombieSiege => zombieSiege.Id == updateZombieSiegeDto.Id, cancellationToken);
if (zombieSiegeToUpdate is null) return Result.Failure<ZombieSiegeDto>(ZombieSiegeErrors.NotFound);
mapper.Map(updateZombieSiegeDto, zombieSiegeToUpdate);
zombieSiegeToUpdate.ModifiedBy = modifiedBy;
try
{
await context.SaveChangesAsync(cancellationToken);
return Result.Success(mapper.Map<ZombieSiegeDto>(zombieSiegeToUpdate));
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return Result.Failure<ZombieSiegeDto>(GeneralErrors.DatabaseError);
}
}
public async Task<Result<bool>> DeleteZombieSiegeAsync(Guid zombieSiegeId, CancellationToken cancellationToken)
{
var zombieSiegeToDelete = await context.ZombieSieges
.FirstOrDefaultAsync(zombieSiege => zombieSiege.Id == zombieSiegeId, cancellationToken);
if (zombieSiegeToDelete is null) return Result.Failure<bool>(ZombieSiegeErrors.NotFound);
try
{
context.ZombieSieges.Remove(zombieSiegeToDelete);
await context.SaveChangesAsync(cancellationToken);
return Result.Success(true);
}
catch (Exception e)
{
logger.LogError(e, e.Message);
return Result.Failure<bool>(GeneralErrors.DatabaseError);
}
}
}

View File

@ -36,6 +36,10 @@ public class ApplicationContext(DbContextOptions<ApplicationContext> options) :
public DbSet<DesertStormParticipant> DesertStormParticipants { get; set; } public DbSet<DesertStormParticipant> DesertStormParticipants { get; set; }
public DbSet<ZombieSiege> ZombieSieges { get; set; }
public DbSet<ZombieSiegeParticipant> ZombieSiegeParticipants { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {
base.OnConfiguring(optionsBuilder); base.OnConfiguring(optionsBuilder);

View File

@ -0,0 +1,26 @@
using Database.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Database.Configurations;
public class ZombieSiegeConfiguration : IEntityTypeConfiguration<ZombieSiege>
{
public void Configure(EntityTypeBuilder<ZombieSiege> builder)
{
builder.HasKey(zombieSiege => zombieSiege.Id);
builder.Property(zombieSiege => zombieSiege.Id).ValueGeneratedNever();
builder.Property(zombieSiege => zombieSiege.EventDate).IsRequired();
builder.Property(zombieSiege => zombieSiege.AllianceSize).IsRequired();
builder.Property(zombieSiege => zombieSiege.CreatedBy).IsRequired().HasMaxLength(150);
builder.Property(zombieSiege => zombieSiege.Level).IsRequired();
builder.Property(zombieSiege => zombieSiege.ModifiedOn).IsRequired(false);
builder.Property(zombieSiege => zombieSiege.ModifiedBy).IsRequired(false).HasMaxLength(150);
builder.HasOne(zombieSiege => zombieSiege.Alliance)
.WithMany(alliance => alliance.ZombieSieges)
.HasForeignKey(zombieSiege => zombieSiege.AllianceId)
.OnDelete(DeleteBehavior.Cascade);
}
}

View File

@ -0,0 +1,28 @@
using Database.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Database.Configurations;
public class ZombieSiegeParticipantConfiguration : IEntityTypeConfiguration<ZombieSiegeParticipant>
{
public void Configure(EntityTypeBuilder<ZombieSiegeParticipant> builder)
{
builder.HasKey(zombieSiegeParticipant => zombieSiegeParticipant.Id);
builder.Property(zombieSiegeParticipant => zombieSiegeParticipant.Id).ValueGeneratedNever();
builder.Property(zombieSiegeParticipant => zombieSiegeParticipant.PlayerId).IsRequired();
builder.Property(zombieSiegeParticipant => zombieSiegeParticipant.ZombieSiegeId).IsRequired();
builder.Property(zombieSiegeParticipant => zombieSiegeParticipant.SurvivedWaves).IsRequired();
builder.HasOne(zombieSiegeParticipant => zombieSiegeParticipant.Player)
.WithMany(player => player.ZombieSiegeParticipants)
.HasForeignKey(zombieSiegeParticipant => zombieSiegeParticipant.PlayerId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(zombieSiegeParticipant => zombieSiegeParticipant.ZombieSiege)
.WithMany(zombieSiege => zombieSiege.ZombieSiegeParticipants)
.HasForeignKey(zombieSiegeParticipant => zombieSiegeParticipant.ZombieSiegeId)
.OnDelete(DeleteBehavior.Cascade);
}
}

View File

@ -25,4 +25,6 @@ public class Alliance : BaseEntity
public ICollection<MarshalGuard> MarshalGuards { get; set; } = []; public ICollection<MarshalGuard> MarshalGuards { get; set; } = [];
public ICollection<VsDuel> VsDuels { get; set; } = []; public ICollection<VsDuel> VsDuels { get; set; } = [];
public ICollection<ZombieSiege> ZombieSieges { get; set; } = [];
} }

View File

@ -33,4 +33,6 @@ public class Player : BaseEntity
public ICollection<Note> Notes { get; set; } = []; public ICollection<Note> Notes { get; set; } = [];
public ICollection<CustomEventParticipant> CustomEventParticipants { get; set; } = []; public ICollection<CustomEventParticipant> CustomEventParticipants { get; set; } = [];
public ICollection<ZombieSiegeParticipant> ZombieSiegeParticipants { get; set; } = [];
} }

View File

@ -0,0 +1,22 @@
namespace Database.Entities;
public class ZombieSiege : BaseEntity
{
public int Level { get; set; }
public Guid AllianceId { get; set; }
public Alliance Alliance { get; set; } = null!;
public DateTime EventDate { get; set; }
public required string CreatedBy { get; set; }
public DateTime? ModifiedOn { get; set; }
public string? ModifiedBy { get; set; }
public int AllianceSize { get; set; }
public ICollection<ZombieSiegeParticipant> ZombieSiegeParticipants { get; set; } = [];
}

View File

@ -0,0 +1,14 @@
namespace Database.Entities;
public class ZombieSiegeParticipant : BaseEntity
{
public Player Player { get; set; } = null!;
public Guid PlayerId { get; set; }
public Guid ZombieSiegeId { get; set; }
public ZombieSiege ZombieSiege { get; set; } = null!;
public int SurvivedWaves { get; set; }
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Database.Migrations
{
/// <inheritdoc />
public partial class AddZombieSiege : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
schema: "dbo",
table: "Players",
type: "datetime2",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "datetime2",
oldDefaultValue: new DateTime(2024, 11, 20, 10, 20, 41, 226, DateTimeKind.Local).AddTicks(9868));
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
schema: "dbo",
table: "Notes",
type: "datetime2",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "datetime2",
oldDefaultValue: new DateTime(2024, 11, 20, 10, 20, 41, 225, DateTimeKind.Local).AddTicks(5440));
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
schema: "dbo",
table: "Alliances",
type: "datetime2",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "datetime2",
oldDefaultValue: new DateTime(2024, 11, 20, 10, 20, 41, 211, DateTimeKind.Local).AddTicks(7279));
migrationBuilder.CreateTable(
name: "ZombieSieges",
schema: "dbo",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Level = table.Column<int>(type: "int", nullable: false),
AllianceId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
EventDate = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: false),
ModifiedOn = table.Column<DateTime>(type: "datetime2", nullable: true),
ModifiedBy = table.Column<string>(type: "nvarchar(150)", maxLength: 150, nullable: true),
AllianceSize = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ZombieSieges", x => x.Id);
table.ForeignKey(
name: "FK_ZombieSieges_Alliances_AllianceId",
column: x => x.AllianceId,
principalSchema: "dbo",
principalTable: "Alliances",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ZombieSiegeParticipants",
schema: "dbo",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
PlayerId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ZombieSiegeId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SurvivedWaves = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ZombieSiegeParticipants", x => x.Id);
table.ForeignKey(
name: "FK_ZombieSiegeParticipants_Players_PlayerId",
column: x => x.PlayerId,
principalSchema: "dbo",
principalTable: "Players",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ZombieSiegeParticipants_ZombieSieges_ZombieSiegeId",
column: x => x.ZombieSiegeId,
principalSchema: "dbo",
principalTable: "ZombieSieges",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ZombieSiegeParticipants_PlayerId",
schema: "dbo",
table: "ZombieSiegeParticipants",
column: "PlayerId");
migrationBuilder.CreateIndex(
name: "IX_ZombieSiegeParticipants_ZombieSiegeId",
schema: "dbo",
table: "ZombieSiegeParticipants",
column: "ZombieSiegeId");
migrationBuilder.CreateIndex(
name: "IX_ZombieSieges_AllianceId",
schema: "dbo",
table: "ZombieSieges",
column: "AllianceId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ZombieSiegeParticipants",
schema: "dbo");
migrationBuilder.DropTable(
name: "ZombieSieges",
schema: "dbo");
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
schema: "dbo",
table: "Players",
type: "datetime2",
nullable: false,
defaultValue: new DateTime(2024, 11, 20, 10, 20, 41, 226, DateTimeKind.Local).AddTicks(9868),
oldClrType: typeof(DateTime),
oldType: "datetime2");
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
schema: "dbo",
table: "Notes",
type: "datetime2",
nullable: false,
defaultValue: new DateTime(2024, 11, 20, 10, 20, 41, 225, DateTimeKind.Local).AddTicks(5440),
oldClrType: typeof(DateTime),
oldType: "datetime2");
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
schema: "dbo",
table: "Alliances",
type: "datetime2",
nullable: false,
defaultValue: new DateTime(2024, 11, 20, 10, 20, 41, 211, DateTimeKind.Local).AddTicks(7279),
oldClrType: typeof(DateTime),
oldType: "datetime2");
}
}
}

View File

@ -26,7 +26,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.Admonition", b => modelBuilder.Entity("Database.Entities.Admonition", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("CreatedBy") b.Property<string>("CreatedBy")
@ -62,7 +61,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.Alliance", b => modelBuilder.Entity("Database.Entities.Alliance", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("Abbreviation") b.Property<string>("Abbreviation")
@ -71,9 +69,7 @@ namespace Database.Migrations
.HasColumnType("nvarchar(5)"); .HasColumnType("nvarchar(5)");
b.Property<DateTime>("CreatedOn") b.Property<DateTime>("CreatedOn")
.ValueGeneratedOnAdd() .HasColumnType("datetime2");
.HasColumnType("datetime2")
.HasDefaultValue(new DateTime(2024, 11, 20, 10, 20, 41, 211, DateTimeKind.Local).AddTicks(7279));
b.Property<string>("ModifiedBy") b.Property<string>("ModifiedBy")
.HasMaxLength(150) .HasMaxLength(150)
@ -98,7 +94,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.CustomEvent", b => modelBuilder.Entity("Database.Entities.CustomEvent", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("AllianceId") b.Property<Guid>("AllianceId")
@ -145,7 +140,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.CustomEventParticipant", b => modelBuilder.Entity("Database.Entities.CustomEventParticipant", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<long?>("AchievedPoints") b.Property<long?>("AchievedPoints")
@ -172,7 +166,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.DesertStorm", b => modelBuilder.Entity("Database.Entities.DesertStorm", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("AllianceId") b.Property<Guid>("AllianceId")
@ -217,7 +210,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.DesertStormParticipant", b => modelBuilder.Entity("Database.Entities.DesertStormParticipant", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("DesertStormId") b.Property<Guid>("DesertStormId")
@ -247,7 +239,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.MarshalGuard", b => modelBuilder.Entity("Database.Entities.MarshalGuard", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("AllianceId") b.Property<Guid>("AllianceId")
@ -287,7 +278,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.MarshalGuardParticipant", b => modelBuilder.Entity("Database.Entities.MarshalGuardParticipant", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("MarshalGuardId") b.Property<Guid>("MarshalGuardId")
@ -311,7 +301,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.Note", b => modelBuilder.Entity("Database.Entities.Note", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<string>("CreatedBy") b.Property<string>("CreatedBy")
@ -320,9 +309,7 @@ namespace Database.Migrations
.HasColumnType("nvarchar(150)"); .HasColumnType("nvarchar(150)");
b.Property<DateTime>("CreatedOn") b.Property<DateTime>("CreatedOn")
.ValueGeneratedOnAdd() .HasColumnType("datetime2");
.HasColumnType("datetime2")
.HasDefaultValue(new DateTime(2024, 11, 20, 10, 20, 41, 225, DateTimeKind.Local).AddTicks(5440));
b.Property<string>("ModifiedBy") b.Property<string>("ModifiedBy")
.HasMaxLength(150) .HasMaxLength(150)
@ -349,7 +336,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.Player", b => modelBuilder.Entity("Database.Entities.Player", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("AllianceId") b.Property<Guid>("AllianceId")
@ -361,9 +347,7 @@ namespace Database.Migrations
.HasColumnType("nvarchar(150)"); .HasColumnType("nvarchar(150)");
b.Property<DateTime>("CreatedOn") b.Property<DateTime>("CreatedOn")
.ValueGeneratedOnAdd() .HasColumnType("datetime2");
.HasColumnType("datetime2")
.HasDefaultValue(new DateTime(2024, 11, 20, 10, 20, 41, 226, DateTimeKind.Local).AddTicks(9868));
b.Property<int>("Level") b.Property<int>("Level")
.HasColumnType("int"); .HasColumnType("int");
@ -514,7 +498,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.VsDuel", b => modelBuilder.Entity("Database.Entities.VsDuel", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("AllianceId") b.Property<Guid>("AllianceId")
@ -562,7 +545,6 @@ namespace Database.Migrations
modelBuilder.Entity("Database.Entities.VsDuelParticipant", b => modelBuilder.Entity("Database.Entities.VsDuelParticipant", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier"); .HasColumnType("uniqueidentifier");
b.Property<Guid>("PlayerId") b.Property<Guid>("PlayerId")
@ -583,6 +565,65 @@ namespace Database.Migrations
b.ToTable("VsDuelParticipants", "dbo"); b.ToTable("VsDuelParticipants", "dbo");
}); });
modelBuilder.Entity("Database.Entities.ZombieSiege", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("AllianceId")
.HasColumnType("uniqueidentifier");
b.Property<int>("AllianceSize")
.HasColumnType("int");
b.Property<string>("CreatedBy")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("nvarchar(150)");
b.Property<DateTime>("EventDate")
.HasColumnType("datetime2");
b.Property<int>("Level")
.HasColumnType("int");
b.Property<string>("ModifiedBy")
.HasMaxLength(150)
.HasColumnType("nvarchar(150)");
b.Property<DateTime?>("ModifiedOn")
.HasColumnType("datetime2");
b.HasKey("Id");
b.HasIndex("AllianceId");
b.ToTable("ZombieSieges", "dbo");
});
modelBuilder.Entity("Database.Entities.ZombieSiegeParticipant", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("PlayerId")
.HasColumnType("uniqueidentifier");
b.Property<int>("SurvivedWaves")
.HasColumnType("int");
b.Property<Guid>("ZombieSiegeId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("PlayerId");
b.HasIndex("ZombieSiegeId");
b.ToTable("ZombieSiegeParticipants", "dbo");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -912,6 +953,36 @@ namespace Database.Migrations
b.Navigation("VsDuel"); b.Navigation("VsDuel");
}); });
modelBuilder.Entity("Database.Entities.ZombieSiege", b =>
{
b.HasOne("Database.Entities.Alliance", "Alliance")
.WithMany("ZombieSieges")
.HasForeignKey("AllianceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Alliance");
});
modelBuilder.Entity("Database.Entities.ZombieSiegeParticipant", b =>
{
b.HasOne("Database.Entities.Player", "Player")
.WithMany("ZombieSiegeParticipants")
.HasForeignKey("PlayerId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Database.Entities.ZombieSiege", "ZombieSiege")
.WithMany("ZombieSiegeParticipants")
.HasForeignKey("ZombieSiegeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
b.Navigation("ZombieSiege");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null) b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
@ -976,6 +1047,8 @@ namespace Database.Migrations
b.Navigation("Users"); b.Navigation("Users");
b.Navigation("VsDuels"); b.Navigation("VsDuels");
b.Navigation("ZombieSieges");
}); });
modelBuilder.Entity("Database.Entities.CustomEvent", b => modelBuilder.Entity("Database.Entities.CustomEvent", b =>
@ -1006,6 +1079,8 @@ namespace Database.Migrations
b.Navigation("Notes"); b.Navigation("Notes");
b.Navigation("VsDuelParticipants"); b.Navigation("VsDuelParticipants");
b.Navigation("ZombieSiegeParticipants");
}); });
modelBuilder.Entity("Database.Entities.Rank", b => modelBuilder.Entity("Database.Entities.Rank", b =>
@ -1017,6 +1092,11 @@ namespace Database.Migrations
{ {
b.Navigation("VsDuelParticipants"); b.Navigation("VsDuelParticipants");
}); });
modelBuilder.Entity("Database.Entities.ZombieSiege", b =>
{
b.Navigation("ZombieSiegeParticipants");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -19,6 +19,8 @@ import {ChangePasswordComponent} from "./pages/change-password/change-password.c
import {DesertStormDetailComponent} from "./pages/desert-storm/desert-storm-detail/desert-storm-detail.component"; import {DesertStormDetailComponent} from "./pages/desert-storm/desert-storm-detail/desert-storm-detail.component";
import {ResetPasswordComponent} from "./Authentication/reset-password/reset-password.component"; import {ResetPasswordComponent} from "./Authentication/reset-password/reset-password.component";
import {CustomEventComponent} from "./pages/custom-event/custom-event.component"; import {CustomEventComponent} from "./pages/custom-event/custom-event.component";
import {ZombieSiegeComponent} from "./pages/zombie-siege/zombie-siege.component";
import {ZombieSiegeDetailComponent} from "./pages/zombie-siege/zombie-siege-detail/zombie-siege-detail.component";
const routes: Routes = [ const routes: Routes = [
{path: 'players', component: PlayerComponent, canActivate: [authGuard]}, {path: 'players', component: PlayerComponent, canActivate: [authGuard]},
@ -34,6 +36,8 @@ const routes: Routes = [
{path: 'account', component: AccountComponent, canActivate: [authGuard]}, {path: 'account', component: AccountComponent, canActivate: [authGuard]},
{path: 'change-password', component: ChangePasswordComponent, canActivate: [authGuard]}, {path: 'change-password', component: ChangePasswordComponent, canActivate: [authGuard]},
{path: 'custom-event', component: CustomEventComponent, canActivate: [authGuard]}, {path: 'custom-event', component: CustomEventComponent, canActivate: [authGuard]},
{path: 'zombie-siege', component: ZombieSiegeComponent, canActivate: [authGuard]},
{path: 'zombie-siege-detail/:id', component: ZombieSiegeDetailComponent, canActivate: [authGuard]},
{path: 'login', component: LoginComponent}, {path: 'login', component: LoginComponent},
{path: 'confirm-email', component: EmailConfirmationComponent}, {path: 'confirm-email', component: EmailConfirmationComponent},
{path: 'sign-up', component: SignUpComponent}, {path: 'sign-up', component: SignUpComponent},

View File

@ -47,6 +47,9 @@ import { ForgotPasswordComponent } from './Authentication/forgot-password/forgot
import { ResetPasswordComponent } from './Authentication/reset-password/reset-password.component'; import { ResetPasswordComponent } from './Authentication/reset-password/reset-password.component';
import { CustomEventComponent } from './pages/custom-event/custom-event.component'; import { CustomEventComponent } from './pages/custom-event/custom-event.component';
import { UnderDevelopmentComponent } from './helpers/under-development/under-development.component'; import { UnderDevelopmentComponent } from './helpers/under-development/under-development.component';
import { ZombieSiegeComponent } from './pages/zombie-siege/zombie-siege.component';
import { ZombieSiegeParticipantsModalComponent } from './modals/zombie-siege-participants-modal/zombie-siege-participants-modal.component';
import { ZombieSiegeDetailComponent } from './pages/zombie-siege/zombie-siege-detail/zombie-siege-detail.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -84,7 +87,10 @@ import { UnderDevelopmentComponent } from './helpers/under-development/under-dev
ForgotPasswordComponent, ForgotPasswordComponent,
ResetPasswordComponent, ResetPasswordComponent,
CustomEventComponent, CustomEventComponent,
UnderDevelopmentComponent UnderDevelopmentComponent,
ZombieSiegeComponent,
ZombieSiegeParticipantsModalComponent,
ZombieSiegeDetailComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -0,0 +1,14 @@
.check-box-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.check-box-container .form-check {
flex: 1 1 calc(50% - 10px);
box-sizing: border-box;
}
.text-color {
color: #43c315;
}

View File

@ -0,0 +1,29 @@
<div class="modal-header flex-column" xmlns="http://www.w3.org/1999/html">
<div class="d-flex justify-content-between align-items-center w-100">
<h4 class="modal-title">Select Number of Survived Waves</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="activeModal.dismiss()"></button>
</div>
</div>
<div class="modal-body">
<div class="check-box-container d-flex flex-wrap">
@for (player of playerParticipated; track player.playerId) {
<div class="form-check flex-item">
<label class="form-check-label" for="{{player.playerId}}">
<span class="text-color">{{player.playerName}}</span>
</label>
<select class="form-select" [(ngModel)]="player.survivedWaves">
@for (wave of waves; track wave) {
<option [ngValue]="wave">{{wave}}</option>
}
</select>
</div>
}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" (click)="activeModal.dismiss()">Close</button>
<button type="submit" (click)="activeModal.close(playerParticipated)" class=" btn btn-success">{{isUpdate ? 'Update Participants' : 'Add to event'}}</button>
</div>

View File

@ -0,0 +1,45 @@
import {Component, inject, Input, OnInit} from '@angular/core';
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
import {PlayerService} from "../../services/player.service";
import {PlayerModel} from "../../models/player.model";
@Component({
selector: 'app-zombie-siege-participants-modal',
templateUrl: './zombie-siege-participants-modal.component.html',
styleUrl: './zombie-siege-participants-modal.component.css'
})
export class ZombieSiegeParticipantsModalComponent implements OnInit {
private readonly _playerService: PlayerService = inject(PlayerService);
public activeModal: NgbActiveModal = inject(NgbActiveModal);
public playerParticipated: { playerId: string, playerName: string, survivedWaves: number; }[] = [];
public waves: number[] = [0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14,15,16,17,18,19,20];
public isUpdate: boolean = false;
@Input() players: { playerId: string, playerName: string, survivedWaves: number; }[] | undefined;
@Input({required: true}) allianceId!: string;
ngOnInit() {
if (this.players) {
this.playerParticipated = [...this.players];
this.isUpdate = true;
} else {
this.getPlayers();
this.isUpdate = false;
}
}
getPlayers() {
this._playerService.getAlliancePlayer(this.allianceId).subscribe({
next: ((response) => {
if (response) {
response.forEach((player: PlayerModel) => {
this.playerParticipated.push({playerId: player.id, playerName: player.playerName, survivedWaves: 0})
});
this.playerParticipated.sort((a, b) => a.playerName.localeCompare(b.playerName));
}
})
});
}
}

View File

@ -0,0 +1,24 @@
import {ZombieSiegeParticipantModel} from "./zombieSiegeParticipant.model";
export interface ZombieSiegeModel {
id: string;
allianceId: string;
eventDate: Date;
createdBy: string;
modifiedOn?: Date;
modifiedBy?: string;
allianceSize: number;
level: number;
totalLevel20Players: number;
}
export interface ZombieSiegeDetailModel extends ZombieSiegeModel{
zombieSiegeParticipants: ZombieSiegeParticipantModel[];
}
export interface CreateZombieSiegeModel {
allianceId: string;
allianceSize: number;
level: number;
eventDate: string;
}

View File

@ -0,0 +1,13 @@
export interface ZombieSiegeParticipantModel {
id: string;
playerId: string;
zombieSiegeId: string;
survivedWaves: number;
playerName: string;
}
export interface CreateZombieSiegeParticipantModel {
playerId: string;
zombieSiegeId: string;
survivedWaves: number;
}

View File

@ -40,6 +40,11 @@
</a> </a>
</li> </li>
<li class="nav-item">
<a (click)="isShown = false" class="nav-link" routerLink="/zombie-siege" routerLinkActive="active">Zombie Siege
</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a (click)="isShown = false" class="nav-link" routerLink="/custom-event" routerLinkActive="active">Custom Event <a (click)="isShown = false" class="nav-link" routerLink="/custom-event" routerLinkActive="active">Custom Event
</a> </a>

View File

@ -1,14 +1,17 @@
.custom-rating-icon { .custom-rating-icon {
font-size: 1.5rem; font-size: 1.5rem;
padding-right: 0.1rem; padding-right: 0.1rem;
color: #b0c4de; color: #b0c4de; /* Standardfarbe */
} }
.filled {
color: #1e90ff; .green {
color: #28a745; /* Grün für Bewertung 5 */
} }
.low {
color: #deb0b0; .yellow {
color: #ffc107; /* Gelb für Bewertung 4 */
} }
.filled.low {
color: #ff1e1e; .red {
color: #dc3545; /* Rot für Bewertung < 4 */
} }

View File

@ -12,10 +12,14 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Level: <span class="text-primary">{{marshalGuardDetail.level}}</span></h5> <h5 class="card-title">Level: <span class="text-primary">{{marshalGuardDetail.level}}</span></h5>
<p class="card-text">Participation rate: <ngb-progressbar class="mb-3" type="success" [value]="marshalGuardDetail.participants" [max]="marshalGuardDetail.allianceSize" [showValue]="true" /></p> <p class="card-text">Participation rate: <ngb-progressbar class="mb-3" type="success" [value]="marshalGuardDetail.participants" [max]="marshalGuardDetail.allianceSize" [showValue]="true" /></p>
<p class="card-text">Reward phase: <br> <p class="card-text">Reward phase: <br><ngb-rating [(rate)]="marshalGuardDetail.rewardPhase" [max]="5" [readonly]="true">
<ngb-rating [(rate)]="marshalGuardDetail.rewardPhase" [max]="5" [readonly]="true">
<ng-template let-fill="fill" let-index="index"> <ng-template let-fill="fill" let-index="index">
<i class="bi-star{{ fill === 100 ? '-fill' : '' }} custom-rating-icon" [class.filled]="fill === 100" [class.low]="index < 3"></i> <i
class="bi-star{{ fill === 100 ? '-fill' : '' }} custom-rating-icon"
[class.green]="marshalGuardDetail.rewardPhase === 5"
[class.yellow]="marshalGuardDetail.rewardPhase === 4"
[class.red]="marshalGuardDetail.rewardPhase < 4">
</i>
</ng-template> </ng-template>
</ngb-rating> </ngb-rating>
</p> </p>

View File

@ -13,17 +13,20 @@
color: #43c315; color: #43c315;
} }
.custom-rating { .custom-rating-icon {
font-size: 1.0rem; font-size: 1.0rem;
padding-right: 0.1rem; padding-right: 0.1rem;
color: #b0c4de; color: #b0c4de;
} }
.filled {
color: #1e90ff; .green {
color: #28a745;
} }
.low {
color: #deb0b0; .yellow {
color: #ffc107;
} }
.filled.low {
color: #ff1e1e; .red {
color: #dc3545;
} }

View File

@ -96,7 +96,12 @@
<td> <td>
<ngb-rating [(rate)]="marshalGuard.rewardPhase" [max]="5" [readonly]="true"> <ngb-rating [(rate)]="marshalGuard.rewardPhase" [max]="5" [readonly]="true">
<ng-template let-fill="fill" let-index="index"> <ng-template let-fill="fill" let-index="index">
<i class="bi-star{{ fill === 100 ? '-fill' : '' }} custom-rating" [class.filled]="fill === 100" [class.low]="index < 3"></i> <i
class="bi-star{{ fill === 100 ? '-fill' : '' }} custom-rating-icon"
[class.green]="marshalGuard.rewardPhase === 5"
[class.yellow]="marshalGuard.rewardPhase === 4"
[class.red]="marshalGuard.rewardPhase < 4">
</i>
</ng-template> </ng-template>
</ngb-rating> </ngb-rating>
</td> </td>

View File

@ -30,7 +30,7 @@ export class PlayerComponent implements OnInit {
private allianceId = this._tokenService.getAllianceId(); private allianceId = this._tokenService.getAllianceId();
public players: PlayerModel[] = []; public players: PlayerModel[] = [];
public tempPlayers: PlayerModel[] = []; public activePlayers: PlayerModel[] = [];
public r1Players: PlayerModel[] = []; public r1Players: PlayerModel[] = [];
public r2Players: PlayerModel[] = []; public r2Players: PlayerModel[] = [];
public r3Players: PlayerModel[] = []; public r3Players: PlayerModel[] = [];
@ -45,8 +45,8 @@ export class PlayerComponent implements OnInit {
this.filter.valueChanges.subscribe({ this.filter.valueChanges.subscribe({
next: ((value) => { next: ((value) => {
const term = value.toLowerCase(); const term = value.toLowerCase();
this.filteredPlayers = this.tempPlayers.filter(player => { this.filteredPlayers = this.activePlayers.filter(player => {
return player.playerName.toLowerCase().includes(term.toLowerCase()); return player.playerName.toLowerCase().includes(term);
}) })
}) })
}); });
@ -55,20 +55,27 @@ export class PlayerComponent implements OnInit {
} }
getPlayers(allianceId: string) { getPlayers(allianceId: string) {
this.filteredPlayers = [];
this.players = []; this.players = [];
this.r1Players = []; this.r1Players = [];
this.r2Players = []; this.r2Players = [];
this.r3Players = []; this.r3Players = [];
this.r4Players = []; this.r4Players = [];
this.tempPlayers = []; this.filteredPlayers = [];
this.activePlayers = [];
this._playerService.getAlliancePlayer(allianceId).subscribe({ this._playerService.getAlliancePlayer(allianceId).subscribe({
next: ((response: PlayerModel[]): void => { next: ((response: PlayerModel[]): void => {
if (response) { if (response) {
this.tempPlayers = response; response.sort((a, b) => {
const numA = parseInt(a.rankName.substring(1));
const numB = parseInt(b.rankName.substring(1));
return numB - numA;
})
this.players = response; this.players = response;
this.filteredPlayers = response; this.activePlayers = [...this.players];
response.filter((player: PlayerModel) => { this.filteredPlayers = [...this.players];
response.forEach((player: PlayerModel) => {
if (player.rankName === "R1") { if (player.rankName === "R1") {
this.r1Players.push(player); this.r1Players.push(player);
} else if (player.rankName === "R2") { } else if (player.rankName === "R2") {
@ -163,36 +170,29 @@ export class PlayerComponent implements OnInit {
} }
onRankFilterChange(event: any) { onRankFilterChange(event: any) {
switch (event.target.value) { const rank = event.target.value;
case 'R1': {
this.filter.patchValue('');
this.filteredPlayers = this.r1Players;
this.tempPlayers = this.r1Players;
} break;
case 'R2': {
this.filter.patchValue(''); this.filter.patchValue('');
this.filteredPlayers = this.r2Players; switch (rank) {
this.tempPlayers = this.r2Players; case 'R1': {
this.activePlayers = [...this.r1Players];
} break;
case 'R2': {
this.activePlayers = [...this.r2Players];
break; break;
} }
case 'R3': { case 'R3': {
this.filter.patchValue(''); this.activePlayers = [...this.r3Players];
this.filteredPlayers = this.r3Players;
this.tempPlayers = this.r3Players;
break; break;
} }
case 'R4': { case 'R4': {
this.filter.patchValue(''); this.activePlayers = [...this.r4Players];
this.filteredPlayers = this.r4Players;
this.tempPlayers = this.r4Players;
} break; } break;
default: { default: {
this.filter.patchValue(''); this.activePlayers = [...this.players];
this.filteredPlayers = this.players;
this.tempPlayers = this.players;
break; break;
} }
} }
this.filteredPlayers = [...this.activePlayers];
} }
} }

View File

@ -0,0 +1,41 @@
<div class="container mt-3 pb-5">
<!-- Back button-->
<div class="d-grid gap-2 col-6 mx-auto">
<button routerLink="/zombie-siege" class="btn btn-primary" type="button"><i class="bi bi-arrow-left"></i> Back</button>
</div>
@if (zombieSiegeDetail) {
<div class="card mt-5" [ngClass]="zombieSiegeDetail.totalLevel20Players >= 20 ? 'border-success' : 'border-danger'">
<h5 class="card-header d-flex justify-content-between">
<div>Event Date: {{zombieSiegeDetail.eventDate | date: 'dd.MM.yyyy'}}</div>
<div [ngClass]="zombieSiegeDetail.totalLevel20Players >= 20 ? 'text-success' : 'text-danger'">{{zombieSiegeDetail.totalLevel20Players >= 20 ? 'COMPLETED' : 'INCOMPLETE'}}</div>
</h5>
<div class="card-body">
<h5 class="card-title">Level: <span class="text-primary">{{zombieSiegeDetail.level}}</span></h5>
<p class="card-text">Alliance size: <span class="text-primary">{{zombieSiegeDetail.allianceSize}}</span></p>
<p class="card-text">Wave 20 Survivor: <span class="text-primary">{{zombieSiegeDetail.totalLevel20Players}}</span></p>
<hr>
<div>
<p class="card-text">Creator: <span class="text-primary">{{zombieSiegeDetail.createdBy}}</span></p>
@if (zombieSiegeDetail.modifiedOn) {
<p class="card-text">Modified: <span class="text-primary">{{zombieSiegeDetail.modifiedOn | date: 'dd.MM.yyyy HH:mm'}}</span>
by <span class="text-primary">{{zombieSiegeDetail.modifiedBy}}</span></p>
}
</div>
<hr>
<p class="card-text text-center">Players</p>
<div ngbScrollSpy class="bg-light p-3 rounded-2 mb-3" style="height: 200px">
<div class="row">
@for (player of zombieSiegeDetail.zombieSiegeParticipants; track player.id) {
<div class="col-12 col-md-6">
<p><span class="fw-bold text-primary">{{player.playerName}}</span> - Surviving waves: <span class="text-info">{{player.survivedWaves}}</span></p>
</div>
}
</div>
</div>
</div>
</div>
}
</div>

View File

@ -0,0 +1,36 @@
import {Component, inject, OnInit} from '@angular/core';
import {ZombieSiegeDetailModel} from "../../../models/zombieSiege.model";
import {ActivatedRoute} from "@angular/router";
import {ZombieSiegeService} from "../../../services/zombie-siege.service";
@Component({
selector: 'app-zombie-siege-detail',
templateUrl: './zombie-siege-detail.component.html',
styleUrl: './zombie-siege-detail.component.css'
})
export class ZombieSiegeDetailComponent implements OnInit {
private readonly _activatedRote: ActivatedRoute = inject(ActivatedRoute);
private readonly _zombieSiegeService: ZombieSiegeService = inject(ZombieSiegeService);
private zombieSiegeId!: string;
public zombieSiegeDetail: ZombieSiegeDetailModel | undefined;
ngOnInit() {
this.zombieSiegeId = this._activatedRote.snapshot.params['id'];
this.getZombieSiegeDetail(this.zombieSiegeId);
}
getZombieSiegeDetail(zombieSiegeId: string) {
this._zombieSiegeService.getZombieSiegeDetail(zombieSiegeId).subscribe({
next: ((response: ZombieSiegeDetailModel) => {
if (response) {
this.zombieSiegeDetail = response;
this.zombieSiegeDetail.zombieSiegeParticipants.sort((a, b) => b.survivedWaves - a.survivedWaves);
}
})
});
}
}

View File

@ -0,0 +1,112 @@
<div class="container mt-3 pb-5">
<h2 class="text-center">Zombie Siege</h2>
@if (!isCreateZombieSiege) {
<div class="d-grid gap-2 col-6 mx-auto">
<button (click)="onCreateEvent()" class="btn btn-primary" type="button">Create new Event</button>
</div>
}
@if (isCreateZombieSiege) {
<form [formGroup]="zombieSiegeForm">
<div class="form-floating mb-3 is-invalid">
<input [ngClass]="{
'is-invalid': f['eventDate'].invalid && (f['eventDate'].dirty || f['eventDate'].touched),
'is-valid': f['eventDate'].valid}"
type="date" class="form-control" id="eventDate" placeholder="eventDate" formControlName="eventDate">
<label for="level">eventDate</label>
@if (f['eventDate'].invalid && (f['eventDate'].dirty || f['eventDate'].touched)) {
<div class="invalid-feedback">
@if (f['eventDate'].hasError('required')) {
<p>eventDate is required</p>
}
</div>
}
</div>
<div class="form-floating mb-3 is-invalid">
<input [ngClass]="{
'is-invalid': f['level'].invalid && (f['level'].dirty || f['level'].touched),
'is-valid': f['level'].valid}"
type="number" class="form-control" id="level" placeholder="level" formControlName="level">
<label for="level">Level</label>
@if (f['level'].invalid && (f['level'].dirty || f['level'].touched)) {
<div class="invalid-feedback">
@if (f['level'].hasError('required')) {
<p>Level is required</p>
}
</div>
}
</div>
<div class="form-floating mb-3 is-invalid">
<input type="number" class="form-control" id="allianceSize" placeholder="allianceSize" formControlName="allianceSize">
<label for="level">allianceSize</label>
</div>
@if (!playerSelected) {
<div class="d-flex justify-content-center">
<p class="text-warning">Please add participants</p>
</div>
} @else {
<div class="d-flex justify-content-center">
<p><span class="fw-bold text-warning">{{playerParticipated.length}}</span> player(s) selected</p>
</div>
}
<div class="d-grid gap-2 col-6 mx-auto">
<button (click)="onAddParticipants()" class="btn btn-primary" type="button">{{isUpdate || playerParticipated.length > 0 ? 'Update Participants' : 'Add Participants'}}</button>
</div>
<div class="d-flex justify-content-between">
<button (click)="onCancel()" type="button" class="btn btn-warning">Cancel</button>
<button [disabled]="zombieSiegeForm.invalid || playerParticipated.length <= 0" (click)="onSubmit()" type="submit" class="btn btn-success">{{isUpdate ? 'Update': 'Create'}}</button>
</div>
</form>
}
@if(!isCreateZombieSiege) {
@if (zombieSieges.length > 0) {
<div class="table-responsive mt-5">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th scope="col">Event Date</th>
<th scope="col">Level</th>
<th scope="col">Alliance Size</th>
<th scope="col">Survived 20 waves</th>
<th scope="col">Creator</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
@for (zombieSiege of zombieSieges; track zombieSiege.id) {
<tr class="">
<td>{{zombieSiege.eventDate | date: 'dd.MM.yyyy'}}</td>
<td>{{zombieSiege.level}}</td>
<td>{{zombieSiege.allianceSize}}</td>
<td>{{zombieSiege.totalLevel20Players}}
@if (zombieSiege.totalLevel20Players <= 20) {
<i class="ps-3 bi bi-emoji-frown-fill text-danger"></i>
} @else {
<i class="ps-3 bi bi-emoji-smile-fill text-success"></i>
}
</td>
<td>{{zombieSiege.createdBy}}</td>
<td>
<div class="d-flex gap-3 justify-content-around">
<i (click)="onGoToZombieSiegeDetail(zombieSiege)" class="bi custom-info-icon bi-info-circle-fill"></i>
<i (click)="onEditZombieSiege(zombieSiege)" class="bi custom-edit-icon bi-pencil-fill"></i>
<i (click)="onDeleteZombieSiege(zombieSiege)" class="bi custom-delete-icon bi-trash3"></i>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
} @else {
<div class="alert alert-secondary text-center mt-5" role="alert">
No saved zombie sieges
</div>
}
}
</div>

View File

@ -0,0 +1,268 @@
import {Component, inject, OnInit} from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {CreateZombieSiegeModel, ZombieSiegeDetailModel, ZombieSiegeModel} from "../../models/zombieSiege.model";
import {JwtTokenService} from "../../services/jwt-token.service";
import {PlayerService} from "../../services/player.service";
import {PlayerModel} from "../../models/player.model";
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
import {
ZombieSiegeParticipantsModalComponent
} from "../../modals/zombie-siege-participants-modal/zombie-siege-participants-modal.component";
import {ZombieSiegeService} from "../../services/zombie-siege.service";
import {ToastrService} from "ngx-toastr";
import {
CreateZombieSiegeParticipantModel,
ZombieSiegeParticipantModel
} from "../../models/zombieSiegeParticipant.model";
import {ZombieSiegeParticipantService} from "../../services/zombie-siege-participant.service";
import Swal from "sweetalert2";
import {Router} from "@angular/router";
import {forkJoin, Observable} from "rxjs";
@Component({
selector: 'app-zombie-siege',
templateUrl: './zombie-siege.component.html',
styleUrl: './zombie-siege.component.css'
})
export class ZombieSiegeComponent implements OnInit {
private readonly _tokenService: JwtTokenService = inject(JwtTokenService);
private readonly _playerService: PlayerService = inject(PlayerService);
private readonly _modalService: NgbModal = inject(NgbModal);
private readonly _zombieSiegeService: ZombieSiegeService = inject(ZombieSiegeService);
private readonly _zombieSiegeParticipantService: ZombieSiegeParticipantService = inject(ZombieSiegeParticipantService);
private readonly _toastr: ToastrService = inject(ToastrService);
private readonly _router: Router = inject(Router);
private allianceId: string = this._tokenService.getAllianceId()!;
public isCreateZombieSiege: boolean = false;
public playerSelected: boolean = false;
public zombieSiegeForm!: FormGroup;
public playerParticipated: { playerId: string, playerName: string, survivedWaves: number; }[] = [];
public isUpdate: boolean = false;
public zombieSieges: ZombieSiegeModel[] = [];
public alliancePlayers: PlayerModel[] = [];
public zombieSiegeDetailModel: ZombieSiegeDetailModel | undefined;
private originalZombieSiegeParticipants: ZombieSiegeParticipantModel[] = [];
get f() {
return this.zombieSiegeForm.controls;
}
ngOnInit() {
this.getZombieSieges(10);
}
getZombieSieges(limit: number) {
this._zombieSiegeService.getAllianceZombieSieges(this.allianceId, limit).subscribe({
next: ((response: any) => {
if (response) {
this.zombieSieges = response;
} else {
this.zombieSieges = [];
}
})
,error: error => {
console.log(error);
this._toastr.error('Could not load zombie Sieges', 'Load');
}
})
}
onCreateEvent() {
this.getPlayers();
}
onAddParticipants() {
const modalRef = this._modalService.open(ZombieSiegeParticipantsModalComponent,
{animation: true, backdrop: 'static', centered: true, size: 'lg', scrollable: true});
if (this.playerSelected) {
this.playerParticipated.sort((a, b) => a.playerName.localeCompare(b.playerName));
modalRef.componentInstance.players = [...this.playerParticipated];
}
modalRef.componentInstance.allianceId = this.allianceId;
modalRef.closed.subscribe({
next: ((response: any) => {
if (response) {
this.playerSelected = true;
this.playerParticipated = [...response];
this.playerParticipated.sort((a, b) => a.playerName.localeCompare(b.playerName));
}
})
})
}
onCancel() {
this.isCreateZombieSiege = false;
this.isUpdate = false;
this.playerParticipated = [];
}
onSubmit() {
if (this.isUpdate) {
this.updateZombieSiege();
return;
}
const createZombieSiege: CreateZombieSiegeModel = this.zombieSiegeForm.getRawValue() as CreateZombieSiegeModel;
this._zombieSiegeService.createZombieSiege(createZombieSiege).subscribe({
next: ((response) => {
if (response) {
this.insertZombieSiegeParticipants(response.id);
this._toastr.success('Successfully created Zombie Sieges', 'Create Zombie Siege');
}
}),
error: error => {
console.log(error);
this._toastr.error('Could not create zombie Siege', 'Create Zombie Siege');
}
});
}
onGoToZombieSiegeDetail(zombieSiege: ZombieSiegeModel) {
this._router.navigate(['zombie-siege-detail', zombieSiege.id]).then();
}
onEditZombieSiege(zombieSiege: ZombieSiegeModel) {
this._zombieSiegeService.getZombieSiegeDetail(zombieSiege.id).subscribe({
next: ((response) => {
if (response) {
this.zombieSiegeDetailModel = response;
const originalParticipants = structuredClone(response.zombieSiegeParticipants);
this.playerParticipated = structuredClone(response.zombieSiegeParticipants);
this.playerParticipated.sort((a, b) => a.playerName.localeCompare(b.playerName));
this.playerSelected = true;
this.isUpdate = true;
this.createZombieSiegeForm(true, response);
this.originalZombieSiegeParticipants = originalParticipants;
}
})
});
}
onDeleteZombieSiege(zombieSiege: ZombieSiegeModel) {
Swal.fire({
title: "Delete Zombie Siege ?",
text: `Do you really want to delete the zombie siege?`,
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!"
}).then((result) => {
if (result.isConfirmed) {
this._zombieSiegeService.deleteZombieSiege(zombieSiege.id).subscribe({
next: ((response) => {
if (response) {
Swal.fire({
title: "Deleted!",
text: "Zombie Siege has been deleted",
icon: "success"
}).then(_ => this.getZombieSieges(10));
}
}),
error: (error: Error) => {
console.log(error);
}
});
}
});
}
private getPlayers() {
this._playerService.getAlliancePlayer(this.allianceId).subscribe({
next: ((response) => {
if (response) {
this.alliancePlayers = response;
this.createZombieSiegeForm(false);
}
})
});
}
private createZombieSiegeForm(isUpdate: boolean, zombieSiegeModel: ZombieSiegeDetailModel | null = null) {
if (isUpdate) {
this.playerSelected = true;
this.playerParticipated = [...zombieSiegeModel!.zombieSiegeParticipants];
} else {
this.playerSelected = false;
}
const d = zombieSiegeModel ? new Date(zombieSiegeModel!.eventDate) : new Date();
this.zombieSiegeForm = new FormGroup({
id: new FormControl<string>(zombieSiegeModel ? zombieSiegeModel!.id : ''),
allianceId: new FormControl<string>(this.allianceId),
eventDate: new FormControl<string>(new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate())).toISOString().substring(0, 10)),
level: new FormControl<number | null>(zombieSiegeModel ? zombieSiegeModel!.level : null, [Validators.required]),
allianceSize: new FormControl<number>(zombieSiegeModel ? zombieSiegeModel!.allianceSize : this.alliancePlayers.length),
});
this.zombieSiegeForm.get('allianceSize')?.disable();
this.isCreateZombieSiege = true;
}
private insertZombieSiegeParticipants(zombieSiegeId: string) {
const createZombieSiegeParticipants: CreateZombieSiegeParticipantModel[] = [];
this.playerParticipated.forEach((player) => {
const createZombieSiegeParticipant: CreateZombieSiegeParticipantModel = {
zombieSiegeId: zombieSiegeId,
playerId: player.playerId,
survivedWaves: player.survivedWaves,
};
createZombieSiegeParticipants.push(createZombieSiegeParticipant);
});
this._zombieSiegeParticipantService.insertZombieSiegeParticipants(createZombieSiegeParticipants).subscribe({
next: (() => {
this.onCancel();
this.getZombieSieges(10);
this.playerParticipated = [];
})
});
}
private updateZombieSiege() {
const toUpdate: any[] = [];
const updateZombieSiege: ZombieSiegeModel = this.zombieSiegeForm.getRawValue() as ZombieSiegeModel;
this.playerParticipated.forEach((p) => {
const player = this.originalZombieSiegeParticipants.find(d => d.playerId == p.playerId)!;
if (player.survivedWaves !== p.survivedWaves) {
toUpdate.push(p);
}
});
this._zombieSiegeService.updateZombieSiege(updateZombieSiege.id, updateZombieSiege).subscribe({
next: ((response) => {
if (response) {
this.updateZombieSiegeParticipants(toUpdate);
}
})
})
}
private updateZombieSiegeParticipants(zombieSiegeParticipants: ZombieSiegeParticipantModel[]) {
if (zombieSiegeParticipants.length <= 0) {
this._toastr.success('Successfully updated!', 'Successfully');
this.onCancel();
this.getZombieSieges(10);
return;
}
const requests: Observable<ZombieSiegeParticipantModel>[] = [];
zombieSiegeParticipants.forEach((participant) => {
const request = this._zombieSiegeParticipantService.updateZombieSiegeParticipant(participant.id, participant);
requests.push(request);
})
forkJoin(requests).subscribe({
next: ((response) => {
if (response) {
this._toastr.success('Successfully updated!', 'Successfully');
this.onCancel();
this.getZombieSieges(10);
}
})
})
}
}

View File

@ -0,0 +1,28 @@
import {inject, Injectable} from '@angular/core';
import {environment} from "../../environments/environment";
import {HttpClient, HttpParams} from "@angular/common/http";
import {Observable} from "rxjs";
import {CreateZombieSiegeParticipantModel, ZombieSiegeParticipantModel} from "../models/zombieSiegeParticipant.model";
@Injectable({
providedIn: 'root'
})
export class ZombieSiegeParticipantService {
private readonly _serviceUrl = environment.apiBaseUrl + 'ZombieSiegeParticipants/';
private readonly _httpClient: HttpClient = inject(HttpClient);
getPlayerZombieSiegeParticipants(playerId: string, last: number): Observable<ZombieSiegeParticipantModel[]> {
let params = new HttpParams();
params = params.append('last', last);
return this._httpClient.get<ZombieSiegeParticipantModel[]>(this._serviceUrl + 'Player/' + playerId, {params: params});
}
insertZombieSiegeParticipants(zombieSiegeParticipants: CreateZombieSiegeParticipantModel[]): Observable<boolean> {
return this._httpClient.post<boolean>(this._serviceUrl, zombieSiegeParticipants)
}
updateZombieSiegeParticipant(zombieSiegeParticipantId: string, zombieSiegeParticipant: ZombieSiegeParticipantModel): Observable<ZombieSiegeParticipantModel> {
return this._httpClient.put<ZombieSiegeParticipantModel>(this._serviceUrl + zombieSiegeParticipantId, zombieSiegeParticipant);
}
}

View File

@ -0,0 +1,36 @@
import {inject, Injectable} from '@angular/core';
import {environment} from "../../environments/environment";
import {HttpClient, HttpParams} from "@angular/common/http";
import {Observable} from "rxjs";
import {CreateZombieSiegeModel, ZombieSiegeDetailModel, ZombieSiegeModel} from "../models/zombieSiege.model";
@Injectable({
providedIn: 'root'
})
export class ZombieSiegeService {
private readonly _serviceUrl = environment.apiBaseUrl + 'ZombieSieges/';
private readonly _httpClient: HttpClient = inject(HttpClient);
getZombieSiegeDetail(zombieSiegeId: string): Observable<ZombieSiegeDetailModel> {
return this._httpClient.get<ZombieSiegeDetailModel>(this._serviceUrl + 'GetZombieSiegeDetail/' + zombieSiegeId);
}
getAllianceZombieSieges(allianceId: string, take: number): Observable<ZombieSiegeModel[]> {
let params = new HttpParams();
params = params.append('take', take);
return this._httpClient.get<ZombieSiegeModel[]>(this._serviceUrl + 'Alliance/' + allianceId, {params: params});
}
createZombieSiege(createZombieSiege: CreateZombieSiegeModel): Observable<ZombieSiegeModel> {
return this._httpClient.post<ZombieSiegeModel>(this._serviceUrl, createZombieSiege);
}
updateZombieSiege(zombieSiegeId: string, zombieSiege: ZombieSiegeModel): Observable<ZombieSiegeModel> {
return this._httpClient.put<ZombieSiegeModel>(this._serviceUrl + zombieSiegeId, zombieSiege);
}
deleteZombieSiege(zombieSiegeId: string): Observable<boolean> {
return this._httpClient.delete<boolean>(this._serviceUrl + zombieSiegeId);
}
}