From 7d820c8a21e88ea24b78a0fe31dc7b07d7891b1f Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 14:24:59 +0400 Subject: [PATCH 01/13] =?UTF-8?q?add:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=D0=BA=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B4=D0=BB=D1=8F=20=D0=BE?= =?UTF-8?q?=D1=82=D1=87=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/ChangeRecordDto.cs | 1 + back/Contracts/Mappers/ChangeRecordMapper.cs | 5 ++- .../Services/IReportPeriodService.cs | 8 ++++ .../ViewModels/ChangeRecordViewModel.cs | 1 + .../Controllers/ReportController.cs | 37 +++++++++++++++++++ .../Extensions/AddDomainServicesExt.cs | 6 +-- .../Repositories/ChangeRecordRepo.cs | 3 +- back/Services/Reports/ReportPeriodService.cs | 29 +++++++++++++++ .../Exceptions/ReportDataNotFoundException.cs | 10 +++++ 9 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 back/Contracts/Services/IReportPeriodService.cs create mode 100644 back/Controllers/Controllers/ReportController.cs create mode 100644 back/Services/Reports/ReportPeriodService.cs create mode 100644 back/Services/Support/Exceptions/ReportDataNotFoundException.cs diff --git a/back/Contracts/DTOs/ChangeRecordDto.cs b/back/Contracts/DTOs/ChangeRecordDto.cs index 21d140c..6e2fe42 100644 --- a/back/Contracts/DTOs/ChangeRecordDto.cs +++ b/back/Contracts/DTOs/ChangeRecordDto.cs @@ -5,6 +5,7 @@ public class ChangeRecordDto public Guid Id { get; set; } public Guid UserId { get; set; } public Guid? SpendingGroupId { get; set; } + public string SpendingGroupName { get; set; } = string.Empty; public decimal Sum { get; set; } public DateTime ChangedAt { get; set; } } \ No newline at end of file diff --git a/back/Contracts/Mappers/ChangeRecordMapper.cs b/back/Contracts/Mappers/ChangeRecordMapper.cs index 606344d..065eff0 100644 --- a/back/Contracts/Mappers/ChangeRecordMapper.cs +++ b/back/Contracts/Mappers/ChangeRecordMapper.cs @@ -8,8 +8,9 @@ public static class ChangeRecordMapper public static ChangeRecordViewModel ToView(this ChangeRecordDto dto) => new() { - Id = dto.Id, + Id = dto.Id, Sum = dto.Sum, - ChangedAt = dto.ChangedAt + ChangedAt = dto.ChangedAt, + SpendingGroupName = dto.SpendingGroupName }; } \ No newline at end of file diff --git a/back/Contracts/Services/IReportPeriodService.cs b/back/Contracts/Services/IReportPeriodService.cs new file mode 100644 index 0000000..46719a4 --- /dev/null +++ b/back/Contracts/Services/IReportPeriodService.cs @@ -0,0 +1,8 @@ +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface IReportPeriodService +{ + Task> GetReportData(DateTime from, DateTime to); +} \ No newline at end of file diff --git a/back/Contracts/ViewModels/ChangeRecordViewModel.cs b/back/Contracts/ViewModels/ChangeRecordViewModel.cs index 3a75eb3..99e6d82 100644 --- a/back/Contracts/ViewModels/ChangeRecordViewModel.cs +++ b/back/Contracts/ViewModels/ChangeRecordViewModel.cs @@ -5,4 +5,5 @@ public class ChangeRecordViewModel public Guid Id { get; set; } public decimal Sum { get; set; } public DateTime ChangedAt { get; set; } + public string SpendingGroupName { get; set; } = string.Empty; } \ No newline at end of file diff --git a/back/Controllers/Controllers/ReportController.cs b/back/Controllers/Controllers/ReportController.cs new file mode 100644 index 0000000..3ff6690 --- /dev/null +++ b/back/Controllers/Controllers/ReportController.cs @@ -0,0 +1,37 @@ +using Contracts.Services; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Services.Support; + +namespace Controllers.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class ReportController : ControllerBase +{ + private readonly IReportPeriodService _reportPeriodService; + + public ReportController(IReportPeriodService reportPeriodService) + { + _reportPeriodService = reportPeriodService; + } + + [HttpGet("period")] + public async Task>> GetReportData( + [FromQuery] DateTime from, [FromQuery] DateTime to) + { + try + { + var periodData = await _reportPeriodService.GetReportData(from, to); + return Ok(periodData); + } + catch (ReportDataNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } +} \ No newline at end of file diff --git a/back/Controllers/Extensions/AddDomainServicesExt.cs b/back/Controllers/Extensions/AddDomainServicesExt.cs index 7d6cc0a..1a7fc59 100644 --- a/back/Controllers/Extensions/AddDomainServicesExt.cs +++ b/back/Controllers/Extensions/AddDomainServicesExt.cs @@ -1,5 +1,6 @@ using Contracts.Services; using Services.Domain; +using Services.Reports; namespace Controllers.Extensions; @@ -9,11 +10,10 @@ public static class AddDomainServicesExtension { services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Infrastructure/Repositories/ChangeRecordRepo.cs b/back/Infrastructure/Repositories/ChangeRecordRepo.cs index 6da1839..94aab20 100644 --- a/back/Infrastructure/Repositories/ChangeRecordRepo.cs +++ b/back/Infrastructure/Repositories/ChangeRecordRepo.cs @@ -46,6 +46,7 @@ public class ChangeRecordRepo : IChangeRecordRepo using var context = _factory.CreateDbContext(); var record = await context.ChangeRecords + .Include(x => x.SpendingGroup) .FirstOrDefaultAsync(x => x.Id == search.Id); if (record == null) { @@ -71,7 +72,7 @@ public class ChangeRecordRepo : IChangeRecordRepo query = query.Where(x => x.ChangedAt >= search.From && x.ChangedAt <= search.To); } } - return await query.Select(x => x.ToDto()).ToListAsync(); + return await query.Include(x => x.SpendingGroup).Select(x => x.ToDto()).ToListAsync(); } public async Task Update(ChangeRecordDto changeRecord) diff --git a/back/Services/Reports/ReportPeriodService.cs b/back/Services/Reports/ReportPeriodService.cs new file mode 100644 index 0000000..51b64a0 --- /dev/null +++ b/back/Services/Reports/ReportPeriodService.cs @@ -0,0 +1,29 @@ +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Services.Support; + +namespace Services.Reports; + +public class ReportPeriodService : IReportPeriodService +{ + private readonly IChangeRecordRepo _changeRecordRepo; + + public ReportPeriodService(IChangeRecordRepo changeRecordRepo) + { + _changeRecordRepo = changeRecordRepo; + } + + public async Task> GetReportData(DateTime from, DateTime to) + { + var records = await _changeRecordRepo.GetList(new ChangeRecordSearch() { From = from, To = to }); + + if (!records.Any()) + { + throw new ReportDataNotFoundException("Нет данных за указанный период"); + } + return records.Select(x => x.ToView()).ToList(); + } +} diff --git a/back/Services/Support/Exceptions/ReportDataNotFoundException.cs b/back/Services/Support/Exceptions/ReportDataNotFoundException.cs new file mode 100644 index 0000000..915e419 --- /dev/null +++ b/back/Services/Support/Exceptions/ReportDataNotFoundException.cs @@ -0,0 +1,10 @@ +using Services.Support.Exceptions; + +namespace Services.Support; + +public class ReportDataNotFoundException : EntityNotFoundException +{ + public ReportDataNotFoundException(string message) : base(message) { } + public ReportDataNotFoundException(string message, Exception innerException) + : base(message, innerException) { } +} \ No newline at end of file From 9b019c7251e2eb7b5eae4f889b30ed5e6591085f Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sun, 1 Dec 2024 20:32:23 +0400 Subject: [PATCH 02/13] =?UTF-8?q?add:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=D0=B0=20=D0=BE=20=D1=81?= =?UTF-8?q?=D0=BC=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D1=8B=20=D1=82=D1=80=D0=B0=D1=82=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=20?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0,=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=20=D0=B2=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=B8=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/ISpendingGroupRepo.cs | 1 + .../Services/IReportOffsetFromPlanService.cs | 9 ++++++ .../Repositories/SpendingGroupRepo.cs | 24 ++++++++++++++ .../Reports/ReportOffsetFromPlanService.cs | 32 +++++++++++++++++++ .../Exceptions/ReportDataNotFoundException.cs | 10 ++++++ 5 files changed, 76 insertions(+) create mode 100644 back/Contracts/Services/IReportOffsetFromPlanService.cs create mode 100644 back/Services/Reports/ReportOffsetFromPlanService.cs create mode 100644 back/Services/Support/Exceptions/ReportDataNotFoundException.cs diff --git a/back/Contracts/Repositories/ISpendingGroupRepo.cs b/back/Contracts/Repositories/ISpendingGroupRepo.cs index f8f7999..54f4476 100644 --- a/back/Contracts/Repositories/ISpendingGroupRepo.cs +++ b/back/Contracts/Repositories/ISpendingGroupRepo.cs @@ -6,6 +6,7 @@ namespace Contracts.Repositories; public interface ISpendingGroupRepo { Task Get(SpendingGroupSearch search); + Task GetByPlan(SpendingPlanSearch search); Task> GetList(SpendingGroupSearch? search = null); Task Create(SpendingGroupDto spendingGroup); Task Delete(SpendingGroupSearch search); diff --git a/back/Contracts/Services/IReportOffsetFromPlanService.cs b/back/Contracts/Services/IReportOffsetFromPlanService.cs new file mode 100644 index 0000000..f9c51ed --- /dev/null +++ b/back/Contracts/Services/IReportOffsetFromPlanService.cs @@ -0,0 +1,9 @@ +using Contracts.SearchModels; +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface IReportOffsetFromPlanService +{ + public Task GetReportData(SpendingPlanSearch search); +} \ No newline at end of file diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs index 6c8382a..a356914 100644 --- a/back/Infrastructure/Repositories/SpendingGroupRepo.cs +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -54,6 +54,30 @@ public class SpendingGroupRepo : ISpendingGroupRepo return group?.ToDto(); } + public async Task GetByPlan(SpendingPlanSearch search) + { + using var context = _factory.CreateDbContext(); + var plan = await context.SpendingPlans + .FirstOrDefaultAsync(x => x.Id == search.Id); + + if (plan == null) + { + return null; + } + + var group = await context.SpendingGroups + .Where(x => x.Id == plan.SpendingGroupId) + .Include(x => x.ChangeRecords != null + ? x.ChangeRecords + .Where(cg => cg.ChangedAt >= plan.StartAt && cg.ChangedAt <= plan.EndAt) + .OrderBy(cg => cg.ChangedAt) + : null) + .LastAsync(); + group.SpendingPlans = [plan]; + + return group.ToDto(); + } + public async Task> GetList(SpendingGroupSearch? search = null) { using var context = _factory.CreateDbContext(); diff --git a/back/Services/Reports/ReportOffsetFromPlanService.cs b/back/Services/Reports/ReportOffsetFromPlanService.cs new file mode 100644 index 0000000..d2a66b6 --- /dev/null +++ b/back/Services/Reports/ReportOffsetFromPlanService.cs @@ -0,0 +1,32 @@ +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Services.Support.Exceptions; + +namespace Services.Reports; + +public class ReportOffsetFromPlanService : IReportOffsetFromPlanService +{ + private readonly ISpendingGroupRepo _spendingGroupRepo; + + public ReportOffsetFromPlanService(ISpendingGroupRepo spendingGroupRepo) + { + _spendingGroupRepo = spendingGroupRepo; + } + + public async Task GetReportData(SpendingPlanSearch search) + { + var group = await _spendingGroupRepo.GetByPlan(search); + if (group == null) + { + throw new EntityNotFoundException("Не удалось найти группу по такому плану"); + } + if (!group.ChangeRecords.Any()) + { + throw new ReportDataNotFoundException("Данные об изменении баланса отсутствуют"); + } + return group.ToView(); + } +} diff --git a/back/Services/Support/Exceptions/ReportDataNotFoundException.cs b/back/Services/Support/Exceptions/ReportDataNotFoundException.cs new file mode 100644 index 0000000..ffdd60e --- /dev/null +++ b/back/Services/Support/Exceptions/ReportDataNotFoundException.cs @@ -0,0 +1,10 @@ +namespace Services.Support.Exceptions; + +public class ReportDataNotFoundException : EntityNotFoundException +{ + public ReportDataNotFoundException(string message) + : base(message) { } + public ReportDataNotFoundException(string message, Exception innerException) + : base(message, innerException) { } + +} \ No newline at end of file From f800f6cf0e50dc1532bb51dc2bfd8360661ee418 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sun, 1 Dec 2024 21:39:11 +0400 Subject: [PATCH 03/13] =?UTF-8?q?add:=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=D0=B0=20=D0=BE=20=D1=81?= =?UTF-8?q?=D0=BC=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D1=8B=20=D0=B7=D0=B0=D1=82=D1=80=D0=B0=D1=82=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BD=D0=BE=D1=81=D0=B8=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ReportController.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/back/Controllers/Controllers/ReportController.cs b/back/Controllers/Controllers/ReportController.cs index 3ff6690..3e3402e 100644 --- a/back/Controllers/Controllers/ReportController.cs +++ b/back/Controllers/Controllers/ReportController.cs @@ -1,7 +1,7 @@ using Contracts.Services; using Contracts.ViewModels; using Microsoft.AspNetCore.Mvc; -using Services.Support; +using Services.Support.Exceptions; namespace Controllers.Controllers; @@ -10,10 +10,13 @@ namespace Controllers.Controllers; public class ReportController : ControllerBase { private readonly IReportPeriodService _reportPeriodService; + private readonly IReportOffsetFromPlanService _reportOffsetFromPlanService; - public ReportController(IReportPeriodService reportPeriodService) + public ReportController(IReportPeriodService reportPeriodService, + IReportOffsetFromPlanService reportOffsetFromPlanService) { _reportPeriodService = reportPeriodService; + _reportOffsetFromPlanService = reportOffsetFromPlanService; } [HttpGet("period")] @@ -34,4 +37,26 @@ public class ReportController : ControllerBase return StatusCode(500, ex.Message); } } + + [HttpGet("plan/{id}")] + public async Task> GetReportOffsetFromPlan(Guid id) + { + try + { + var offset = await _reportOffsetFromPlanService.GetReportData(new() { Id = id }); + return Ok(offset); + } + catch (ReportDataNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } } \ No newline at end of file From 5cb4937bcf4dd0659d96098a4e5cea2c0c86222d Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sun, 1 Dec 2024 23:49:16 +0400 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=20=D1=81=D0=BC=D0=B5?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D0=B5=D1=82=20=D0=BD=D0=BE=D1=80=D0=BC=D0=B0=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/AddRepotServicesExt.cs | 13 +++++++++++++ back/Controllers/Program.cs | 1 + .../Repositories/SpendingGroupRepo.cs | 19 ++++++++++++++----- back/Services/Reports/ReportPeriodService.cs | 2 +- 4 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 back/Controllers/Extensions/AddRepotServicesExt.cs diff --git a/back/Controllers/Extensions/AddRepotServicesExt.cs b/back/Controllers/Extensions/AddRepotServicesExt.cs new file mode 100644 index 0000000..6c2832b --- /dev/null +++ b/back/Controllers/Extensions/AddRepotServicesExt.cs @@ -0,0 +1,13 @@ +using Contracts.Services; +using Services.Reports; + +namespace Controllers.Extensions; + +public static class AddReportServicesExtension +{ + public static void AddReportServices(this IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + } +} \ No newline at end of file diff --git a/back/Controllers/Program.cs b/back/Controllers/Program.cs index 2e20712..73d61e5 100644 --- a/back/Controllers/Program.cs +++ b/back/Controllers/Program.cs @@ -6,6 +6,7 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbConnectionService(builder.Configuration); builder.Services.AddRepos(); builder.Services.AddDomainServices(); +builder.Services.AddReportServices(); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs index a356914..a04ee9b 100644 --- a/back/Infrastructure/Repositories/SpendingGroupRepo.cs +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -46,6 +46,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo using var context = _factory.CreateDbContext(); var group = await context.SpendingGroups + .AsNoTracking() .Include(x => x.ChangeRecords) .Include(x => x.SpendingPlans) .FirstOrDefaultAsync(x => x.Id == search.Id @@ -58,6 +59,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo { using var context = _factory.CreateDbContext(); var plan = await context.SpendingPlans + .AsNoTracking() .FirstOrDefaultAsync(x => x.Id == search.Id); if (plan == null) @@ -66,13 +68,20 @@ public class SpendingGroupRepo : ISpendingGroupRepo } var group = await context.SpendingGroups + .AsNoTracking() .Where(x => x.Id == plan.SpendingGroupId) - .Include(x => x.ChangeRecords != null - ? x.ChangeRecords + .Include(x => x.ChangeRecords + // Выбираем из них только те, которые попадают в диапазон дат плана .Where(cg => cg.ChangedAt >= plan.StartAt && cg.ChangedAt <= plan.EndAt) + // И сортируем их по дате .OrderBy(cg => cg.ChangedAt) - : null) - .LastAsync(); + ) + .FirstOrDefaultAsync(); + if (group == null) + { + return null; + } + group.SpendingPlans = [plan]; return group.ToDto(); @@ -82,7 +91,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo { using var context = _factory.CreateDbContext(); - var query = context.SpendingGroups.AsQueryable(); + var query = context.SpendingGroups.AsNoTracking().AsQueryable(); if (search != null) { diff --git a/back/Services/Reports/ReportPeriodService.cs b/back/Services/Reports/ReportPeriodService.cs index 51b64a0..96539fa 100644 --- a/back/Services/Reports/ReportPeriodService.cs +++ b/back/Services/Reports/ReportPeriodService.cs @@ -3,7 +3,7 @@ using Contracts.Repositories; using Contracts.SearchModels; using Contracts.Services; using Contracts.ViewModels; -using Services.Support; +using Services.Support.Exceptions; namespace Services.Reports; From 7139b67994e477b3483b15bf663855f68ad2aea7 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sun, 1 Dec 2024 23:56:57 +0400 Subject: [PATCH 05/13] =?UTF-8?q?fix:=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=D1=8B=20=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=82=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E=D1=82=20=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B8=D0=B2=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=BD=D0=B5=20=D0=B1?= =?UTF-8?q?=D1=8B=D0=BB=D0=BE=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=20=D1=83=20=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Infrastructure/Repositories/SpendingGroupRepo.cs | 2 +- back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs index a04ee9b..7d96e7b 100644 --- a/back/Infrastructure/Repositories/SpendingGroupRepo.cs +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -70,7 +70,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo var group = await context.SpendingGroups .AsNoTracking() .Where(x => x.Id == plan.SpendingGroupId) - .Include(x => x.ChangeRecords + .Include(x => x.ChangeRecords! // Выбираем из них только те, которые попадают в диапазон дат плана .Where(cg => cg.ChangedAt >= plan.StartAt && cg.ChangedAt <= plan.EndAt) // И сортируем их по дате diff --git a/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs b/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs index cd5d76e..3f7a57a 100644 --- a/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs +++ b/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs @@ -12,7 +12,8 @@ public static class ChangeRecordMapper Sum = changeRecord.Sum, ChangedAt = changeRecord.ChangedAt, SpendingGroupId = changeRecord.SpendingGroupId, - UserId = changeRecord.UserId + UserId = changeRecord.UserId, + SpendingGroupName = changeRecord.SpendingGroup?.Name ?? string.Empty }; public static ChangeRecord ToModel(this ChangeRecordDto changeRecord) From 0bdf0c9f6c66ca7c417b9d8dd903b94d5dce6260 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 9 Dec 2024 23:31:42 +0400 Subject: [PATCH 06/13] =?UTF-8?q?add:=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/ReportOffsetServiceTests.cs | 56 +++++++++++++++++++ .../Reports/ReportPeriodServiceTests.cs | 41 ++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 back/Services.Tests/Reports/ReportOffsetServiceTests.cs create mode 100644 back/Services.Tests/Reports/ReportPeriodServiceTests.cs diff --git a/back/Services.Tests/Reports/ReportOffsetServiceTests.cs b/back/Services.Tests/Reports/ReportOffsetServiceTests.cs new file mode 100644 index 0000000..74824d8 --- /dev/null +++ b/back/Services.Tests/Reports/ReportOffsetServiceTests.cs @@ -0,0 +1,56 @@ +using Contracts.DTO; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.ViewModels; +using Moq; +using Services.Reports; +using Services.Support.Exceptions; + +namespace Services.Tests.Reports; + +public class ReportOffsetServiceTests +{ + [Fact] + public void GetReportData_WhenSpendingGroupNotFound_ThenThrowsEntityNotFoundException() + { + var spendingGroupRepoMock = new Mock(); + spendingGroupRepoMock.Setup(repo => repo.GetByPlan(It.IsAny())).ReturnsAsync((SpendingGroupDto)null); + var reportOffsetService = new ReportOffsetFromPlanService(spendingGroupRepoMock.Object); + + Assert.ThrowsAsync(() => reportOffsetService.GetReportData(new())); + } + + [Fact] + public void GetReportData_WhenSpendingGroupHasNoChangeRecords_ThenThrowsReportDataNotFoundException() + { + var spendingGroupRepoMock = new Mock(); + spendingGroupRepoMock.Setup(repo => repo.GetByPlan(It.IsAny())).ReturnsAsync(new SpendingGroupDto()); + var reportOffsetService = new ReportOffsetFromPlanService(spendingGroupRepoMock.Object); + + Assert.ThrowsAsync(() => reportOffsetService.GetReportData(new())); + } + + [Fact] + public async Task GetReportData_WhenSpendingGroupHasChangeRecords_ThenReturnsSpendingGroupViewModel() + { + var spendingGroupRepoMock = new Mock(); + var spendingGroup = new SpendingGroupDto() + { + ChangeRecords = + [ + new() + { + Id = Guid.NewGuid(), + ChangedAt = DateTime.Now + } + ] + }; + spendingGroupRepoMock.Setup(repo => repo.GetByPlan(It.IsAny())).ReturnsAsync(spendingGroup); + var reportOffsetService = new ReportOffsetFromPlanService(spendingGroupRepoMock.Object); + + var result = await reportOffsetService.GetReportData(new()); + + Assert.NotNull(result); + Assert.IsType(result); + } +} \ No newline at end of file diff --git a/back/Services.Tests/Reports/ReportPeriodServiceTests.cs b/back/Services.Tests/Reports/ReportPeriodServiceTests.cs new file mode 100644 index 0000000..5b7fc35 --- /dev/null +++ b/back/Services.Tests/Reports/ReportPeriodServiceTests.cs @@ -0,0 +1,41 @@ +using Contracts.DTO; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.ViewModels; +using Moq; +using Services.Reports; +using Services.Support.Exceptions; + +namespace Services.Tests.Reports; + +public class ReportPeriodServiceTests +{ + [Fact] + public void GetReportData_WhenChangeRecordsNotFound_ThenThrowsReportDataNotFoundException() + { + var changeRecordRepoMock = new Mock(); + changeRecordRepoMock.Setup(repo => repo.GetList(It.IsAny())).ReturnsAsync((List)null); + var reportPeriodService = new ReportPeriodService(changeRecordRepoMock.Object); + + Assert.ThrowsAsync(() => reportPeriodService.GetReportData(DateTime.MinValue, DateTime.MaxValue)); + } + + [Fact] + public async Task GetReportData_WhenChangeRecordsFound_ThenReturnsChangeRecordViewModels() + { + var changeRecordRepoMock = new Mock(); + var changeRecords = new List() + { + new() { Id = Guid.NewGuid(), ChangedAt = DateTime.Now }, + new() { Id = Guid.NewGuid(), ChangedAt = DateTime.Now } + }; + changeRecordRepoMock.Setup(repo => repo.GetList(It.IsAny())).ReturnsAsync(changeRecords); + var reportPeriodService = new ReportPeriodService(changeRecordRepoMock.Object); + + var result = await reportPeriodService.GetReportData(DateTime.MinValue, DateTime.MaxValue); + + changeRecordRepoMock.Verify(repo => repo.GetList(It.IsAny()), Times.Once); + Assert.NotNull(result); + Assert.IsType>(result.ToList()); + } +} \ No newline at end of file From 15a39c07a032cd54b1e1af2446fbd31fe1ab1332 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 10 Dec 2024 18:22:11 +0400 Subject: [PATCH 07/13] =?UTF-8?q?add:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82=D1=87=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D1=84=D1=80=D0=BE=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/core/api/Api.ts | 41 ++++++++++++++++++++++- front/src/core/api/data-contracts.ts | 9 ++--- front/src/core/api/http-client.ts | 2 +- front/src/core/services/report-service.ts | 25 ++++++++++++++ front/src/main.ts | 2 ++ 5 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 front/src/core/services/report-service.ts diff --git a/front/src/core/api/Api.ts b/front/src/core/api/Api.ts index 6e6cab0..b8a13a6 100644 --- a/front/src/core/api/Api.ts +++ b/front/src/core/api/Api.ts @@ -165,6 +165,45 @@ export class Api extends HttpClient + this.request({ + path: `/api/Report/period`, + method: "GET", + query: query, + format: "json", + ...params, + }); + /** + * No description + * + * @tags Report + * @name ReportPlanDetail + * @request GET:/api/Report/plan/{id} + * @response `200` `SpendingGroupViewModel` Success + */ + reportPlanDetail = (id: string, params: RequestParams = {}) => + this.request({ + path: `/api/Report/plan/${id}`, + method: "GET", + format: "json", + ...params, + }); /** * No description * @@ -412,7 +451,7 @@ export class Api extends HttpClient { - public baseUrl: string = import.meta.env.VITE_API_URL; + public baseUrl: string = "http://172.29.224.204:5215"; private securityData: SecurityDataType | null = null; private securityWorker?: ApiConfig["securityWorker"]; private abortControllers = new Map(); diff --git a/front/src/core/services/report-service.ts b/front/src/core/services/report-service.ts new file mode 100644 index 0000000..14ddd51 --- /dev/null +++ b/front/src/core/services/report-service.ts @@ -0,0 +1,25 @@ +import { Dayjs } from "dayjs"; +import { Api } from "../api/Api"; +import { SpendingGroupViewModel } from "../api/data-contracts"; + +export class ReportService { + private readonly _api: Api + constructor(api: Api) { + this._api = api; + } + + public async getOffsetFromPlanData(Id: string): Promise { + let res = await this._api.reportPlanDetail(Id); + console.log(res); + return res.data; + } + + public async getPeriodData(from: Dayjs, to: Dayjs): Promise { + let res = await this._api.reportPeriodList({ + from: from.toISOString(), + to: to.toISOString() + }); + console.log(res); + return res.data; + } +} \ No newline at end of file diff --git a/front/src/main.ts b/front/src/main.ts index f18efa1..ff4a586 100644 --- a/front/src/main.ts +++ b/front/src/main.ts @@ -8,6 +8,7 @@ import { AuthService } from './core/services/auth-service' import { ChangeRecordService } from './core/services/change-record-service' import { GroupService } from './core/services/group-service' import { PlanService } from './core/services/plans-service' +import { ReportService } from './core/services/report-service' const app = createApp(App) @@ -20,5 +21,6 @@ app.provide(AuthService.name, new AuthService(api)); app.provide(ChangeRecordService.name, new ChangeRecordService(api)); app.provide(GroupService.name, new GroupService(api)); app.provide(PlanService.name, new PlanService(api)); +app.provide(ReportService.name, new ReportService(api)); app.mount('#app') From 732514300b793a5c92b6f6752613b3bc48dd4540 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 10 Dec 2024 23:59:28 +0400 Subject: [PATCH 08/13] =?UTF-8?q?fix:=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=BD=D0=B0=D1=85=D0=BE=D0=B4=D0=B8=D1=82=20?= =?UTF-8?q?=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B=20=D0=B7=D0=B0=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=82=20=D0=BF=D0=BE=20id=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Infrastructure/Repositories/SpendingGroupRepo.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs index dece4fa..b7a36a3 100644 --- a/back/Infrastructure/Repositories/SpendingGroupRepo.cs +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -102,10 +102,13 @@ public class SpendingGroupRepo : ISpendingGroupRepo query = query.Where(x => x.Id == search.Id); } - if (!string.IsNullOrWhiteSpace(search.Name) && search.UserId.HasValue) + if (search.UserId.HasValue) { - query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase) - && x.UserId == search.UserId); + query = query.Where(x => x.UserId == search.UserId); + if (!string.IsNullOrWhiteSpace(search.Name)) + { + query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase)); + } } } From c9852a5384cf64448e9948df14d9cd5473c79e66 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 11 Dec 2024 00:00:04 +0400 Subject: [PATCH 09/13] =?UTF-8?q?add:=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=20?= =?UTF-8?q?=D0=BE=20=D1=81=D0=BC=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B7=D0=B0=D1=82=D1=80=D0=B0=D1=82=20=D0=BE=D1=82=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=20=D0=BF=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.debug.yml | 24 +-- front/components.d.ts | 3 + front/package-lock.json | 174 +++++++++++++++++++- front/package.json | 3 +- front/src/components/main/Header.vue | 5 +- front/src/components/pages/Groups.vue | 9 +- front/src/components/pages/Home.vue | 2 +- front/src/components/pages/OffsetReport.vue | 90 ++++++++++ front/src/components/pages/Plans.vue | 13 +- front/src/core/api/Api.ts | 2 +- front/src/core/api/http-client.ts | 2 +- front/src/core/services/report-service.ts | 44 ++++- front/src/router.ts | 7 +- 13 files changed, 352 insertions(+), 26 deletions(-) create mode 100644 front/src/components/pages/OffsetReport.vue diff --git a/docker-compose.debug.yml b/docker-compose.debug.yml index 02d8292..32a0470 100644 --- a/docker-compose.debug.yml +++ b/docker-compose.debug.yml @@ -16,17 +16,17 @@ services: - ~/.vsdbg:/remote_debugger:rw depends_on: - database - dombudg: - image: dombudg - build: - context: front - dockerfile: ./Dockerfile - environment: - - VITE_API_URL=http://api:5125 - ports: - - 80:80 - depends_on: - - api + # dombudg: + # image: dombudg + # build: + # context: front + # dockerfile: ./Dockerfile + # environment: + # - VITE_API_URL=http://api:5125 + # ports: + # - 80:80 + # depends_on: + # - api database: image: postgres:14 environment: @@ -35,6 +35,8 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data + ports: + - 5432:5432 volumes: postgres_data: diff --git a/front/components.d.ts b/front/components.d.ts index 55d7595..ec0d073 100644 --- a/front/components.d.ts +++ b/front/components.d.ts @@ -23,11 +23,14 @@ declare module 'vue' { ASpace: typeof import('ant-design-vue/es')['Space'] ASpin: typeof import('ant-design-vue/es')['Spin'] ATable: typeof import('ant-design-vue/es')['Table'] + ATypographyText: typeof import('ant-design-vue/es')['TypographyText'] + ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle'] ChangeRecordManager: typeof import('./src/components/support/ChangeRecordManager.vue')['default'] Groups: typeof import('./src/components/pages/Groups.vue')['default'] Header: typeof import('./src/components/main/Header.vue')['default'] Home: typeof import('./src/components/pages/Home.vue')['default'] Login: typeof import('./src/components/pages/Login.vue')['default'] + OffsetReport: typeof import('./src/components/pages/OffsetReport.vue')['default'] PlanManager: typeof import('./src/components/support/PlanManager.vue')['default'] Plans: typeof import('./src/components/pages/Plans.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/front/package-lock.json b/front/package-lock.json index c1ae6fb..6ec091f 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -13,7 +13,8 @@ "dayjs": "^1.11.13", "pinia": "^2.2.8", "vue": "^3.5.12", - "vue-router": "^4.5.0" + "vue-router": "^4.5.0", + "vue3-charts": "^1.1.33" }, "devDependencies": { "@vitejs/plugin-vue": "^5.1.4", @@ -1443,6 +1444,136 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -1726,6 +1857,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2308,6 +2448,16 @@ ], "license": "MIT" }, + "node_modules/ramda": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz", + "integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2895,6 +3045,28 @@ "vue": "^3.0.0" } }, + "node_modules/vue3-charts": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/vue3-charts/-/vue3-charts-1.1.33.tgz", + "integrity": "sha512-gu2N/oORcAWLo3orfoKz5CRohZdmxQP7k2SZ8cgRsD9hFmYpekesE41EUPdGuZ5Y9gAo2LbGYW7fmIGbbPezDg==", + "dependencies": { + "d3-array": "^3.2.0", + "d3-axis": "^3.0.0", + "d3-format": "^3.1.0", + "d3-hierarchy": "^3.1.2", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-selection": "^3.0.0", + "d3-shape": "^3.1.0", + "ramda": "^0.28.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "vue": ">=3.0.0" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/front/package.json b/front/package.json index 9dae701..6552556 100644 --- a/front/package.json +++ b/front/package.json @@ -15,7 +15,8 @@ "dayjs": "^1.11.13", "pinia": "^2.2.8", "vue": "^3.5.12", - "vue-router": "^4.5.0" + "vue-router": "^4.5.0", + "vue3-charts": "^1.1.33" }, "devDependencies": { "@vitejs/plugin-vue": "^5.1.4", diff --git a/front/src/components/main/Header.vue b/front/src/components/main/Header.vue index d2d0563..063d911 100644 --- a/front/src/components/main/Header.vue +++ b/front/src/components/main/Header.vue @@ -3,6 +3,7 @@ import { inject } from 'vue'; import { useUserStore } from '../../store'; import { AuthService } from '../../core/services/auth-service'; import router from '../../router'; +import { HomeOutlined, BlockOutlined } from '@ant-design/icons-vue'; const store = useUserStore(); const authService = inject(AuthService.name) as AuthService; @@ -19,8 +20,8 @@ function logout() {
ДомБюдж
diff --git a/front/src/components/pages/Groups.vue b/front/src/components/pages/Groups.vue index 8781ced..43b2c9b 100644 --- a/front/src/components/pages/Groups.vue +++ b/front/src/components/pages/Groups.vue @@ -3,7 +3,7 @@ import { useAsyncState } from '@vueuse/core'; import { inject } from 'vue'; import { GroupService } from '../../core/services/group-service'; import SpendingGroupManager from '../support/SpendingGroupManager.vue'; -import { DeleteOutlined } from '@ant-design/icons-vue'; +import { DeleteOutlined, CalendarOutlined } from '@ant-design/icons-vue'; const groupService = inject(GroupService.name) as GroupService; @@ -43,12 +43,15 @@ const onDelete = (key: string) => {