diff --git a/.gitignore b/.gitignore
index 52720ca..e8e8bdc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -364,4 +364,5 @@ FodyWeavers.xsd
appsettings.json
appsettings.Development.json
+appsettings.example.json
/.idea
diff --git a/AonFreelancing.csproj b/AonFreelancing.csproj
index fd58373..88ae984 100644
--- a/AonFreelancing.csproj
+++ b/AonFreelancing.csproj
@@ -19,4 +19,8 @@
+
+
+
+
diff --git a/Contexts/MainAppContext.cs b/Contexts/MainAppContext.cs
index fac806e..106f444 100644
--- a/Contexts/MainAppContext.cs
+++ b/Contexts/MainAppContext.cs
@@ -1,4 +1,5 @@
-using AonFreelancing.Models;
+
+using AonFreelancing.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Reflection.Emit;
@@ -6,7 +7,7 @@
namespace AonFreelancing.Contexts
{
- public class MainAppContext(DbContextOptions contextOptions)
+ public class MainAppContext(DbContextOptions contextOptions)
: IdentityDbContext(contextOptions)
{
// For TPT design, no need to define each one
@@ -20,20 +21,23 @@ public class MainAppContext(DbContextOptions contextOptions)
public DbSet TempUsers { get; set; }
public DbSet Bids { get; set; }
public DbSet Tasks { get; set; }
-
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ optionsBuilder.UseSqlServer("aon");
+ }
protected override void OnModelCreating(ModelBuilder builder)
{
-
+
// For TPT design
builder.Entity().ToTable("AspNetUsers")
- .HasIndex(u=>u.PhoneNumber).IsUnique();
+ .HasIndex(u => u.PhoneNumber).IsUnique();
builder.Entity().ToTable("TempUser")
- .HasIndex(u=>u.PhoneNumber).IsUnique();
-
+ .HasIndex(u => u.PhoneNumber).IsUnique();
+
builder.Entity().ToTable("Freelancers");
builder.Entity().ToTable("Clients");
builder.Entity().ToTable("SystemUsers");
- builder.Entity().ToTable("otps", o => o.HasCheckConstraint("CK_CODE","LEN([Code]) = 6"));
+ builder.Entity().ToTable("otps", o => o.HasCheckConstraint("CK_CODE", "LEN([Code]) = 6"));
//set up relationships
builder.Entity().HasOne()
@@ -42,11 +46,11 @@ protected override void OnModelCreating(ModelBuilder builder)
.HasPrincipalKey(nameof(TempUser.PhoneNumber));
builder.Entity().ToTable("Projects", tb => tb.HasCheckConstraint("CK_PRICE_TYPE", "[PriceType] IN ('Fixed', 'PerHour')"));
-
+
builder.Entity()
.ToTable("Projects", tb => tb.HasCheckConstraint("CK_QUALIFICATION_NAME", "[QualificationName] IN ('uiux', 'frontend', 'mobile', 'backend', 'fullstack')"));
builder.Entity().ToTable("Projects", tb => tb.HasCheckConstraint("CK_STATUS", "[Status] IN ('Available', 'Closed')"))
- .Property(p=>p.Status).HasDefaultValue("Available");
+ .Property(p => p.Status).HasDefaultValue("Available");
builder.Entity()
.HasOne(b => b.Project)
diff --git a/Controllers/Mobile/v1/AuthController.cs b/Controllers/Mobile/v1/AuthController.cs
index b7a2d3a..7045f71 100644
--- a/Controllers/Mobile/v1/AuthController.cs
+++ b/Controllers/Mobile/v1/AuthController.cs
@@ -3,7 +3,7 @@
using AonFreelancing.Models.DTOs;
using AonFreelancing.Models.Requests;
using AonFreelancing.Models.Responses;
-using AonFreelancing.Services;
+using AonFreelancing.Models.Services;
using AonFreelancing.Utilities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
@@ -37,6 +37,48 @@ JwtService jwtService
_jwtService = jwtService;
}
+
+ [HttpPost("RegisterByPhone")]
+
+ public async Task RegisterPhoneNumberAsync([FromBody] RegistByPhoneNumberDTO reg)
+ {
+ var user= await _mainAppContext.TempUsers.Where(ph=>ph.PhoneNumber==reg.PhoneNumber).FirstOrDefaultAsync();
+
+
+ if (user != null && !user.PhoneNumberConfirmed==false)
+ {
+
+ return Unauthorized(CreateErrorResponse(StatusCodes.Status401Unauthorized.ToString(), "Verify Your Account First"));
+ }
+
+ user = new TempUser() { PhoneNumber=reg.PhoneNumber };
+
+ await _mainAppContext.TempUsers.AddAsync(user);
+
+ string otpCode = _otpManager.GenerateOtp();
+ await _mainAppContext.SaveChangesAsync();
+
+ OTP otp = new OTP()
+ {
+ Code = otpCode,
+ PhoneNumber = reg.PhoneNumber,
+ CreatedDate = DateTime.Now,
+ ExpiresAt = DateTime.Now.AddMinutes(1),
+ };
+
+
+ //send the otp to the specified phone number
+ await _otpManager.SendOTPAsync(otp.Code, reg.PhoneNumber);
+ await _mainAppContext.OTPs.AddAsync(otp);
+ await _mainAppContext.SaveChangesAsync();
+
+
+
+ return Ok();
+
+
+ }
+
[HttpPost("sendVerificationCode")]
public async Task SendVerificationCodeAsync([FromBody] PhoneNumberReq phoneNumberReq)
{
diff --git a/Controllers/Mobile/v1/FreelancersController.cs b/Controllers/Mobile/v1/FreelancersController.cs
index 9a749d8..1278c6c 100644
--- a/Controllers/Mobile/v1/FreelancersController.cs
+++ b/Controllers/Mobile/v1/FreelancersController.cs
@@ -10,7 +10,8 @@
namespace AonFreelancing.Controllers.Mobile.v1
{
- [Authorize]
+ [Authorize ]
+
[Route("api/mobile/v1/freelancers")]
[ApiController]
public class FreelancersController : BaseController
diff --git a/Controllers/Mobile/v1/ProjectsController.cs b/Controllers/Mobile/v1/ProjectsController.cs
index eae2346..437f82b 100644
--- a/Controllers/Mobile/v1/ProjectsController.cs
+++ b/Controllers/Mobile/v1/ProjectsController.cs
@@ -1,7 +1,9 @@
+
using AonFreelancing.Contexts;
using AonFreelancing.Models;
using AonFreelancing.Models.DTOs;
+using AonFreelancing.Models.Services;
using AonFreelancing.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@@ -13,15 +15,16 @@ namespace AonFreelancing.Controllers.Mobile.v1
[Authorize]
[Route("api/mobile/v1/projects")]
[ApiController]
- public class ProjectsController(MainAppContext mainAppContext, UserManager userManager) : BaseController
+ public class ProjectsController(MainAppContext mainAppContext, UserManager userManager,TaskService taskService) : BaseController
{
+
[Authorize(Roles = "CLIENT")]
[HttpPost]
public async Task PostProjectAsync([FromBody] ProjectInputDto projectInputDto)
{
- if(!ModelState.IsValid)
+ if (!ModelState.IsValid)
{
-
+
return base.CustomBadRequest();
}
var user = await userManager.GetUserAsync(HttpContext.User);
@@ -40,6 +43,7 @@ public async Task PostProjectAsync([FromBody] ProjectInputDto pro
Budget = projectInputDto.Budget,
PriceType = projectInputDto.PriceType,
CreatedAt = DateTime.Now,
+
};
await mainAppContext.Projects.AddAsync(project);
@@ -62,12 +66,12 @@ public async Task GetClientFeedAsync(
var count = await query.CountAsync();
- if(!string.IsNullOrEmpty(trimmedQuery))
+ if (!string.IsNullOrEmpty(trimmedQuery))
{
query = query
- .Where(p=>p.Title.ToLower().Contains(trimmedQuery));
+ .Where(p => p.Title.ToLower().Contains(trimmedQuery));
}
- if(qualificationNames != null && qualificationNames.Count >0)
+ if (qualificationNames != null && qualificationNames.Count > 0)
{
query = query
.Where(p => qualificationNames.Contains(p.QualificationName));
@@ -78,7 +82,7 @@ public async Task GetClientFeedAsync(
.Take(pageSize)
.Select(p => new ProjectOutDTO
{
- Id= p.Id,
+ Id = p.Id,
Title = p.Title,
Description = p.Description,
Status = p.Status,
@@ -92,10 +96,11 @@ public async Task GetClientFeedAsync(
CreationTime = StringOperations.GetTimeAgo(p.CreatedAt)
})
.ToListAsync();
-
- return Ok(CreateSuccessResponse(new {
- Total=count,
- Items=projects
+
+ return Ok(CreateSuccessResponse(new
+ {
+ Total = count,
+ Items = projects
}));
}
@@ -133,7 +138,7 @@ public async Task SubmitBidAsync(int id, [FromBody] BidInputDto b
FreelancerId = user.Id,
ProposedPrice = bidDto.ProposedPrice,
Notes = bidDto.Notes,
- Status = "pending",
+ Status = "pending",
SubmittedAt = DateTime.Now
};
@@ -181,22 +186,24 @@ public async Task GetProjectDetailsAsync(int id)
var orderedBids = project.Bids
.OrderByDescending(b => b.ProposedPrice)
- .Select(b => new BidOutDto {
- Id = b.Id,
- FreelancerId = b.FreelancerId,
- Freelancer = new FreelancerShortOutDTO {
- Id = b.FreelancerId,
- Name = b.Freelancer.Name
- },
- ProposedPrice = b.ProposedPrice,
- Notes = b.Notes,
- Status = b.Status,
- SubmittedAt = b.SubmittedAt,
- ApprovedAt = b.ApprovedAt
- } );
-
-
-
+ .Select(b => new BidOutDto
+ {
+ Id = b.Id,
+ FreelancerId = b.FreelancerId,
+ Freelancer = new FreelancerShortOutDTO
+ {
+ Id = b.FreelancerId,
+ Name = b.Freelancer.Name
+ },
+ ProposedPrice = b.ProposedPrice,
+ Notes = b.Notes,
+ Status = b.Status,
+ SubmittedAt = b.SubmittedAt,
+ ApprovedAt = b.ApprovedAt
+ });
+
+
+
return Ok(CreateSuccessResponse(new
{
project.Id,
@@ -204,7 +211,7 @@ public async Task GetProjectDetailsAsync(int id)
project.Status,
project.Budget,
project.Duration,
- project.Description,
+ project.Description,
Bids = orderedBids
}));
}
@@ -233,109 +240,8 @@ public async Task CreateTaskAsync(int id, [FromBody] TaskInputDto
}
- [Authorize(Roles = "CLIENT, FREELANCER")]
- [HttpPut("tasks/{id}")]
- public async Task UpdateTaskStatusAsync(int id, [FromBody] TaskStatusDto taskStatusDto)
- {
- var task = await mainAppContext.Tasks.FindAsync(id);
- if (task == null)
- {
- var errorResponse = new ApiResponse
- {
- IsSuccess = false,
- Results = null,
- Errors = new List
- {
- new Error { Code = "404", Message = "Task not found." }
- }
- };
- return NotFound(errorResponse);
- }
-
- var validStatuses = new List { "to-do", "in-progress", "in-review", "done" };
-
- if (!validStatuses.Contains(taskStatusDto.NewStatus.ToLower()))
- {
- var errorResponse = new ApiResponse
- {
- IsSuccess = false,
- Results = null,
- Errors = new List
- {
- new Error { Code = "400", Message = "Invalid status provided." }
- }
- };
- return BadRequest(errorResponse);
- }
-
- if (task.Status.ToLower() == "to do" && taskStatusDto.NewStatus.ToLower() != "in progress")
- {
- var errorResponse = new ApiResponse
- {
- IsSuccess = false,
- Results = null,
- Errors = new List
- {
- new Error { Code = "400", Message = "Invalid status transition from 'To Do'." }
- }
- };
- return BadRequest(errorResponse);
- }
- if (task.Status.ToLower() == "in progress" &&
- taskStatusDto.NewStatus.ToLower() != "in review" && taskStatusDto.NewStatus.ToLower() != "done")
- {
- var errorResponse = new ApiResponse
- {
- IsSuccess = false,
- Results = null,
- Errors = new List
- {
- new Error { Code = "400", Message = "Invalid status transition from 'In Progress'." }
- }
- };
- return BadRequest(errorResponse);
- }
- if (task.Status.ToLower() == "in review" && taskStatusDto.NewStatus.ToLower() != "done")
- {
- var errorResponse = new ApiResponse
- {
- IsSuccess = false,
- Results = null,
- Errors = new List
- {
- new Error { Code = "400", Message = "Invalid status transition from 'In Review'." }
- }
- };
- return BadRequest(errorResponse);
- }
- if (task.Status.ToLower() == "done")
- {
- var errorResponse = new ApiResponse
- {
- IsSuccess = false,
- Results = null,
- Errors = new List
- {
- new Error { Code = "400", Message = "No further status transitions allowed from 'Done'." }
- }
- };
- return BadRequest(errorResponse);
- }
-
- task.Status = taskStatusDto.NewStatus;
-
- task.CompletedAt = taskStatusDto.NewStatus.ToLower() == "done" ? DateTime.UtcNow : (DateTime?)null;
-
- await mainAppContext.SaveChangesAsync();
-
- var successResponse = new ApiResponse
- {
- IsSuccess = true,
- Results = "Task status updated.",
- Errors = null
- };
- return Ok(successResponse);
- }
+
+
[Authorize(Roles = "CLIENT")]
@@ -427,4 +333,4 @@ public async Task UploadProjectImage(int id, IFormFile file)
//}
}
-}
\ No newline at end of file
+}
diff --git a/Controllers/Mobile/v1/TaskController.cs b/Controllers/Mobile/v1/TaskController.cs
new file mode 100644
index 0000000..17ec5c6
--- /dev/null
+++ b/Controllers/Mobile/v1/TaskController.cs
@@ -0,0 +1,183 @@
+using AonFreelancing.Contexts;
+using AonFreelancing.Models;
+using AonFreelancing.Models.DTOs;
+using AonFreelancing.Models.Services;
+using AonFreelancing.Utilities;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace AonFreelancing.Controllers.Mobile.v1
+{
+ [Authorize]
+ [Route("api/mobile/v1/Task")]
+ [ApiController]
+ public class TaskController(MainAppContext mainAppContext, UserManager userManager,TaskService taskService) : BaseController
+ {
+
+
+
+ [HttpPut("{id}/UpdateTask")]
+ public async TaskUpdateTask(int id, [FromBody]TaskInputDto T)
+ {
+
+ if (!ModelState.IsValid)
+ return BadRequest(CreateErrorResponse("400","enter vaild Input"));
+
+ var task=await mainAppContext .Tasks.Where(t=>t.Id==id).FirstOrDefaultAsync();
+ if (task != null) {
+ task.Name = T.Name;
+ task.DeadlineAt=T.DeadlineAt;
+ task.Notes = T.Notes;
+
+ mainAppContext.Tasks.Update(task);
+ mainAppContext.SaveChanges();
+
+
+ }
+
+ return NotFound(CreateErrorResponse("404","the Task Not Found"));
+
+
+ }
+ [HttpGet("{id}/GEtTask")]
+ public async Task GetTasks(long id,string status)
+ {
+ if (string.IsNullOrEmpty(status))
+ {
+
+ return BadRequest("Status is requird");
+
+ }
+ var task= await mainAppContext.Tasks.Where(t => t.ProjectId == id && t.Status == status).ToListAsync();
+ if (task != null) {
+
+ return Ok(CreateSuccessResponse