Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/Application/Abstractions/Data/IApplicationDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
using Domain.Applications;
using Domain.Applications;
using Domain.Areas;
using Domain.AuditLogs;
using Domain.Businesses;
using Domain.BusinessMembers;
using Domain.Countries;
using Domain.Customers;
using Domain.Districts;
using Domain.EmailVerification;
using Domain.Localities;
using Domain.MfaLogs;
using Domain.MfaSettings;
using Domain.Otps;
using Domain.PasswordResets;
using Domain.Permissions;
using Domain.Regions;
using Domain.RolePermissions;
using Domain.Roles;
using Domain.SmtpConfigs;
Expand Down Expand Up @@ -45,6 +49,10 @@ public interface IApplicationDbContext
DbSet<Otp> Otp { get; }
DbSet<SmtpConfig> SmtpConfig { get; }
DbSet<Country> Countries { get; }
DbSet<Region> Regions { get; }
DbSet<District> Districts { get; }
DbSet<Area> Areas { get; }
DbSet<Locality> Localities { get; }
EntityEntry Entry(object entity);
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
4 changes: 4 additions & 0 deletions src/Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@
<InternalsVisibleTo Include="Application.UnitTests" />
</ItemGroup>

<ItemGroup>
<Folder Include="Applications\Delete\" />
</ItemGroup>

Comment on lines +25 to +28
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty folder item in project file: The Applications\Delete folder item appears to be empty or incorrectly configured. This should either be removed or the proper files should be added to this folder.

Suggested change
<ItemGroup>
<Folder Include="Applications\Delete\" />
</ItemGroup>

Copilot uses AI. Check for mistakes.
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using SharedKernel;

namespace Application.Applications.Delete;

public sealed class DeleteApplicationCommandHandler
: ICommandHandler<DeleteApplicationCommand>
{
Expand Down
12 changes: 12 additions & 0 deletions src/Application/Areas/Create/CreateAreaCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Application.Abstractions.Messaging;
using Domain.Areas;

namespace Application.Areas.Create;

public sealed record CreateAreaCommand(
Guid CountryId,
Guid DistrictId,
string Name,
Area.AreaType Type,
bool IsActive
) : ICommand<Guid>;
37 changes: 37 additions & 0 deletions src/Application/Areas/Create/CreateAreaCommandHandler .cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Domain;
using Domain.Areas;
using SharedKernel;

namespace Application.Areas.Create;

public sealed class CreateAreaCommandHandler
: ICommandHandler<CreateAreaCommand, Guid>
{
private readonly IApplicationDbContext _context;

public CreateAreaCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task<Result<Guid>> Handle(
CreateAreaCommand command,
CancellationToken cancellationToken)
{
var area = new Area
{
CountryId = command.CountryId,
DistrictId = command.DistrictId,
Name = command.Name,
Type = command.Type,
IsActive = command.IsActive
};

await _context.Areas.AddAsync(area, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);

return Result.Success(area.Id);
}
}
35 changes: 35 additions & 0 deletions src/Application/Areas/Create/CreateAreaValidator .cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using FluentValidation;

namespace Application.Areas.Create;

public class CreateAreaValidator : AbstractValidator<CreateAreaCommand>
{
public CreateAreaValidator()
{
RuleFor(x => x.CountryId)
.NotEmpty()
.WithMessage("CountryId is required.")
.NotEqual(Guid.Empty)
.WithMessage("CountryId cannot be empty GUID.");

RuleFor(x => x.DistrictId)
.NotEmpty()
.WithMessage("ID is required.")
.NotEqual(Guid.Empty)
.WithMessage("DistrictId cannot be empty GUID.");

RuleFor(x => x.Name)
.NotEmpty()
.WithMessage("Name is required.")
.MaximumLength(255)
.WithMessage("Name must not exceed 255 characters.");

RuleFor(x => x.Type)
.IsInEnum()
.WithMessage(
"Invalid area type. Valid values: 1=Upazila, 2=City, 3=Thana, 4=Municipality, 5=Township."
);
}
}

5 changes: 5 additions & 0 deletions src/Application/Areas/Delete/DeleteAreaCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Application.Abstractions.Messaging;

namespace Application.Areas.Delete;

public sealed record DeleteAreaCommand(Guid Id) : ICommand;
34 changes: 34 additions & 0 deletions src/Application/Areas/Delete/DeleteAreaCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Domain.Areas;
using Microsoft.EntityFrameworkCore;
using SharedKernel;

namespace Application.Areas.Delete;

public sealed class DeleteAreaCommandHandler
: ICommandHandler<DeleteAreaCommand>
{
private readonly IApplicationDbContext _context;

public DeleteAreaCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task<Result> Handle(DeleteAreaCommand command, CancellationToken cancellationToken)
{
Area? area = await _context.Areas
.FirstOrDefaultAsync(a => a.Id == command.Id, cancellationToken);

if (area is null)
{
return Result.Failure("Area not found.");
}

_context.Areas.Remove(area);
await _context.SaveChangesAsync(cancellationToken);

return Result.Success();
}
}
16 changes: 16 additions & 0 deletions src/Application/Areas/Delete/DeleteAreaValidator .cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using FluentValidation;

namespace Application.Areas.Delete;

public class DeleteAreaValidator : AbstractValidator<DeleteAreaCommand>
{
public DeleteAreaValidator()
{
RuleFor(x => x.Id)
.NotEmpty()
.WithMessage("ID is required.")
.NotEqual(Guid.Empty)
.WithMessage("ID cannot be empty GUID.");
}
}
11 changes: 11 additions & 0 deletions src/Application/Areas/Get/AreaResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Application.Areas.Get;

public sealed record AreaResponse(
Guid Id,
Guid CountryId,
Guid DistrictId,
string Name,
int Type,
string TypeName,
bool IsActive
);
6 changes: 6 additions & 0 deletions src/Application/Areas/Get/GetAreaByIdQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System;
using Application.Abstractions.Messaging;

namespace Application.Areas.Get;

public sealed record GetAreaByIdQuery(Guid Id) : IQuery<AreaResponse>;
41 changes: 41 additions & 0 deletions src/Application/Areas/Get/GetAreaByIdQueryHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Microsoft.EntityFrameworkCore;
using SharedKernel;

namespace Application.Areas.Get;

public sealed class GetAreaByIdQueryHandler
: IQueryHandler<GetAreaByIdQuery, AreaResponse>
{
private readonly IApplicationDbContext _context;

public GetAreaByIdQueryHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task<Result<AreaResponse>> Handle(
GetAreaByIdQuery query,
CancellationToken cancellationToken)
{
AreaResponse? area = await _context.Areas.Where(a => a.Id == query.Id)
.Select(a => new AreaResponse(
a.Id,
a.CountryId,
a.DistrictId,
a.Name,
(int)a.Type,
a.Type.ToString(),
a.IsActive
))
.FirstOrDefaultAsync(cancellationToken);

if (area is null)
{
return Result.Failure<AreaResponse>("Area not found.");
}

return Result.Success(area);
}
}
6 changes: 6 additions & 0 deletions src/Application/Areas/GetAll/GetAllAreasQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Application.Abstractions.Messaging;
using Application.Areas.Get;

namespace Application.Areas.GetAll;

public sealed record GetAllAreasQuery() : IQuery<List<AreaResponse>>;
38 changes: 38 additions & 0 deletions src/Application/Areas/GetAll/GetAllAreasQueryHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Application.Areas.Get;
using Application.Areas.GetAll;
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate using directive in namespace: "Application.Areas.GetAll" is imported twice - once at line 3 and again at line 4. Remove the duplicate import.

Suggested change
using Application.Areas.GetAll;

Copilot uses AI. Check for mistakes.
using Microsoft.EntityFrameworkCore;
using SharedKernel;

namespace Application.Areas.GetAll;

public sealed class GetAllAreasQueryHandler
: IQueryHandler<GetAllAreasQuery, List<AreaResponse>>
{
private readonly IApplicationDbContext _context;

public GetAllAreasQueryHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task<Result<List<AreaResponse>>> Handle(
GetAllAreasQuery query,
CancellationToken cancellationToken)
{
List<AreaResponse> areas = await _context.Areas
.Select(a => new AreaResponse(
a.Id,
a.CountryId,
a.DistrictId,
a.Name,
(int)a.Type,
a.Type.ToString(),
a.IsActive
))
.ToListAsync(cancellationToken);

return Result.Success(areas);
}
}
13 changes: 13 additions & 0 deletions src/Application/Areas/Update/UpdateAreaCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Application.Abstractions.Messaging;
using Domain.Areas;

namespace Application.Areas.Update;

public sealed record UpdateAreaCommand(
Guid Id,
Guid CountryId,
Guid DistrictId,
string Name,
Area.AreaType Type,
bool IsActive
) : ICommand<Guid>;
55 changes: 55 additions & 0 deletions src/Application/Areas/Update/UpdateAreaCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Domain.Areas;
using Microsoft.EntityFrameworkCore;
using SharedKernel;

namespace Application.Areas.Update;

public sealed class UpdateAreaCommandHandler
: ICommandHandler<UpdateAreaCommand, Guid>
{
private readonly IApplicationDbContext _context;

public UpdateAreaCommandHandler(IApplicationDbContext context)
{
_context = context;
}

public async Task<Result<Guid>> Handle(
UpdateAreaCommand command,
CancellationToken cancellationToken)
{
Area? area = await _context.Areas
.FirstOrDefaultAsync(a => a.Id == command.Id, cancellationToken);

if (area is null)
{
return Result.Failure<Guid>("Area not found.");
}

// Check if area name is unique within the same district (excluding current area)
bool areaNameExists = await _context.Areas
.AnyAsync(a =>
a.DistrictId == command.DistrictId &&
a.Name == command.Name &&
a.Id != command.Id,
cancellationToken);

if (areaNameExists)
{
return Result.Failure<Guid>("Area name already exists in this district.");
}

// Update properties
area.CountryId = command.CountryId;
area.DistrictId = command.DistrictId;
area.Name = command.Name;
area.Type = command.Type;
area.IsActive = command.IsActive;

await _context.SaveChangesAsync(cancellationToken);

return Result.Success(area.Id);
}
}
Loading