diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e40201c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,3 @@ +- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools. +- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first. +- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it. diff --git a/DataAccess/InstitutionDAO.cs b/DataAccess/InstitutionDAO.cs index eb3c202..db59e38 100644 --- a/DataAccess/InstitutionDAO.cs +++ b/DataAccess/InstitutionDAO.cs @@ -56,15 +56,22 @@ public IQueryable AllInstitutions() institutionName = institutionName.Trim(); institutionAddress = institutionAddress.Trim(); - var institution = await db.Institutions.AsNoTracking() + try + { + var institution = await db.Institutions.AsNoTracking() .FirstOrDefaultAsync(i => i.InstitutionName.Contains(institutionName) && i.InstitutionAddress.Contains(institutionAddress)); - return institution; + return institution; + } + catch (Exception ex) + { + throw new Exception(); + } } #endregion #region Get Institution By ID - public async Task GetInstitutionByID(int institutionId) + public async Task GetInstitutionByID(int? institutionId) { var institution = await db.Institutions.FirstOrDefaultAsync(u => u.InstitutionId == institutionId); return institution; @@ -87,7 +94,7 @@ public async Task UpdateInstitution() #endregion #region Delete Institution - public async Task DeleteInstitution(int institutionId) + public async Task DeleteInstitution(int? institutionId) { return false; } diff --git a/DataAccess/InstitutionRequestDAO.cs b/DataAccess/InstitutionRequestDAO.cs new file mode 100644 index 0000000..64279cc --- /dev/null +++ b/DataAccess/InstitutionRequestDAO.cs @@ -0,0 +1,112 @@ +using Microsoft.EntityFrameworkCore; +using ObjectBusiness; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataAccess +{ + public class InstitutionRequestDAO + { + #region Variables + private readonly BackToMeDbContext db; + #endregion + + #region Constructor + public InstitutionRequestDAO(BackToMeDbContext db) + { + this.db = db; + } + #endregion + + #region Create Institution Request + public async Task CreateInstitutionRequest(InstitutionRequest institutionRequest) + { + institutionRequest.InstitutionRequestId = new Random().Next(); + var isAdded = db.InstitutionRequests.Add(institutionRequest); + if (isAdded != null) + { + await db.SaveChangesAsync(); + return true; + } + return false; + } + #endregion + + #region All Institution Requests + public IQueryable AllInstitutionRequests() + { + var listRequests = db.InstitutionRequests.Where(r => r.IsVerifiedEmail).AsNoTracking() + .OrderByDescending(u => u.InstitutionName); + + return listRequests; + } + #endregion + + #region Get Institution Request By ID + public async Task GetInstitutionRequestByID(int requestId) + { + var institutionRequest = await db.InstitutionRequests.FirstOrDefaultAsync(r => r.InstitutionRequestId == requestId); + return institutionRequest; + } + #endregion + + #region Get Institution Request By Work Email + public async Task GetInstitutionRequestByWorkEmail(string workEmail) + { + var institutionRequest = await db.InstitutionRequests.FirstOrDefaultAsync(r => r.WorkEmail == workEmail); + return institutionRequest; + } + #endregion + + #region Get Institution Request By Name and Address + public async Task GetInstitutionRequestByNameAndAddress(string institutionName, string institutionAddress) + { + if (string.IsNullOrWhiteSpace(institutionName) || string.IsNullOrWhiteSpace(institutionAddress)) + { + return null; + } + + institutionName = institutionName.Trim(); + institutionAddress = institutionAddress.Trim(); + + try + { + var canConnect = await db.Database.CanConnectAsync(); + var request = await db.InstitutionRequests.AsNoTracking() + .FirstOrDefaultAsync(i => i.InstitutionName.Contains(institutionName) && + i.InstitutionAddress.Contains(institutionAddress)); + return request; + } + catch (Exception) + { + throw; + } + } + #endregion + + #region Update Institution Request + public async Task UpdateInstitutionRequest() + { + try + { + await db.SaveChangesAsync(); + return true; + } + catch (Exception ex) + { + return false; + } + } + #endregion + + #region Delete Institution Request + public async Task DeleteInstitutionRequest(int? requestId) + { + return false; + } + #endregion + } +} diff --git a/DataAccess/PostDAO.cs b/DataAccess/PostDAO.cs index a9b87a3..8544b8b 100644 --- a/DataAccess/PostDAO.cs +++ b/DataAccess/PostDAO.cs @@ -351,7 +351,7 @@ public async Task HandOverAdmin(int postId, int oldUserId) if (post == null) return false; post.OldUserId = oldUserId; - post.UserId = usersDAO.GetAdmin().UserId; + post.UserId = usersDAO.GetSystemAdmin().UserId; post.UpdatedAt = DateTime.Now; try { diff --git a/DataAccess/StudentDAO.cs b/DataAccess/StudentDAO.cs index b07531f..625b78f 100644 --- a/DataAccess/StudentDAO.cs +++ b/DataAccess/StudentDAO.cs @@ -45,7 +45,7 @@ public IQueryable AllStudents() #region Get Student By Id public async Task GetStudentById(int studentId) { - var student = await db.Student.FirstOrDefaultAsync(s => s.StudentId == studentId); + var student = await db.Student.FirstOrDefaultAsync(s => s.UserId == studentId); return student; } #endregion diff --git a/DataAccess/UsersDAO.cs b/DataAccess/UsersDAO.cs index 8a42f6e..3c60689 100644 --- a/DataAccess/UsersDAO.cs +++ b/DataAccess/UsersDAO.cs @@ -63,20 +63,10 @@ on s.UserId equals u.UserId } #endregion - #region Get Admin - public Users GetAdmin() + #region Get System Admin + public Users GetSystemAdmin() { - var user = db.Users.FirstOrDefault(u => u.Role == Role.Admin); - return user; - } - #endregion - - #region Get User By Student Id - public async Task GetUserByStudentId(int studentId) - { - var user = await db.Users - .Include(u => u.Student) - .FirstOrDefaultAsync(s => s.Student.StudentId == studentId); + var user = db.Users.FirstOrDefault(u => u.Role == Role.SystemAdmin); return user; } #endregion diff --git a/LostFound_API/Controllers/InstitutionController.cs b/LostFound_API/Controllers/InstitutionController.cs index 5d02bf2..667f66f 100644 --- a/LostFound_API/Controllers/InstitutionController.cs +++ b/LostFound_API/Controllers/InstitutionController.cs @@ -4,8 +4,15 @@ using LostFound_API.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; using ObjectBusiness; using Repository; +using Services; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; using System.Text.Json; using System.Threading.Tasks; @@ -19,8 +26,11 @@ public class InstitutionController : ControllerBase { #region Variables private readonly IInstitutionRepository institutionRepository; + private readonly IUsersRepository usersRepository; private readonly StateService stateService; + private readonly IMemoryCache memoryCache; private readonly IConfiguration configuration; + private readonly EmailSender emailSender; private readonly IHttpClientFactory httpClientFactory; #endregion @@ -28,16 +38,22 @@ public class InstitutionController : ControllerBase public InstitutionController(IInstitutionRepository institutionRepository, StateService stateService, IConfiguration configuration, - IHttpClientFactory httpClientFactory) + IHttpClientFactory httpClientFactory, + IUsersRepository usersRepository, + EmailSender emailSender, + IMemoryCache memoryCache) { this.institutionRepository = institutionRepository; this.stateService = stateService; this.configuration = configuration; this.httpClientFactory = httpClientFactory; + this.usersRepository = usersRepository; + this.emailSender = emailSender; + this.memoryCache = memoryCache; } #endregion - [HttpGet] + [HttpGet("full-state-name")] public IActionResult Test() { var state = stateService.ByCode["GA"]; @@ -46,164 +62,401 @@ public IActionResult Test() } // GET: api/ - //[HttpGet] - //public IEnumerable Get() - //{ - // return new string[] { "value1", "value2" }; - //} - - // GET api//5 - [HttpGet("search")] - public async Task Search(string query) + [HttpGet] + public ActionResult Get() { - if (string.IsNullOrWhiteSpace(query)) - { - return BadRequest(new - { - message = "Query is required" - }); - } - - var apiKey = configuration["CollegeScorecard:ApiKey"]; - var client = httpClientFactory.CreateClient(); - - var url = - $"https://api.data.gov/ed/collegescorecard/v1/schools" + - $"?api_key={apiKey}" + - $"&school.name={Uri.EscapeDataString(query)}" + - $"&fields=id,school.name,school.address,school.city,school.state,school.zip,school.school_url,location.lat,location.lon" + - $"&per_page=10"; - - // Send GET request to College Scorecard API - var res = await client.GetAsync(url); + var institutions = institutionRepository.AllInstitutions(); + return Ok(institutions); + } - // If API returns errors - if (!res.IsSuccessStatusCode) + #region Sign Up + // POST api/ + [HttpPost("sign-up")] + public async Task CreateInstitution([FromBody] UserDTO userDTO) + { + if (userDTO == null || string.IsNullOrWhiteSpace(userDTO.InstitutionName) || string.IsNullOrWhiteSpace(userDTO.InstitutionAddress)) { - return StatusCode( - (int)res.StatusCode, - "Unable to retrieve institutions."); + return BadRequest("Invalid institution data"); } - var json = await res.Content.ReadAsStringAsync(); - - // Parse JSON to JsonDocument in order to read each property - using var document = JsonDocument.Parse(json); - - // Get property "results" - var results = document.RootElement.GetProperty("results"); + var institution = new Institution + { + InstitutionId = new Random().Next(), + InstitutionAddress = userDTO.InstitutionAddress, + InstitutionCity = userDTO.InstitutionCity, + InstitutionState = userDTO.InstitutionState, + InstitutionName = userDTO.InstitutionName, + InstitutionWebsite = userDTO.InstitutionWebsite, + InstitutionPhone = userDTO.InstitutionPhone, + }; - // Create DTO list to return to frontend. - var institutions = new List(); + var isAddedInstitution = await institutionRepository.CreateInstitution(institution); - foreach (var item in results.EnumerateArray()) + if (isAddedInstitution) { - institutions.Add(new InstitutionSearchDTO + var userSignUp = new Users { - // GET "id" field - // - // JSON: - // - // "id":166027 - // - // => - // int - Id = item.TryGetProperty("id", out var id) - && id.ValueKind == JsonValueKind.Number - ? id.GetInt32() : 0, - - // GET school name. - // - // JSON: - // - // "school.name":"Georgia State University" - // - // if null then return "" - Name = item.GetProperty("school.name").GetString() ?? "", - - // TryGetProperty to skip throw exception - // if property does not exist. - // - // If exist: - // get Address. - // - // If no: - // return null. - Address = item.TryGetProperty("school.address", out var address) - ? address.GetString() : null, - - // City - City = item.TryGetProperty("school.city", out var city) - ? city.GetString() : null, - - // State - State = item.TryGetProperty("school.state", out var state) - ? state.GetString() : null, - - // Zip Code - ZipCode = item.TryGetProperty("school.zip", out var zip) - ? zip.GetString() : null, - - // Website - Website = item.TryGetProperty("school.school_url", out var website) - ? website.GetString() : null, - - // Latitude - Latitude = item.TryGetProperty("location.lat", out var lat) - && lat.ValueKind == JsonValueKind.Number - ? lat.GetDouble() : null, - - // Longitude - Longitude = item.TryGetProperty("location.lon", out var lon) - && lon.ValueKind == JsonValueKind.Number - ? lon.GetDouble() : null, - - }); + UserId = new Random().Next(), + Email = userDTO.Email, + FirstName = userDTO.FirstName, + LastName = userDTO.LastName, + InstitutionId = institution.InstitutionId, + Password = userDTO.Password, + PickImage1 = userDTO.PickImage1, + PickImage2 = userDTO.PickImage2, + Role = Role.Student, + }; + + var isAddedUser = await usersRepository.SignUp(userSignUp); + if (isAddedUser) + { + // Send verification code via email + var token = Guid.NewGuid().ToString(); + memoryCache.Set($"EMAIL_VERIFY_{token}", userDTO.Email, TimeSpan.FromMinutes(15)); + + string senderName = "Back2Me"; + string senderEmail = "mycampuslostfound@gmail.com"; + string toName = institution.InstitutionName; + string toEmail = userDTO.Email; + string subject = "🏫 Verify Your Institution Registration"; + + string content = $@" + + + + + + + +
+ +
+

🏫 Verify Your Institution Registration

+
+ +
+ +

Hello,

+ +

+ Thank you for registering + {institution.InstitutionName} at {institution.InstitutionAddress} Campus + with Back2Me. +

+ +

+ To activate your institution and complete the registration process, + please verify your administrator email by clicking the button below. +

+ +
+ ⚡ This verification link will expire in 15 minutes. +
+ +

+ + Verify Institution + +

+ +

+ If you did not submit this registration request, + you can safely ignore this email. +

+ +
+ + + +
+ + + "; + + await emailSender.SendEmail(senderName, senderEmail, toName, toEmail, subject, content); + + // JWT config + var issuer = configuration["JwtConfig:Issuer"]; + var audience = configuration["JwtConfig:Audience"]; + var key = configuration["JwtConfig:Key"]; + var tokenValidityMins = configuration.GetValue("JwtConfig:TokenValidityMins"); + var tokenExpiryTimeStamp = DateTime.UtcNow.AddMinutes(tokenValidityMins); // Token expiration time + + // Create JWT access token and assign token + var tokenDescriptor = new SecurityTokenDescriptor // Describe token + { + Subject = new ClaimsIdentity(new[] + { + new Claim(ClaimTypes.NameIdentifier, userDTO.Email), + new Claim(ClaimTypes.Role, userDTO.Role.ToString()), + }), + Issuer = issuer, + Audience = audience, + Expires = tokenExpiryTimeStamp, + SigningCredentials = new SigningCredentials( + new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)), + SecurityAlgorithms.HmacSha256Signature + ) + }; + + // Process token was described + var tokenHandler = new JwtSecurityTokenHandler(); + var securityToken = tokenHandler.CreateToken(tokenDescriptor); // Create object JWT Token + var accessToken = tokenHandler.WriteToken(securityToken); // Serialize token to string for client + + // Assign token for client + userDTO.AccessToken = accessToken; + userDTO.ExpiresIn = (int)tokenExpiryTimeStamp.Subtract(DateTime.Now).TotalSeconds; + + return Ok(new + { + Message = "Created institution successfully" + }); + } } - // return List to frontend. - // - // Frontend will receive: - // - // [ - // { - // id:166027, - // name:"Georgia State University", - // address:"33 Gilmer St SE", - // city:"Atlanta", - // state:"GA", - // zipCode:"30303", - // website:"www.gsu.edu", - // latitude:33.75, - // longitude:-84.38 - // } - // ] - - return Ok(institutions); + return BadRequest("Create institution failed"); } + #endregion - // POST api/ - [Authorize] - [HttpPost] - public async Task CreateInstitution([FromBody] Institution institution) + #region Verify Institution + [HttpGet("verify-institution")] + public async Task VerifyEmail([FromQuery] string token, [FromQuery] bool isForgotPassword) { - if (institution == null || string.IsNullOrWhiteSpace(institution.InstitutionName) || string.IsNullOrWhiteSpace(institution.InstitutionAddress)) + try { - return BadRequest("Invalid institution data"); - } + if (!memoryCache.TryGetValue($"EMAIL_VERIFY_{token}", out string userEmail)) + { + return BadRequest(new + { + Message = "Invalid or expired verification link" + }); + } - var isAddedInstitution = await institutionRepository.CreateInstitution(institution); + var user = await usersRepository.GetUserByEmail(userEmail); + + // Seperate send email is verified and don't send for forgot password + if (!isForgotPassword) + { + user.IsVerifiedEmail = true; + var isUpdated = await usersRepository.UpdateUser(); + + if (isUpdated) + { + var institution = await institutionRepository.GetInstitutionByID(user.InstitutionId); + + // Send email: Institution email verified + string senderName = "Back2Me"; + string senderEmail = "mycampuslostfound@gmail.com"; + string toName = institution.InstitutionName; + string toEmail = user.Email; + string subject = "🏫 Your Institution Has Been Verified!"; + string content = $@" + + + + + + + +
+ +
+

🏫 Institution Verified Successfully!

+
+ +
+ +

Hello,

+ +

+ Congratulations! Your institution + {institution.InstitutionName} + has been successfully verified. +

+ +
+ 🎉 Your Institution Administrator account is now active and your campus is ready to use Back2Me. +
+ +

+ You can now sign in to manage your institution, oversee lost and found activities, and support students on your campus. +

+ +

+ + Sign In + +

+ +

+ If you did not perform this verification, please contact the Back2Me support team immediately. +

+ +
+ + + +
+ + + + "; + + await emailSender.SendEmail(senderName, senderEmail, toName, toEmail, subject, content); + } + } + + // Remove cache + memoryCache.Remove($"EMAIL_VERIFY_{token}"); - if (isAddedInstitution) - { return Ok(new { - Message = "Created institution successfully" + Message = "Verified email successfully", + Email = user.Email }); } - - return BadRequest("Create institution failed"); + catch (Exception ex) + { + return BadRequest("Something went wrong!"); + } } + #endregion // PUT api//5 [HttpPut("{id}")] diff --git a/LostFound_API/Controllers/InstitutionRequestController.cs b/LostFound_API/Controllers/InstitutionRequestController.cs new file mode 100644 index 0000000..f4c05fd --- /dev/null +++ b/LostFound_API/Controllers/InstitutionRequestController.cs @@ -0,0 +1,1005 @@ +using DataAccess; +using LostFound_API.DTOs.Institution; +using LostFound_API.DTOs.Users; +using LostFound_API.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json.Linq; +using ObjectBusiness; +using Repository; +using Services; +using System.Security.Claims; +using System.Text.Json; +using System.Threading.Tasks; + +// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 + +namespace LostFound_API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class InstitutionRequestController : ControllerBase + { + #region Variables + private readonly IInstitutionRequestRepository institutionRequestRepository; + private readonly IInstitutionRepository institutionRepository; + private readonly IUsersRepository usersRepository; + private readonly StateService stateService; + private readonly IMemoryCache memoryCache; + private readonly IConfiguration configuration; + private readonly EmailSender emailSender; + private readonly IHttpClientFactory httpClientFactory; + #endregion + + #region Constructor + public InstitutionRequestController(IInstitutionRequestRepository institutionRequestRepository, + StateService stateService, + IConfiguration configuration, + IHttpClientFactory httpClientFactory, + IUsersRepository usersRepository, + EmailSender emailSender, + IMemoryCache memoryCache, + IInstitutionRepository institutionRepository) + { + this.institutionRequestRepository = institutionRequestRepository; + this.stateService = stateService; + this.configuration = configuration; + this.httpClientFactory = httpClientFactory; + this.usersRepository = usersRepository; + this.emailSender = emailSender; + this.memoryCache = memoryCache; + this.institutionRepository = institutionRepository; + } + #endregion + + #region Get All Institution Requests + // GET: api/ + [Authorize] + [HttpGet] + public ActionResult Get() + { + var requests = institutionRequestRepository.AllInstitutionRequests(); + return Ok(requests); + } + #endregion + + #region Get Institution Request By ID + // GET api//5 + [Authorize(Roles = "SystemAdmin")] + [HttpGet("{id}")] + public async Task Get(int id) + { + var request = await institutionRequestRepository.GetInstitutionRequestByID(id); + if (request == null) + { + return NotFound(new + { + message = "Request not found" + }); + } + + return Ok(request); + } + #endregion + + #region Verify Email + [HttpGet("verify-email")] + public async Task VerifyEmail([FromQuery] string token, [FromQuery] bool isForgotPassword) + { + try + { + if (!memoryCache.TryGetValue($"EMAIL_VERIFY_{token}", out string userEmail)) + { + return BadRequest(new + { + Message = "Invalid or expired verification link" + }); + } + + var institutionRequest = await institutionRequestRepository.GetInstitutionRequestByWorkEmail(userEmail!); + + // Seperate send email is verified and don't send for forgot password + if (!isForgotPassword) + { + institutionRequest.IsVerifiedEmail = true; + institutionRequest.EmailVerifiedAt = DateTime.Now; + var isUpdated = await institutionRequestRepository.UpdateInstitutionRequest(); + + if (isUpdated) + { + // Send email email has been verified + string senderName = "Back2Me"; + string senderEmail = "mycampuslostfound@gmail.com"; + string toName = institutionRequest.ApplicantName; + string toEmail = institutionRequest.WorkEmail; + string subject = "Your Email Has Been Verified!"; + string content = $@" + + + + + +
+
+

✅ Work Email Verified

+
+ +
+ +

Hello {institutionRequest.ApplicantName},

+ +

+ Thank you! Your work email has been successfully verified. +

+ +
+ 📧 Email Verified: {institutionRequest.WorkEmail} +
+ +

+ Your institution registration request for + {institutionRequest.InstitutionName} + has now been submitted for review. +

+ +

+ Our team will verify the information you provided before approving your institution. + This process may take some time depending on the volume of requests. +

+ +
+ ⏳ Current Status: Pending Institution Review +
+ +

+ Once your institution has been approved, we'll send you another email with instructions + to create your administrator password and activate your Back2Me administrator account. +

+ +

+ No further action is required at this time. +

+ +
+ + +
+ + + "; + + await emailSender.SendEmail(senderName, senderEmail, toName, toEmail, subject, content); + } + } + + // Remove cache + memoryCache.Remove($"EMAIL_VERIFY_{token}"); + + return Ok(new + { + Message = "Verified email successfully", + Email = institutionRequest.WorkEmail + }); + } + catch (Exception ex) + { + return BadRequest("Something went wrong!"); + } + } + #endregion + + #region Verify Website + [HttpGet("verify-website/{id}")] + public async Task VerifyWebsite(int id) + { + try + { + var institutionRequest = await institutionRequestRepository.GetInstitutionRequestByID(id); + if (institutionRequest != null) + { + institutionRequest.IsVerifiedWebsite = true; + institutionRequest.WebsiteVerifiedAt = DateTime.Now; + var isUpdated = await institutionRequestRepository.UpdateInstitutionRequest(); + if (isUpdated) + { + return Ok(new + { + Message = "Verified website successfully", + Website = institutionRequest.InstitutionWebsite + }); + } + else + { + return BadRequest(new + { + Message = "Failed to update website verification status" + }); + } + } + else + { + return NotFound(new + { + Message = "Institution request not found" + }); + } + } + catch (Exception ex) + { + return BadRequest("Something went wrong!"); + } + } + #endregion + + #region Search Institution + // GET api//5 + [HttpGet("search")] + public async Task Search(string query) + { + if (string.IsNullOrWhiteSpace(query)) + { + return BadRequest(new + { + message = "Query is required" + }); + } + + var apiKey = configuration["CollegeScorecard:ApiKey"]; + var client = httpClientFactory.CreateClient(); + + var url = + $"https://api.data.gov/ed/collegescorecard/v1/schools" + + $"?api_key={apiKey}" + + $"&school.name={Uri.EscapeDataString(query)}" + + $"&fields=id,school.name,school.address,school.city,school.state,school.zip,school.school_url,location.lat,location.lon" + + $"&per_page=10"; + + // Send GET request to College Scorecard API + var res = await client.GetAsync(url); + + // If API returns errors + if (!res.IsSuccessStatusCode) + { + return StatusCode( + (int)res.StatusCode, + "Unable to retrieve institutions."); + } + + var json = await res.Content.ReadAsStringAsync(); + + // Parse JSON to JsonDocument in order to read each property + using var document = JsonDocument.Parse(json); + + // Get property "results" + var results = document.RootElement.GetProperty("results"); + + // Create DTO list to return to frontend. + var institutions = new List(); + + foreach (var item in results.EnumerateArray()) + { + institutions.Add(new InstitutionSearchDTO + { + // GET "id" field + // + // JSON: + // + // "id":166027 + // + // => + // int + Id = item.TryGetProperty("id", out var id) + && id.ValueKind == JsonValueKind.Number + ? id.GetInt32() : 0, + + // GET school name. + // + // JSON: + // + // "school.name":"Georgia State University" + // + // if null then return "" + Name = item.GetProperty("school.name").GetString() ?? "", + + // TryGetProperty to skip throw exception + // if property does not exist. + // + // If exist: + // get Address. + // + // If no: + // return null. + Address = item.TryGetProperty("school.address", out var address) + ? address.GetString() : null, + + // City + City = item.TryGetProperty("school.city", out var city) + ? city.GetString() : null, + + // State + State = item.TryGetProperty("school.state", out var state) + ? state.GetString() : null, + + // Zip Code + ZipCode = item.TryGetProperty("school.zip", out var zip) + ? zip.GetString() : null, + + // Website + Website = item.TryGetProperty("school.school_url", out var website) + ? website.GetString() : null, + + // Latitude + Latitude = item.TryGetProperty("location.lat", out var lat) + && lat.ValueKind == JsonValueKind.Number + ? lat.GetDouble() : null, + + // Longitude + Longitude = item.TryGetProperty("location.lon", out var lon) + && lon.ValueKind == JsonValueKind.Number + ? lon.GetDouble() : null, + + }); + } + + // return List to frontend. + // + // Frontend will receive: + // + // [ + // { + // id:166027, + // name:"Georgia State University", + // address:"33 Gilmer St SE", + // city:"Atlanta", + // state:"GA", + // zipCode:"30303", + // website:"www.gsu.edu", + // latitude:33.75, + // longitude:-84.38 + // } + // ] + + return Ok(institutions); + } + #endregion + + #region Create Institution Request + // POST api/ + [HttpPost("sign-up")] + public async Task CreateRequest([FromBody] InstitutionRequest request) + { + var requestExists = await institutionRequestRepository + .GetInstitutionRequestByNameAndAddress(request.InstitutionName, request.InstitutionAddress); + if (requestExists != null) + { + return Conflict(new + { + message = "This campus already registered" + }); + } + + var isAdded = await institutionRequestRepository.CreateInstitutionRequest(request); + if (isAdded) + { + // Send email notification to the user who made the request + var token = Guid.NewGuid().ToString(); + memoryCache.Set($"EMAIL_VERIFY_{token}", request.WorkEmail, TimeSpan.FromMinutes(15)); + + string senderName = "Back2Me"; + string senderEmail = "mycampuslostfound@gmail.com"; + string toName = request.ApplicantName; + string toEmail = request.WorkEmail; + string subject = "Institution Registration Received"; + + string content = $@" + + + + + + + +
+ +
+

🏫 Registration Request Received

+
+ +
+ +

Hello,

+ +

+ Thank you for registering + {request.InstitutionName}'s {request.InstitutionAddress} campus + with Back2Me. +

+ +

+ We have successfully received your institution registration request. + Before we review your institution registration, please verify your work email address by clicking the button below. +

+ + + +

+ This verification link will expire in 15 minutes. +

+ +
+ ⏳ Status: Pending Approval +
+ +

+ Once your institution has been approved, we'll send another email with instructions + to create your administrator password and access your Back2Me account. +

+ +

+ No further action is required at this time. +

+ +

+ If you did not submit this registration request, please ignore this email. +

+ +
+ + + +
+ + + "; + + await emailSender.SendEmail(senderName, senderEmail, toName, toEmail, subject, content); + + return Ok(new + { + message = "Create request successfully" + }); + } + + return BadRequest(new + { + message = "Create request failed" + }); + } + #endregion + + #region Approve Request + // PUT api//5 + [Authorize(Roles = "SystemAdmin")] + [HttpPut("approve/{id}")] + public async Task ApproveRequest(int id) + { + // Get the authenticated user's email from the claims + var userEmail = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + if (userEmail == null) + { + return Unauthorized(new + { + message = "User not authenticated" + }); + } + + var userExisted = await usersRepository.GetUserByEmail(userEmail); + + if (userExisted == null) + { + return NotFound(new + { + message = "User does not found" + }); + } + + // Update information + var institutionRequest = await institutionRequestRepository.GetInstitutionRequestByID(id); + institutionRequest.Status = StatusRequestInstitution.Approved; + institutionRequest.UpdatedDate = DateTime.Now; + + var isUpdated = await institutionRequestRepository.UpdateInstitutionRequest(); + if (!isUpdated) + { + return BadRequest(new + { + message = "Update request failed" + }); + } + + var institution = new Institution + { + InstitutionName = institutionRequest.InstitutionName, + InstitutionAddress = institutionRequest.InstitutionAddress, + CreatedDate = DateTime.Now, + UpdatedDate = DateTime.Now + }; + + var isAdded = await institutionRepository.CreateInstitution(institution); + if (!isAdded) + { + return BadRequest(new + { + message = "Create institution failed" + }); + } + + // Send email notification to the user who made the request + var token = Guid.NewGuid().ToString(); + memoryCache.Set($"EMAIL_VERIFY_{token}", institutionRequest.WorkEmail, TimeSpan.FromMinutes(15)); + + string senderName = "Back2Me"; + string senderEmail = "mycampuslostfound@gmail.com"; + string toName = institutionRequest.ApplicantName; + string toEmail = institutionRequest.WorkEmail; + string subject = "Your Institution Has Been Approved"; + + string content = $@" + + + + + + + +
+ +
+

🎉 Institution Approved

+
+ +
+ +

Hello,

+ +

+ Congratulations! Your institution + {institutionRequest.InstitutionName} + has been successfully approved by the Back2Me Team. +

+ +
+ ✅ Your institution is now active and ready to use on the Back2Me platform. +
+ +

+ Before you can sign in, you'll need to create a password for your administrator account. +

+ +

+ Click the button below to set your password and complete your account setup. +

+ +

+ + Set Your Password + +

+ +

+ For your security, this link will expire in 15 minutes. +

+ +

+ Once your password has been created, you can sign in and begin managing your campus Lost & Found system. +

+ +

+ If you were not expecting this email, you can safely ignore it. +

+ +
+ + + +
+ + + "; + + await emailSender.SendEmail(senderName, senderEmail, toName, toEmail, subject, content); + + return Ok(new + { + message = "Update request successfully" + }); + } + #endregion + + #region Reject Request + [Authorize(Roles = "SystemAdmin")] + [HttpPut("reject/{id}")] + public async Task RejectRequest(int id, [FromBody] string rejectReason) + { + // Get the authenticated user's email from the claims + var userEmail = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + if (userEmail == null) + { + return Unauthorized(new + { + message = "User not authenticated" + }); + } + + var userExisted = await usersRepository.GetUserByEmail(userEmail); + + if (userExisted == null) + { + return NotFound(new + { + message = "User does not found" + }); + } + + // Update information + var institutionRequest = await institutionRequestRepository.GetInstitutionRequestByID(id); + institutionRequest.Status = StatusRequestInstitution.Rejected; + institutionRequest.RejectReason = rejectReason; + institutionRequest.ReviewedBy = userExisted.FirstName + " " + userExisted.LastName; + institutionRequest.ReviewedDate = DateTime.Now; + institutionRequest.UpdatedDate = DateTime.Now; + + var isUpdated = await institutionRequestRepository.UpdateInstitutionRequest(); + if (!isUpdated) + { + return BadRequest(new + { + message = "Update request failed" + }); + } + + var institution = new Institution + { + InstitutionName = institutionRequest.InstitutionName, + InstitutionAddress = institutionRequest.InstitutionAddress, + CreatedDate = DateTime.Now, + UpdatedDate = DateTime.Now + }; + + var isAdded = await institutionRepository.CreateInstitution(institution); + if (!isAdded) + { + return BadRequest(new + { + message = "Create institution failed" + }); + } + + // Send email notification to the user who made the request + var token = Guid.NewGuid().ToString(); + memoryCache.Set($"EMAIL_VERIFY_{token}", institutionRequest.WorkEmail, TimeSpan.FromMinutes(15)); + + string senderName = "Back2Me"; + string senderEmail = "mycampuslostfound@gmail.com"; + string toName = institutionRequest.ApplicantName; + string toEmail = institutionRequest.WorkEmail; + string subject = "Institution Registration Request Update"; + + string content = $@" + + + + + + + +
+ +
+

❌ Registration Request Rejected

+
+ +
+ +

Hello,

+ +

+ Thank you for your interest in registering + {institution.InstitutionName} + with Back2Me. +

+ +

+ After reviewing your submission, we regret to inform you that your institution registration request has not been approved at this time. +

+ +
+ Reason:
+ {institutionRequest.RejectReason} +
+ +

+ If you believe this decision was made in error or you would like to provide additional information, please contact the Back2Me Team or submit a new registration request. +

+ +

+ Thank you for your understanding. +

+ +
+ + + +
+ + + "; + + await emailSender.SendEmail(senderName, senderEmail, toName, toEmail, subject, content); + + return Ok(new + { + message = "Update request successfully" + }); + } + #endregion + + // DELETE api//5 + [HttpDelete("{id}")] + public void Delete(int id) + { + } + } +} diff --git a/LostFound_API/Controllers/MessageChatController.cs b/LostFound_API/Controllers/MessageChatController.cs index 7236c7b..cc7de96 100644 --- a/LostFound_API/Controllers/MessageChatController.cs +++ b/LostFound_API/Controllers/MessageChatController.cs @@ -293,7 +293,7 @@ .content p {{

- + 💬 Open Chat

@@ -468,7 +468,7 @@ .content p {{

- + 💬 Open Chat

diff --git a/LostFound_API/Controllers/PickUpRequestController.cs b/LostFound_API/Controllers/PickUpRequestController.cs index 316715b..40664d8 100644 --- a/LostFound_API/Controllers/PickUpRequestController.cs +++ b/LostFound_API/Controllers/PickUpRequestController.cs @@ -44,7 +44,7 @@ public PickUpRequestController(IPickUpRequestRepository pickUpRequestRepository, #endregion // GET: api/ - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet] public async Task>> Get() { @@ -53,7 +53,7 @@ public async Task>> Get() } #region Search Pick-Up Request for Admin - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("search-request")] public async Task>> SearchRequest([FromQuery] string query) { @@ -77,7 +77,7 @@ public async Task> CheckStatusPostPickUp(int postId) } #region Accept Time - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpPost("accept-time/{requestId}")] public async Task AcceptTime(int requestId) { @@ -120,7 +120,7 @@ public async Task AcceptTimeRescheduled(int requestId) #endregion #region Change Time - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpPost("change-time/{requestId}")] public async Task ChangeTime(int requestId, [FromBody] DateTime newDate) { @@ -363,7 +363,7 @@ The user has chosen to pick up the item at a later time. No pic

- + 📄 View Pickup Details

diff --git a/LostFound_API/Controllers/PostController.cs b/LostFound_API/Controllers/PostController.cs index 70065d7..c11e783 100644 --- a/LostFound_API/Controllers/PostController.cs +++ b/LostFound_API/Controllers/PostController.cs @@ -139,7 +139,7 @@ public async Task>> SortByStatus([FromQuery] TypePost t #region All Lost Post Codes // GET: api/ - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("lost-post-codes")] public async Task>> AllLostPostCodes() { @@ -215,7 +215,7 @@ public async Task>> GetAllPostsByUserId() #endregion #region My Posts - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("user-posts/{userId}")] public async Task>> GetAllPostsByUserId(int userId) { @@ -241,7 +241,7 @@ public async Task>> GetAllPostsByUserId(int userId) #endregion #region Get Lost Posts Per Month - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("lost-posts-per-month")] public async Task> GetLostPostsPerMonth() { @@ -251,7 +251,7 @@ public async Task> GetLostPostsPerMonth() #endregion #region Get Found Posts Per Month - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("found-posts-per-month")] public async Task> GetFoundPostsPerMonth() { @@ -261,7 +261,7 @@ public async Task> GetFoundPostsPerMonth() #endregion #region Get Received Posts Per Month - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("received-posts-per-month")] public async Task> GetReceivedPostsPerMonth() { @@ -271,7 +271,7 @@ public async Task> GetReceivedPostsPerMonth() #endregion #region Get Found Posts Not Received - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("found-posts-not-received")] public async Task> GetFoundPostsNotReceived() { @@ -473,7 +473,7 @@ public async Task MarkReceived(int postId) #endregion #region Search Code Lost for Admin - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("search-codes")] public async Task>> SearchCodes([FromQuery] string query) { @@ -587,7 +587,7 @@ public async Task> Put(int id, [FromForm] PostDTO postDTO) #region Delete Post for Admin // DELETE api//5 - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpDelete("{postId}")] public async Task> Delete(int postId) { diff --git a/LostFound_API/Controllers/TransferRequestsController.cs b/LostFound_API/Controllers/TransferRequestsController.cs index 3b55650..edc3f78 100644 --- a/LostFound_API/Controllers/TransferRequestsController.cs +++ b/LostFound_API/Controllers/TransferRequestsController.cs @@ -35,7 +35,7 @@ public TransferRequestsController(IPostRepository postRepository, #endregion // GET: api/ - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet] public ActionResult> Get() { @@ -67,7 +67,7 @@ public string Get(int id) } #region Mark Received - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpPost("mark-received")] public async Task MarkReceived([FromBody] UserHandOverAdminDTO info) { @@ -91,7 +91,7 @@ public async Task MarkReceived([FromBody] UserHandOverAdminDTO inf #endregion #region Mark Received - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpPost("cancel-handover")] public async Task CancelHandover([FromBody] UserHandOverAdminDTO info) { @@ -135,7 +135,7 @@ public async Task CreateRequet([FromBody] UserHandOverAdminDTO inf #endregion #region Search Transfer Request for Admin - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("search-request")] public async Task>> SearchFirstNameRequest([FromQuery] string query) { diff --git a/LostFound_API/Controllers/UsersController.cs b/LostFound_API/Controllers/UsersController.cs index 377dad4..ae41a42 100644 --- a/LostFound_API/Controllers/UsersController.cs +++ b/LostFound_API/Controllers/UsersController.cs @@ -59,7 +59,7 @@ public UsersController(IUsersRepository usersRepository, // GET: api/ #region Get All Users - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet] public async Task>> Get() { @@ -85,7 +85,7 @@ public async Task>> Get() #endregion #region Search Email - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpGet("search-email")] public async Task>> SearchEmail([FromQuery] string query) { @@ -169,7 +169,7 @@ public string Get(int id) // POST api/ #region Sign Up [HttpPost("sign-up")] - public async Task SignUp([FromBody] Users user) + public async Task SignUp([FromBody] UserDTO user) { if (user == null) { @@ -179,22 +179,10 @@ public async Task SignUp([FromBody] Users user) }); } - var institutionIdExist = institutionRepository.GetInstitutionByID(user.InstitutionId); + var institutionIdExist = await institutionRepository.GetInstitutionByNameAndAddress(user.InstitutionName, user.InstitutionAddress); if (institutionIdExist != null) { - // Check duplicate student ID - var existingStudentByStudentId = studentRepository.AllStudents() - .Any(s => s.StudentId == user.StudentId); - - if (existingStudentByStudentId) - { - return Conflict(new - { - message = "Your student ID is already in use" - }); - } - // Check duplicate email var existingUserByEmail = usersRepository.AllUsers() .Any(u => u.Email.Equals(user.Email)); @@ -209,7 +197,20 @@ public async Task SignUp([FromBody] Users user) try { - var isAddedUser = await usersRepository.SignUp(user); + var userSignUp = new Users + { + UserId = user.UserId, + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName, + InstitutionId = institutionIdExist.InstitutionId, + Password = user.Password, + PickImage1 = user.PickImage1, + PickImage2 = user.PickImage2, + Role = Role.Student, + }; + + var isAddedUser = await usersRepository.SignUp(userSignUp); if (isAddedUser) { var student = new Student @@ -319,7 +320,7 @@ .content p {{

- ✅ Verify Email + ✅ Verify Email

If you did not perform this action, you can safely ignore this email.

@@ -401,7 +402,7 @@ .content p {{ { return BadRequest(new { - message = "This campus is not available in the system, so the account cannot be created." + message = "Your account cannot be created because your campus has not joined the platform yet." }); } } @@ -413,7 +414,7 @@ public async Task SignIn([FromBody] SignInRequestDTO signInRequest { try { - var user = await usersRepository.SignIn(signInRequestDTO.StudentId, signInRequestDTO.Password, signInRequestDTO.Email); + var user = await usersRepository.SignIn(signInRequestDTO.Password, signInRequestDTO.Email); if (user != null && !user.IsActive) { @@ -425,9 +426,7 @@ public async Task SignIn([FromBody] SignInRequestDTO signInRequest return Ok(); // Ok to change to select image } - return Unauthorized(signInRequestDTO.StudentId == 0 ? - "Email or password is invalid" : - "Student ID or password is invalid"); + return Unauthorized("Email or password is invalid"); } catch (Exception ex) { @@ -442,7 +441,7 @@ public async Task SelectImages([FromBody] SignInRequestDTO signIn { try { - var user = await usersRepository.SignIn(signInRequestDTO.StudentId, signInRequestDTO.Password, signInRequestDTO.Email); + var user = await usersRepository.SignIn(signInRequestDTO.Password, signInRequestDTO.Email); if (user != null) { // Check Pick image @@ -644,7 +643,7 @@ .content p {{

- Go to Profile + Go to Profile

If you did not perform this action, please contact our support team immediately.

@@ -819,7 +818,7 @@ .content p {{

- ✅ Verify Email + ✅ Verify Email

If you did not perform this action, you can safely ignore this email.

@@ -1022,7 +1021,7 @@ .content p {{

- Reset Password + Reset Password

If you did not request a password reset, please ignore this email. Your account will remain secure.

@@ -1320,7 +1319,7 @@ support team immediately to secure your account. #endregion #region Suspend User - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpPut("suspend-user/{userId}")] public async Task SuspendUser(int userId) { @@ -1367,7 +1366,7 @@ await hubContext.Clients.Group("Admin") #endregion #region Unsuspend User - [Authorize(Roles = "Admin")] + [Authorize(Roles = "SystemAdmin")] [HttpPut("unsuspend-user/{userId}")] public async Task UnsuspendUser(int userId) { diff --git a/LostFound_API/DTOs/Auth/SignInRequestDTO.cs b/LostFound_API/DTOs/Auth/SignInRequestDTO.cs index 8dc1af0..5918fb9 100644 --- a/LostFound_API/DTOs/Auth/SignInRequestDTO.cs +++ b/LostFound_API/DTOs/Auth/SignInRequestDTO.cs @@ -2,9 +2,8 @@ { public class SignInRequestDTO { - public int StudentId { get; set; } public string Password { get; set; } - public string? Email { get; set; } + public string Email { get; set; } public List? PickedIndexes { get; set; } } } diff --git a/LostFound_API/DTOs/Users/UserDTO.cs b/LostFound_API/DTOs/Users/UserDTO.cs index 539af40..eeff2cf 100644 --- a/LostFound_API/DTOs/Users/UserDTO.cs +++ b/LostFound_API/DTOs/Users/UserDTO.cs @@ -1,11 +1,32 @@ -namespace LostFound_API.DTOs.Users +using ObjectBusiness; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LostFound_API.DTOs.Users { public class UserDTO { + public int StudentId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public IFormFile? AvatarUpload { get; set; } public DateTime? DateOfBirth { get; set; } public string? Avatar { get; set; } + public string? InstitutionName { get; set; } + public string? InstitutionAddress { get; set; } + public string? InstitutionCity { get; set; } + public string? InstitutionState { get; set; } + public string? InstitutionPhone { get; set; } + public string? InstitutionWebsite { get; set; } + public string Email { get; set; } + public int UserId { get; set; } + public Role Role { get; set; } + public string Password { get; set; } + public string PickImage1 { get; set; } + public string PickImage2 { get; set; } + + [NotMapped] // This property is not mapped to the database + public string? AccessToken { get; set; } + [NotMapped] + public int ExpiresIn { get; set; } } } diff --git a/LostFound_API/Program.cs b/LostFound_API/Program.cs index 7e9d1f2..a0cc519 100644 --- a/LostFound_API/Program.cs +++ b/LostFound_API/Program.cs @@ -110,6 +110,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -122,6 +123,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Register BackgroundService builder.Services.AddHostedService(); @@ -129,7 +131,13 @@ // Connect to SQL Server builder.Services.AddDbContext(options => { - options.UseSqlServer(builder.Configuration.GetConnectionString("MyConnection")); + options.UseSqlServer( + builder.Configuration.GetConnectionString("MyConnection"), + sql => sql.EnableRetryOnFailure( + maxRetryCount: 5, + maxRetryDelay: TimeSpan.FromSeconds(10), + errorNumbersToAdd: null + )); }); // Register for send email diff --git a/ObjectBusiness/BackToMeDbContext.cs b/ObjectBusiness/BackToMeDbContext.cs index 76f7d21..6820e86 100644 --- a/ObjectBusiness/BackToMeDbContext.cs +++ b/ObjectBusiness/BackToMeDbContext.cs @@ -29,10 +29,53 @@ public BackToMeDbContext(DbContextOptions options) : base(opt public DbSet MessageChat { get; set; } public DbSet Notifications { get; set; } public DbSet Institutions { get; set; } + public DbSet InstitutionRequests { get; set; } #endregion protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity() + .HasOne(m => m.Chat) + .WithMany(c => c.Messages) + .HasForeignKey(m => m.ChatId) + .OnDelete(DeleteBehavior.NoAction); + + modelBuilder.Entity() + .HasOne(m => m.PostOriginal) + .WithMany() + .HasForeignKey(m => m.PostOriginalId) + .OnDelete(DeleteBehavior.NoAction); // Prevent multiple cascade paths issue in SQL Server + + modelBuilder.Entity() + .HasOne(m => m.PostMatched) + .WithMany() + .HasForeignKey(m => m.PostMatchedId) + .OnDelete(DeleteBehavior.NoAction); // Prevent multiple cascade paths issue in SQL Server + + modelBuilder.Entity() + .HasOne(m => m.LostPost) + .WithMany() + .HasForeignKey(m => m.LostPostId) + .OnDelete(DeleteBehavior.NoAction); // Prevent multiple cascade paths issue in SQL Server + + modelBuilder.Entity() + .HasOne(m => m.FoundPost) + .WithMany() + .HasForeignKey(m => m.FoundPostId) + .OnDelete(DeleteBehavior.NoAction); // Prevent multiple cascade paths issue in SQL Server + + modelBuilder.Entity() + .HasOne(c => c.UserA) + .WithMany() + .HasForeignKey(c => c.UserAId) + .OnDelete(DeleteBehavior.NoAction); // Prevent multiple cascade paths issue in SQL Server + + modelBuilder.Entity() + .HasOne(c => c.UserB) + .WithMany() + .HasForeignKey(c => c.UserBId) + .OnDelete(DeleteBehavior.NoAction); // Prevent multiple cascade paths issue in SQL Server + // Allow converting enum to string modelBuilder.Entity() .Property(u => u.Role) @@ -48,6 +91,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .Property(u => u.Status) .HasConversion(); + // Allow converting enum to string + modelBuilder.Entity() + .Property(u => u.Status) + .HasConversion(); + // Allow converting enum to string modelBuilder.Entity() .Property(u => u.Status) @@ -62,16 +110,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasData(new Users { UserId = new Random().Next(), - FirstName = "Media", - LastName = "Center", + FirstName = "System", + LastName = "Admin", Password = "$2a$11$92uWViLQUKTIVIADFxhzqe39tDMoLWJX5e1FyXaeedfrq5CoMAGQ6", CreatedAt = DateTime.Now, IsActive = true, + IsVerifiedEmail = true, IsAgreedToTerms = true, - Email = "baoanwebapp@gmail.com", + Email = "mycampuslostfound@gmail.com", PickImage1 = "1", PickImage2 = "12", - Role = Role.Admin, + Role = Role.SystemAdmin, Avatar = "avatar_CV.jpg", DateOfBirth = new DateTime(2007, 01, 30), }); diff --git a/ObjectBusiness/Institution.cs b/ObjectBusiness/Institution.cs index 18cf8de..3ec689f 100644 --- a/ObjectBusiness/Institution.cs +++ b/ObjectBusiness/Institution.cs @@ -14,18 +14,15 @@ public class Institution { [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] public int InstitutionId { get; set; } + [Required(ErrorMessage = "Institution name cannot be blank")] public string InstitutionName { get; set; } + [Required] public string InstitutionAddress { get; set; } + [Required] public string InstitutionCity { get; set; } + [Required] public string InstitutionState { get; set; } - public string Password { get; set; } - public string PickImage1 { get; set; } - public string PickImage2 { get; set; } - public bool IsActive { get; set; } = true; - public bool IsAgreedToTerms { get; set; } = false; - public bool IsVerifiedEmail { get; set; } = false; public string? InstitutionPhone { get; set; } - public string? InstitutionEmail { get; set; } public string? InstitutionWebsite { get; set; } public string? InstitutionLogo { get; set; } public string? InstitutionDescription { get; set; } diff --git a/ObjectBusiness/InstitutionRequest.cs b/ObjectBusiness/InstitutionRequest.cs new file mode 100644 index 0000000..026916d --- /dev/null +++ b/ObjectBusiness/InstitutionRequest.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectBusiness +{ + public class InstitutionRequest + { + [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] + public int InstitutionRequestId { get; set; } + [Required(ErrorMessage = "Institution name cannot be blank")] + public string InstitutionName { get; set; } + [Required] + public string InstitutionAddress { get; set; } + [Required] + public string InstitutionCity { get; set; } + [Required] + public string InstitutionState { get; set; } + public string? InstitutionPhone { get; set; } + public string? InstitutionWebsite { get; set; } + public string? AdditionalInformation { get; set; } + public string? AdditionalNote { get; set; } + public StudentPopulationRange? EstimatedPopulation { get; set; } + [Required(ErrorMessage = "Applicant name cannot be blank")] + public string ApplicantName { get; set; } + public string? ApplicantPhoneNumber { get; set; } + [Required(ErrorMessage = "Work email cannot be blank")] + public string WorkEmail { get; set; } + [Required(ErrorMessage = "Job title cannot be blank")] + public string JobTitle { get; set; } + public bool IsVerifiedEmail { get; set; } = false; + public bool IsVerifiedWebsite { get; set; } = false; + public bool IsVerifiedInstitution { get; set; } = false; + public StatusRequestInstitution Status { get; set; } = StatusRequestInstitution.Pending; + public string? InstitutionDescription { get; set; } + public string? ReviewedBy { get; set; } + public DateTime? EmailVerifiedAt { get; set; } + public DateTime? WebsiteVerifiedAt { get; set; } + public DateTime? ReviewedDate { get; set; } + public string? RejectReason { get; set; } + public DateTime SubmittedDate { get; set; } = DateTime.Now; + public DateTime? UpdatedDate { get; set; } + } +} diff --git a/ObjectBusiness/Migrations/20260725184416_Initial.Designer.cs b/ObjectBusiness/Migrations/20260725184416_Initial.Designer.cs new file mode 100644 index 0000000..43abb33 --- /dev/null +++ b/ObjectBusiness/Migrations/20260725184416_Initial.Designer.cs @@ -0,0 +1,787 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ObjectBusiness; + +#nullable disable + +namespace ObjectBusiness.Migrations +{ + [DbContext(typeof(BackToMeDbContext))] + [Migration("20260725184416_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.28") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ObjectBusiness.CategoryPost", b => + { + b.Property("CategoryPostId") + .HasColumnType("int"); + + b.Property("CategoryPostName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("CategoryPostId"); + + b.ToTable("CategoryPost"); + + b.HasData( + new + { + CategoryPostId = 781275554, + CategoryPostName = "IPhone", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3499) + }, + new + { + CategoryPostId = 171705011, + CategoryPostName = "IPad", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3503) + }, + new + { + CategoryPostId = 357308849, + CategoryPostName = "Chromebook", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3532) + }, + new + { + CategoryPostId = 663701472, + CategoryPostName = "Earbuds", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3537) + }, + new + { + CategoryPostId = 1270846585, + CategoryPostName = "Wallet", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3541) + }, + new + { + CategoryPostId = 2145456831, + CategoryPostName = "Charger", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3545) + }); + }); + + modelBuilder.Entity("ObjectBusiness.Chat", b => + { + b.Property("ChatId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserAId") + .HasColumnType("int"); + + b.Property("UserALastReadMessageId") + .HasColumnType("int"); + + b.Property("UserBId") + .HasColumnType("int"); + + b.Property("UserBLastReadMessageId") + .HasColumnType("int"); + + b.HasKey("ChatId"); + + b.HasIndex("PostId"); + + b.HasIndex("UserAId"); + + b.HasIndex("UserBId"); + + b.ToTable("Chat"); + }); + + modelBuilder.Entity("ObjectBusiness.Institution", b => + { + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("InstitutionAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionCity") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionLogo") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionPhone") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionState") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionWebsite") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("InstitutionId"); + + b.ToTable("Institutions"); + }); + + modelBuilder.Entity("ObjectBusiness.InstitutionRequest", b => + { + b.Property("InstitutionRequestId") + .HasColumnType("int"); + + b.Property("AdditionalInformation") + .HasColumnType("nvarchar(max)"); + + b.Property("AdditionalNote") + .HasColumnType("nvarchar(max)"); + + b.Property("ApplicantName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ApplicantPhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("EstimatedPopulation") + .HasColumnType("int"); + + b.Property("InstitutionAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionCity") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionPhone") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionState") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionWebsite") + .HasColumnType("nvarchar(max)"); + + b.Property("IsVerifiedEmail") + .HasColumnType("bit"); + + b.Property("IsVerifiedInstitution") + .HasColumnType("bit"); + + b.Property("IsVerifiedWebsite") + .HasColumnType("bit"); + + b.Property("JobTitle") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RejectReason") + .HasColumnType("nvarchar(max)"); + + b.Property("ReviewedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ReviewedDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SubmittedDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("WebsiteVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("WorkEmail") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("InstitutionRequestId"); + + b.ToTable("InstitutionRequests"); + }); + + modelBuilder.Entity("ObjectBusiness.Match", b => + { + b.Property("MatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("FoundPostId") + .HasColumnType("int"); + + b.Property("LostPostId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("MatchId"); + + b.HasIndex("FoundPostId"); + + b.HasIndex("LostPostId"); + + b.ToTable("Match"); + }); + + modelBuilder.Entity("ObjectBusiness.MessageChat", b => + { + b.Property("MessageChatId") + .HasColumnType("int"); + + b.Property("ChatId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("MessageContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserSenderId") + .HasColumnType("int"); + + b.HasKey("MessageChatId"); + + b.HasIndex("ChatId"); + + b.HasIndex("UserSenderId"); + + b.ToTable("MessageChat"); + }); + + modelBuilder.Entity("ObjectBusiness.Notifications", b => + { + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("NotificationContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NotificationType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PostMatchedId") + .HasColumnType("int"); + + b.Property("PostOriginalId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("NotificationId"); + + b.HasIndex("PostMatchedId"); + + b.HasIndex("PostOriginalId"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("ObjectBusiness.PickUpRequest", b => + { + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PickUpDate") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("RequestId"); + + b.HasIndex("PostId"); + + b.ToTable("PickUpRequest"); + }); + + modelBuilder.Entity("ObjectBusiness.Posts", b => + { + b.Property("PostId") + .HasColumnType("int"); + + b.Property("CategoryPostId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Image") + .HasColumnType("nvarchar(max)"); + + b.Property("IsReceived") + .HasColumnType("bit"); + + b.Property("OldUserId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TypePost") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Vector") + .HasColumnType("nvarchar(max)"); + + b.HasKey("PostId"); + + b.HasIndex("CategoryPostId"); + + b.HasIndex("UserId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("ObjectBusiness.Student", b => + { + b.Property("StudentId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("StudentId"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Student"); + }); + + modelBuilder.Entity("ObjectBusiness.TransferRequests", b => + { + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ToUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RequestId"); + + b.HasIndex("PostId"); + + b.ToTable("TransferRequests"); + }); + + modelBuilder.Entity("ObjectBusiness.Users", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Avatar") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DateOfBirth") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsAgreedToTerms") + .HasColumnType("bit"); + + b.Property("IsVerifiedEmail") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PickImage1") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PickImage2") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Role") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("UserId"); + + b.HasIndex("InstitutionId"); + + b.ToTable("Users"); + + b.HasData( + new + { + UserId = 2043193290, + Avatar = "avatar_CV.jpg", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3191), + DateOfBirth = new DateTime(2007, 1, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "mycampuslostfound@gmail.com", + FirstName = "System", + IsActive = true, + IsAgreedToTerms = true, + IsVerifiedEmail = true, + LastName = "Admin", + Password = "$2a$11$92uWViLQUKTIVIADFxhzqe39tDMoLWJX5e1FyXaeedfrq5CoMAGQ6", + PickImage1 = "1", + PickImage2 = "12", + Role = "SystemAdmin" + }); + }); + + modelBuilder.Entity("ObjectBusiness.VerificationCode", b => + { + b.Property("VerificationCodeId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsUsed") + .HasColumnType("bit"); + + b.Property("MatchId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("VerificationCodeId"); + + b.HasIndex("MatchId") + .IsUnique(); + + b.ToTable("VerificationCode"); + }); + + modelBuilder.Entity("ObjectBusiness.Chat", b => + { + b.HasOne("ObjectBusiness.Posts", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "UserA") + .WithMany() + .HasForeignKey("UserAId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "UserB") + .WithMany() + .HasForeignKey("UserBId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("UserA"); + + b.Navigation("UserB"); + }); + + modelBuilder.Entity("ObjectBusiness.Match", b => + { + b.HasOne("ObjectBusiness.Posts", "FoundPost") + .WithMany() + .HasForeignKey("FoundPostId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Posts", "LostPost") + .WithMany() + .HasForeignKey("LostPostId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("FoundPost"); + + b.Navigation("LostPost"); + }); + + modelBuilder.Entity("ObjectBusiness.MessageChat", b => + { + b.HasOne("ObjectBusiness.Chat", "Chat") + .WithMany("Messages") + .HasForeignKey("ChatId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "UserSender") + .WithMany() + .HasForeignKey("UserSenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chat"); + + b.Navigation("UserSender"); + }); + + modelBuilder.Entity("ObjectBusiness.Notifications", b => + { + b.HasOne("ObjectBusiness.Posts", "PostMatched") + .WithMany() + .HasForeignKey("PostMatchedId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Posts", "PostOriginal") + .WithMany() + .HasForeignKey("PostOriginalId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("PostMatched"); + + b.Navigation("PostOriginal"); + }); + + modelBuilder.Entity("ObjectBusiness.PickUpRequest", b => + { + b.HasOne("ObjectBusiness.Posts", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("ObjectBusiness.Posts", b => + { + b.HasOne("ObjectBusiness.CategoryPost", "CategoryPost") + .WithMany() + .HasForeignKey("CategoryPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CategoryPost"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ObjectBusiness.Student", b => + { + b.HasOne("ObjectBusiness.Users", "User") + .WithOne("Student") + .HasForeignKey("ObjectBusiness.Student", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ObjectBusiness.TransferRequests", b => + { + b.HasOne("ObjectBusiness.Posts", "Post") + .WithMany("TransferRequests") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("ObjectBusiness.Users", b => + { + b.HasOne("ObjectBusiness.Institution", "Institution") + .WithMany("Users") + .HasForeignKey("InstitutionId"); + + b.Navigation("Institution"); + }); + + modelBuilder.Entity("ObjectBusiness.VerificationCode", b => + { + b.HasOne("ObjectBusiness.Match", "Match") + .WithOne("VerificationCode") + .HasForeignKey("ObjectBusiness.VerificationCode", "MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + }); + + modelBuilder.Entity("ObjectBusiness.Chat", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ObjectBusiness.Institution", b => + { + b.Navigation("Users"); + }); + + modelBuilder.Entity("ObjectBusiness.Match", b => + { + b.Navigation("VerificationCode"); + }); + + modelBuilder.Entity("ObjectBusiness.Posts", b => + { + b.Navigation("TransferRequests"); + }); + + modelBuilder.Entity("ObjectBusiness.Users", b => + { + b.Navigation("Student"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ObjectBusiness/Migrations/20260725184416_Initial.cs b/ObjectBusiness/Migrations/20260725184416_Initial.cs new file mode 100644 index 0000000..f797e4d --- /dev/null +++ b/ObjectBusiness/Migrations/20260725184416_Initial.cs @@ -0,0 +1,499 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace ObjectBusiness.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CategoryPost", + columns: table => new + { + CategoryPostId = table.Column(type: "int", nullable: false), + CategoryPostName = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CategoryPost", x => x.CategoryPostId); + }); + + migrationBuilder.CreateTable( + name: "InstitutionRequests", + columns: table => new + { + InstitutionRequestId = table.Column(type: "int", nullable: false), + InstitutionName = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionAddress = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionCity = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionState = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionPhone = table.Column(type: "nvarchar(max)", nullable: true), + InstitutionWebsite = table.Column(type: "nvarchar(max)", nullable: true), + AdditionalInformation = table.Column(type: "nvarchar(max)", nullable: true), + AdditionalNote = table.Column(type: "nvarchar(max)", nullable: true), + EstimatedPopulation = table.Column(type: "int", nullable: true), + ApplicantName = table.Column(type: "nvarchar(max)", nullable: false), + ApplicantPhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + WorkEmail = table.Column(type: "nvarchar(max)", nullable: false), + JobTitle = table.Column(type: "nvarchar(max)", nullable: false), + IsVerifiedEmail = table.Column(type: "bit", nullable: false), + IsVerifiedWebsite = table.Column(type: "bit", nullable: false), + IsVerifiedInstitution = table.Column(type: "bit", nullable: false), + Status = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionDescription = table.Column(type: "nvarchar(max)", nullable: true), + ReviewedBy = table.Column(type: "nvarchar(max)", nullable: true), + EmailVerifiedAt = table.Column(type: "datetime2", nullable: true), + WebsiteVerifiedAt = table.Column(type: "datetime2", nullable: true), + ReviewedDate = table.Column(type: "datetime2", nullable: true), + RejectReason = table.Column(type: "nvarchar(max)", nullable: true), + SubmittedDate = table.Column(type: "datetime2", nullable: false), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_InstitutionRequests", x => x.InstitutionRequestId); + }); + + migrationBuilder.CreateTable( + name: "Institutions", + columns: table => new + { + InstitutionId = table.Column(type: "int", nullable: false), + InstitutionName = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionAddress = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionCity = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionState = table.Column(type: "nvarchar(max)", nullable: false), + InstitutionPhone = table.Column(type: "nvarchar(max)", nullable: true), + InstitutionWebsite = table.Column(type: "nvarchar(max)", nullable: true), + InstitutionLogo = table.Column(type: "nvarchar(max)", nullable: true), + InstitutionDescription = table.Column(type: "nvarchar(max)", nullable: true), + CreatedDate = table.Column(type: "datetime2", nullable: false), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Institutions", x => x.InstitutionId); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + UserId = table.Column(type: "int", nullable: false), + InstitutionId = table.Column(type: "int", nullable: true), + FirstName = table.Column(type: "nvarchar(max)", nullable: false), + LastName = table.Column(type: "nvarchar(max)", nullable: false), + Email = table.Column(type: "nvarchar(max)", nullable: false), + DateOfBirth = table.Column(type: "datetime2", nullable: true), + Role = table.Column(type: "nvarchar(max)", nullable: false), + Password = table.Column(type: "nvarchar(max)", nullable: false), + Avatar = table.Column(type: "nvarchar(max)", nullable: true), + PickImage1 = table.Column(type: "nvarchar(max)", nullable: false), + PickImage2 = table.Column(type: "nvarchar(max)", nullable: false), + IsActive = table.Column(type: "bit", nullable: false), + IsAgreedToTerms = table.Column(type: "bit", nullable: false), + IsVerifiedEmail = table.Column(type: "bit", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.UserId); + table.ForeignKey( + name: "FK_Users_Institutions_InstitutionId", + column: x => x.InstitutionId, + principalTable: "Institutions", + principalColumn: "InstitutionId"); + }); + + migrationBuilder.CreateTable( + name: "Posts", + columns: table => new + { + PostId = table.Column(type: "int", nullable: false), + UserId = table.Column(type: "int", nullable: false), + Title = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: false), + CategoryPostId = table.Column(type: "int", nullable: false), + TypePost = table.Column(type: "nvarchar(max)", nullable: false), + Vector = table.Column(type: "nvarchar(max)", nullable: true), + Image = table.Column(type: "nvarchar(max)", nullable: true), + Code = table.Column(type: "nvarchar(max)", nullable: false), + IsReceived = table.Column(type: "bit", nullable: true), + OldUserId = table.Column(type: "int", nullable: true), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Posts", x => x.PostId); + table.ForeignKey( + name: "FK_Posts_CategoryPost_CategoryPostId", + column: x => x.CategoryPostId, + principalTable: "CategoryPost", + principalColumn: "CategoryPostId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Posts_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Student", + columns: table => new + { + StudentId = table.Column(type: "int", nullable: false), + UserId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Student", x => x.StudentId); + table.ForeignKey( + name: "FK_Student_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Chat", + columns: table => new + { + ChatId = table.Column(type: "int", nullable: false), + UserAId = table.Column(type: "int", nullable: false), + UserBId = table.Column(type: "int", nullable: false), + PostId = table.Column(type: "int", nullable: false), + UserALastReadMessageId = table.Column(type: "int", nullable: false), + UserBLastReadMessageId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Chat", x => x.ChatId); + table.ForeignKey( + name: "FK_Chat_Posts_PostId", + column: x => x.PostId, + principalTable: "Posts", + principalColumn: "PostId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Chat_Users_UserAId", + column: x => x.UserAId, + principalTable: "Users", + principalColumn: "UserId"); + table.ForeignKey( + name: "FK_Chat_Users_UserBId", + column: x => x.UserBId, + principalTable: "Users", + principalColumn: "UserId"); + }); + + migrationBuilder.CreateTable( + name: "Match", + columns: table => new + { + MatchId = table.Column(type: "int", nullable: false), + LostPostId = table.Column(type: "int", nullable: false), + FoundPostId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Match", x => x.MatchId); + table.ForeignKey( + name: "FK_Match_Posts_FoundPostId", + column: x => x.FoundPostId, + principalTable: "Posts", + principalColumn: "PostId"); + table.ForeignKey( + name: "FK_Match_Posts_LostPostId", + column: x => x.LostPostId, + principalTable: "Posts", + principalColumn: "PostId"); + }); + + migrationBuilder.CreateTable( + name: "Notifications", + columns: table => new + { + NotificationId = table.Column(type: "int", nullable: false), + PostOriginalId = table.Column(type: "int", nullable: false), + PostMatchedId = table.Column(type: "int", nullable: false), + NotificationType = table.Column(type: "nvarchar(max)", nullable: false), + NotificationContent = table.Column(type: "nvarchar(max)", nullable: false), + IsRead = table.Column(type: "bit", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Notifications", x => x.NotificationId); + table.ForeignKey( + name: "FK_Notifications_Posts_PostMatchedId", + column: x => x.PostMatchedId, + principalTable: "Posts", + principalColumn: "PostId"); + table.ForeignKey( + name: "FK_Notifications_Posts_PostOriginalId", + column: x => x.PostOriginalId, + principalTable: "Posts", + principalColumn: "PostId"); + }); + + migrationBuilder.CreateTable( + name: "PickUpRequest", + columns: table => new + { + RequestId = table.Column(type: "int", nullable: false), + PostId = table.Column(type: "int", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: false), + PickUpDate = table.Column(type: "datetime2", nullable: true), + Status = table.Column(type: "nvarchar(max)", nullable: false), + CreatedDate = table.Column(type: "datetime2", nullable: false), + UpdatedDate = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PickUpRequest", x => x.RequestId); + table.ForeignKey( + name: "FK_PickUpRequest_Posts_PostId", + column: x => x.PostId, + principalTable: "Posts", + principalColumn: "PostId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "TransferRequests", + columns: table => new + { + RequestId = table.Column(type: "int", nullable: false), + PostId = table.Column(type: "int", nullable: false), + UserId = table.Column(type: "int", nullable: false), + ToUserId = table.Column(type: "int", nullable: false), + Status = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ConfirmedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_TransferRequests", x => x.RequestId); + table.ForeignKey( + name: "FK_TransferRequests_Posts_PostId", + column: x => x.PostId, + principalTable: "Posts", + principalColumn: "PostId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "MessageChat", + columns: table => new + { + MessageChatId = table.Column(type: "int", nullable: false), + ChatId = table.Column(type: "int", nullable: false), + UserSenderId = table.Column(type: "int", nullable: false), + MessageContent = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MessageChat", x => x.MessageChatId); + table.ForeignKey( + name: "FK_MessageChat_Chat_ChatId", + column: x => x.ChatId, + principalTable: "Chat", + principalColumn: "ChatId"); + table.ForeignKey( + name: "FK_MessageChat_Users_UserSenderId", + column: x => x.UserSenderId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "VerificationCode", + columns: table => new + { + VerificationCodeId = table.Column(type: "int", nullable: false), + MatchId = table.Column(type: "int", nullable: false), + Code = table.Column(type: "nvarchar(max)", nullable: false), + IsUsed = table.Column(type: "bit", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_VerificationCode", x => x.VerificationCodeId); + table.ForeignKey( + name: "FK_VerificationCode_Match_MatchId", + column: x => x.MatchId, + principalTable: "Match", + principalColumn: "MatchId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "CategoryPost", + columns: new[] { "CategoryPostId", "CategoryPostName", "CreatedAt", "UpdatedAt" }, + values: new object[,] + { + { 171705011, "IPad", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3503), null }, + { 357308849, "Chromebook", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3532), null }, + { 663701472, "Earbuds", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3537), null }, + { 781275554, "IPhone", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3499), null }, + { 1270846585, "Wallet", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3541), null }, + { 2145456831, "Charger", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3545), null } + }); + + migrationBuilder.InsertData( + table: "Users", + columns: new[] { "UserId", "Avatar", "CreatedAt", "DateOfBirth", "Email", "FirstName", "InstitutionId", "IsActive", "IsAgreedToTerms", "IsVerifiedEmail", "LastName", "Password", "PickImage1", "PickImage2", "Role", "UpdatedAt" }, + values: new object[] { 2043193290, "avatar_CV.jpg", new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3191), new DateTime(2007, 1, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), "mycampuslostfound@gmail.com", "System", null, true, true, true, "Admin", "$2a$11$92uWViLQUKTIVIADFxhzqe39tDMoLWJX5e1FyXaeedfrq5CoMAGQ6", "1", "12", "SystemAdmin", null }); + + migrationBuilder.CreateIndex( + name: "IX_Chat_PostId", + table: "Chat", + column: "PostId"); + + migrationBuilder.CreateIndex( + name: "IX_Chat_UserAId", + table: "Chat", + column: "UserAId"); + + migrationBuilder.CreateIndex( + name: "IX_Chat_UserBId", + table: "Chat", + column: "UserBId"); + + migrationBuilder.CreateIndex( + name: "IX_Match_FoundPostId", + table: "Match", + column: "FoundPostId"); + + migrationBuilder.CreateIndex( + name: "IX_Match_LostPostId", + table: "Match", + column: "LostPostId"); + + migrationBuilder.CreateIndex( + name: "IX_MessageChat_ChatId", + table: "MessageChat", + column: "ChatId"); + + migrationBuilder.CreateIndex( + name: "IX_MessageChat_UserSenderId", + table: "MessageChat", + column: "UserSenderId"); + + migrationBuilder.CreateIndex( + name: "IX_Notifications_PostMatchedId", + table: "Notifications", + column: "PostMatchedId"); + + migrationBuilder.CreateIndex( + name: "IX_Notifications_PostOriginalId", + table: "Notifications", + column: "PostOriginalId"); + + migrationBuilder.CreateIndex( + name: "IX_PickUpRequest_PostId", + table: "PickUpRequest", + column: "PostId"); + + migrationBuilder.CreateIndex( + name: "IX_Posts_CategoryPostId", + table: "Posts", + column: "CategoryPostId"); + + migrationBuilder.CreateIndex( + name: "IX_Posts_UserId", + table: "Posts", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_Student_UserId", + table: "Student", + column: "UserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_TransferRequests_PostId", + table: "TransferRequests", + column: "PostId"); + + migrationBuilder.CreateIndex( + name: "IX_Users_InstitutionId", + table: "Users", + column: "InstitutionId"); + + migrationBuilder.CreateIndex( + name: "IX_VerificationCode_MatchId", + table: "VerificationCode", + column: "MatchId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "InstitutionRequests"); + + migrationBuilder.DropTable( + name: "MessageChat"); + + migrationBuilder.DropTable( + name: "Notifications"); + + migrationBuilder.DropTable( + name: "PickUpRequest"); + + migrationBuilder.DropTable( + name: "Student"); + + migrationBuilder.DropTable( + name: "TransferRequests"); + + migrationBuilder.DropTable( + name: "VerificationCode"); + + migrationBuilder.DropTable( + name: "Chat"); + + migrationBuilder.DropTable( + name: "Match"); + + migrationBuilder.DropTable( + name: "Posts"); + + migrationBuilder.DropTable( + name: "CategoryPost"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Institutions"); + } + } +} diff --git a/ObjectBusiness/Migrations/BackToMeDbContextModelSnapshot.cs b/ObjectBusiness/Migrations/BackToMeDbContextModelSnapshot.cs new file mode 100644 index 0000000..db02832 --- /dev/null +++ b/ObjectBusiness/Migrations/BackToMeDbContextModelSnapshot.cs @@ -0,0 +1,784 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ObjectBusiness; + +#nullable disable + +namespace ObjectBusiness.Migrations +{ + [DbContext(typeof(BackToMeDbContext))] + partial class BackToMeDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.28") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ObjectBusiness.CategoryPost", b => + { + b.Property("CategoryPostId") + .HasColumnType("int"); + + b.Property("CategoryPostName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("CategoryPostId"); + + b.ToTable("CategoryPost"); + + b.HasData( + new + { + CategoryPostId = 781275554, + CategoryPostName = "IPhone", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3499) + }, + new + { + CategoryPostId = 171705011, + CategoryPostName = "IPad", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3503) + }, + new + { + CategoryPostId = 357308849, + CategoryPostName = "Chromebook", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3532) + }, + new + { + CategoryPostId = 663701472, + CategoryPostName = "Earbuds", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3537) + }, + new + { + CategoryPostId = 1270846585, + CategoryPostName = "Wallet", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3541) + }, + new + { + CategoryPostId = 2145456831, + CategoryPostName = "Charger", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3545) + }); + }); + + modelBuilder.Entity("ObjectBusiness.Chat", b => + { + b.Property("ChatId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserAId") + .HasColumnType("int"); + + b.Property("UserALastReadMessageId") + .HasColumnType("int"); + + b.Property("UserBId") + .HasColumnType("int"); + + b.Property("UserBLastReadMessageId") + .HasColumnType("int"); + + b.HasKey("ChatId"); + + b.HasIndex("PostId"); + + b.HasIndex("UserAId"); + + b.HasIndex("UserBId"); + + b.ToTable("Chat"); + }); + + modelBuilder.Entity("ObjectBusiness.Institution", b => + { + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("InstitutionAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionCity") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionLogo") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionPhone") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionState") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionWebsite") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("InstitutionId"); + + b.ToTable("Institutions"); + }); + + modelBuilder.Entity("ObjectBusiness.InstitutionRequest", b => + { + b.Property("InstitutionRequestId") + .HasColumnType("int"); + + b.Property("AdditionalInformation") + .HasColumnType("nvarchar(max)"); + + b.Property("AdditionalNote") + .HasColumnType("nvarchar(max)"); + + b.Property("ApplicantName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ApplicantPhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("EstimatedPopulation") + .HasColumnType("int"); + + b.Property("InstitutionAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionCity") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionPhone") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionState") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionWebsite") + .HasColumnType("nvarchar(max)"); + + b.Property("IsVerifiedEmail") + .HasColumnType("bit"); + + b.Property("IsVerifiedInstitution") + .HasColumnType("bit"); + + b.Property("IsVerifiedWebsite") + .HasColumnType("bit"); + + b.Property("JobTitle") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RejectReason") + .HasColumnType("nvarchar(max)"); + + b.Property("ReviewedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ReviewedDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SubmittedDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.Property("WebsiteVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("WorkEmail") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("InstitutionRequestId"); + + b.ToTable("InstitutionRequests"); + }); + + modelBuilder.Entity("ObjectBusiness.Match", b => + { + b.Property("MatchId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("FoundPostId") + .HasColumnType("int"); + + b.Property("LostPostId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("MatchId"); + + b.HasIndex("FoundPostId"); + + b.HasIndex("LostPostId"); + + b.ToTable("Match"); + }); + + modelBuilder.Entity("ObjectBusiness.MessageChat", b => + { + b.Property("MessageChatId") + .HasColumnType("int"); + + b.Property("ChatId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("MessageContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserSenderId") + .HasColumnType("int"); + + b.HasKey("MessageChatId"); + + b.HasIndex("ChatId"); + + b.HasIndex("UserSenderId"); + + b.ToTable("MessageChat"); + }); + + modelBuilder.Entity("ObjectBusiness.Notifications", b => + { + b.Property("NotificationId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("NotificationContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NotificationType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PostMatchedId") + .HasColumnType("int"); + + b.Property("PostOriginalId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("NotificationId"); + + b.HasIndex("PostMatchedId"); + + b.HasIndex("PostOriginalId"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("ObjectBusiness.PickUpRequest", b => + { + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PickUpDate") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime2"); + + b.HasKey("RequestId"); + + b.HasIndex("PostId"); + + b.ToTable("PickUpRequest"); + }); + + modelBuilder.Entity("ObjectBusiness.Posts", b => + { + b.Property("PostId") + .HasColumnType("int"); + + b.Property("CategoryPostId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Image") + .HasColumnType("nvarchar(max)"); + + b.Property("IsReceived") + .HasColumnType("bit"); + + b.Property("OldUserId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TypePost") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Vector") + .HasColumnType("nvarchar(max)"); + + b.HasKey("PostId"); + + b.HasIndex("CategoryPostId"); + + b.HasIndex("UserId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("ObjectBusiness.Student", b => + { + b.Property("StudentId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("StudentId"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Student"); + }); + + modelBuilder.Entity("ObjectBusiness.TransferRequests", b => + { + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("ConfirmedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("PostId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ToUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RequestId"); + + b.HasIndex("PostId"); + + b.ToTable("TransferRequests"); + }); + + modelBuilder.Entity("ObjectBusiness.Users", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("Avatar") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DateOfBirth") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsAgreedToTerms") + .HasColumnType("bit"); + + b.Property("IsVerifiedEmail") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PickImage1") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PickImage2") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Role") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("UserId"); + + b.HasIndex("InstitutionId"); + + b.ToTable("Users"); + + b.HasData( + new + { + UserId = 2043193290, + Avatar = "avatar_CV.jpg", + CreatedAt = new DateTime(2026, 7, 25, 14, 44, 15, 852, DateTimeKind.Local).AddTicks(3191), + DateOfBirth = new DateTime(2007, 1, 30, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "mycampuslostfound@gmail.com", + FirstName = "System", + IsActive = true, + IsAgreedToTerms = true, + IsVerifiedEmail = true, + LastName = "Admin", + Password = "$2a$11$92uWViLQUKTIVIADFxhzqe39tDMoLWJX5e1FyXaeedfrq5CoMAGQ6", + PickImage1 = "1", + PickImage2 = "12", + Role = "SystemAdmin" + }); + }); + + modelBuilder.Entity("ObjectBusiness.VerificationCode", b => + { + b.Property("VerificationCodeId") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsUsed") + .HasColumnType("bit"); + + b.Property("MatchId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("VerificationCodeId"); + + b.HasIndex("MatchId") + .IsUnique(); + + b.ToTable("VerificationCode"); + }); + + modelBuilder.Entity("ObjectBusiness.Chat", b => + { + b.HasOne("ObjectBusiness.Posts", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "UserA") + .WithMany() + .HasForeignKey("UserAId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "UserB") + .WithMany() + .HasForeignKey("UserBId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Post"); + + b.Navigation("UserA"); + + b.Navigation("UserB"); + }); + + modelBuilder.Entity("ObjectBusiness.Match", b => + { + b.HasOne("ObjectBusiness.Posts", "FoundPost") + .WithMany() + .HasForeignKey("FoundPostId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Posts", "LostPost") + .WithMany() + .HasForeignKey("LostPostId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("FoundPost"); + + b.Navigation("LostPost"); + }); + + modelBuilder.Entity("ObjectBusiness.MessageChat", b => + { + b.HasOne("ObjectBusiness.Chat", "Chat") + .WithMany("Messages") + .HasForeignKey("ChatId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "UserSender") + .WithMany() + .HasForeignKey("UserSenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Chat"); + + b.Navigation("UserSender"); + }); + + modelBuilder.Entity("ObjectBusiness.Notifications", b => + { + b.HasOne("ObjectBusiness.Posts", "PostMatched") + .WithMany() + .HasForeignKey("PostMatchedId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("ObjectBusiness.Posts", "PostOriginal") + .WithMany() + .HasForeignKey("PostOriginalId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("PostMatched"); + + b.Navigation("PostOriginal"); + }); + + modelBuilder.Entity("ObjectBusiness.PickUpRequest", b => + { + b.HasOne("ObjectBusiness.Posts", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("ObjectBusiness.Posts", b => + { + b.HasOne("ObjectBusiness.CategoryPost", "CategoryPost") + .WithMany() + .HasForeignKey("CategoryPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ObjectBusiness.Users", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CategoryPost"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ObjectBusiness.Student", b => + { + b.HasOne("ObjectBusiness.Users", "User") + .WithOne("Student") + .HasForeignKey("ObjectBusiness.Student", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ObjectBusiness.TransferRequests", b => + { + b.HasOne("ObjectBusiness.Posts", "Post") + .WithMany("TransferRequests") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("ObjectBusiness.Users", b => + { + b.HasOne("ObjectBusiness.Institution", "Institution") + .WithMany("Users") + .HasForeignKey("InstitutionId"); + + b.Navigation("Institution"); + }); + + modelBuilder.Entity("ObjectBusiness.VerificationCode", b => + { + b.HasOne("ObjectBusiness.Match", "Match") + .WithOne("VerificationCode") + .HasForeignKey("ObjectBusiness.VerificationCode", "MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Match"); + }); + + modelBuilder.Entity("ObjectBusiness.Chat", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("ObjectBusiness.Institution", b => + { + b.Navigation("Users"); + }); + + modelBuilder.Entity("ObjectBusiness.Match", b => + { + b.Navigation("VerificationCode"); + }); + + modelBuilder.Entity("ObjectBusiness.Posts", b => + { + b.Navigation("TransferRequests"); + }); + + modelBuilder.Entity("ObjectBusiness.Users", b => + { + b.Navigation("Student"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ObjectBusiness/Role.cs b/ObjectBusiness/Role.cs index 50a8b8e..42d8bb2 100644 --- a/ObjectBusiness/Role.cs +++ b/ObjectBusiness/Role.cs @@ -8,7 +8,9 @@ namespace ObjectBusiness { public enum Role { - Student, - Admin + SystemAdmin, + InstitutionAdmin, + Staff, + Student } } diff --git a/ObjectBusiness/StatusRequestInstitution.cs b/ObjectBusiness/StatusRequestInstitution.cs new file mode 100644 index 0000000..5801415 --- /dev/null +++ b/ObjectBusiness/StatusRequestInstitution.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectBusiness +{ + public enum StatusRequestInstitution + { + Pending, + Approved, + Rejected, + } +} diff --git a/ObjectBusiness/StudentPopulationRange.cs b/ObjectBusiness/StudentPopulationRange.cs new file mode 100644 index 0000000..e50820b --- /dev/null +++ b/ObjectBusiness/StudentPopulationRange.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ObjectBusiness +{ + public enum StudentPopulationRange + { + Under5000 = 1, + From5000To15000 = 2, + From15001To30000 = 3, + Over30000 = 4 + } +} diff --git a/ObjectBusiness/Users.cs b/ObjectBusiness/Users.cs index 50fa246..b4d6696 100644 --- a/ObjectBusiness/Users.cs +++ b/ObjectBusiness/Users.cs @@ -14,14 +14,14 @@ public class Users { [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] public int UserId { get; set; } - public int InstitutionId { get; set; } + public int? InstitutionId { get; set; } [Required(ErrorMessage = "First name cannot be blank")] public string FirstName { get; set; } [Required(ErrorMessage = "Last name cannot be blank")] public string LastName { get; set; } [Required(ErrorMessage = "Email cannot be blank")] [EmailAddress] - public string Email { get; set; } // Institution email + public string Email { get; set; } // Institution email (ex: .gwinnetttech.edu) public DateTime? DateOfBirth { get; set; } [Required(ErrorMessage = "The roles cannot be different between student, parent, and admin")] public Role Role { get; set; } // Student or Admin diff --git a/ObjectBusiness/appsettings.json b/ObjectBusiness/appsettings.json index 57b8c54..03ea7c2 100644 --- a/ObjectBusiness/appsettings.json +++ b/ObjectBusiness/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - //"MyConnection": "Data Source=DESKTOP-A6BFNON\\ANSQLSERVER;Database=BackToMeDB;User Id=sa;Password=Baoan123.cntt;TrustServerCertificate=true;Encrypt=false;", - "MyConnection": "Data Source=my-sqlserver.cnoi0okmult3.ap-southeast-2.rds.amazonaws.com,1433;Database=BackToMeDB;User Id=admin;Password=Baoan123.cntt;TrustServerCertificate=true;Encrypt=false;" + "MyConnection": "Data Source=DESKTOP-A6BFNON\\ANSQLSERVER;Database=BackToMeDB;User Id=sa;Password=Baoan123.cntt;TrustServerCertificate=true;Encrypt=false;", + //"MyConnection": "Data Source=my-sqlserver.cnoi0okmult3.ap-southeast-2.rds.amazonaws.com,1433;Database=BackToMeDB;User Id=admin;Password=Baoan123.cntt;TrustServerCertificate=true;Encrypt=false;" } } \ No newline at end of file diff --git a/Repository/IInstitutionRepository.cs b/Repository/IInstitutionRepository.cs index 2fced37..f12cd7e 100644 --- a/Repository/IInstitutionRepository.cs +++ b/Repository/IInstitutionRepository.cs @@ -11,7 +11,7 @@ public interface IInstitutionRepository { public Task CreateInstitution(Institution institution); public IQueryable AllInstitutions(); - public Task GetInstitutionByID(int institutionId); + public Task GetInstitutionByID(int? institutionId); public Task GetInstitutionByNameAndAddress(string institutionName, string institutionAddress); } } diff --git a/Repository/IInstitutionRequestRepository.cs b/Repository/IInstitutionRequestRepository.cs new file mode 100644 index 0000000..130c8b7 --- /dev/null +++ b/Repository/IInstitutionRequestRepository.cs @@ -0,0 +1,19 @@ +using ObjectBusiness; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Repository +{ + public interface IInstitutionRequestRepository + { + public Task CreateInstitutionRequest(InstitutionRequest institutionRequest); + public IQueryable AllInstitutionRequests(); + public Task UpdateInstitutionRequest(); + public Task GetInstitutionRequestByID(int requestId); + public Task GetInstitutionRequestByWorkEmail(string workEmail); + public Task GetInstitutionRequestByNameAndAddress(string institutionName, string institutionAddress); + } +} diff --git a/Repository/IUsersRepository.cs b/Repository/IUsersRepository.cs index 2e55a0d..87d1dd9 100644 --- a/Repository/IUsersRepository.cs +++ b/Repository/IUsersRepository.cs @@ -15,8 +15,7 @@ public interface IUsersRepository public Task SuspendUser(); public Task UnsuspendUser(); public Task GetUserByID(int userId); - public Task SignIn(int studentId, string password, string? email); - public Task GetUserByStudentId(int studentId); + public Task SignIn(string password, string email); public Task GetUserByEmail(string email); public Task UpdateUser(); public Users GetAdmin(); diff --git a/Repository/InstitutionRepository.cs b/Repository/InstitutionRepository.cs index 47fbc77..eab73b1 100644 --- a/Repository/InstitutionRepository.cs +++ b/Repository/InstitutionRepository.cs @@ -38,7 +38,7 @@ public IQueryable AllInstitutions() #endregion #region Get user By ID - public async Task GetInstitutionByID(int institutionId) + public async Task GetInstitutionByID(int? institutionId) { var user = await institutionDAO.GetInstitutionByID(institutionId); return user; diff --git a/Repository/InstitutionRequestRepository.cs b/Repository/InstitutionRequestRepository.cs new file mode 100644 index 0000000..ff957b4 --- /dev/null +++ b/Repository/InstitutionRequestRepository.cs @@ -0,0 +1,79 @@ +using DataAccess; +using ObjectBusiness; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Repository +{ + public class InstitutionRequestRepository : IInstitutionRequestRepository + { + #region Variables + private readonly InstitutionRequestDAO institutionRequestDAO; + #endregion + + #region Constructor + public InstitutionRequestRepository(InstitutionRequestDAO institutionRequestDAO) + { + this.institutionRequestDAO = institutionRequestDAO; + } + #endregion + + #region GET All Institution Requests + public IQueryable AllInstitutionRequests() + { + var listInstitution = institutionRequestDAO.AllInstitutionRequests(); + return listInstitution; + } + #endregion + + #region Get Institution Request By Name And Address + public async Task GetInstitutionRequestByNameAndAddress(string institutionName, string institutionAddress) + { + var request = await institutionRequestDAO.GetInstitutionRequestByNameAndAddress(institutionName, institutionAddress); + return request; + } + #endregion + + #region Create Institution Request + public async Task CreateInstitutionRequest(InstitutionRequest institutionRequest) + { + var requestExists = await institutionRequestDAO + .GetInstitutionRequestByNameAndAddress(institutionRequest.InstitutionName, institutionRequest.InstitutionAddress); + if (requestExists != null) + { + return false; // Request already exists + } + + var isAdded = await institutionRequestDAO.CreateInstitutionRequest(institutionRequest); + return isAdded; + } + #endregion + + #region Get Institution Request By ID + public async Task GetInstitutionRequestByID(int requestId) + { + var request = await institutionRequestDAO.GetInstitutionRequestByID(requestId); + return request; + } + #endregion + + #region Get Institution Request By Work Email + public async Task GetInstitutionRequestByWorkEmail(string workEmail) + { + var institutionRequest = await institutionRequestDAO.GetInstitutionRequestByWorkEmail(workEmail); + return institutionRequest; + } + #endregion + + #region Update Institution Request + public async Task UpdateInstitutionRequest() + { + var isUpdated = await institutionRequestDAO.UpdateInstitutionRequest(); + return isUpdated; + } + #endregion + } +} diff --git a/Repository/PickUpRequestRepository.cs b/Repository/PickUpRequestRepository.cs index 7efb09d..02b9616 100644 --- a/Repository/PickUpRequestRepository.cs +++ b/Repository/PickUpRequestRepository.cs @@ -173,7 +173,7 @@ Our admin is reviewing your pickup time.

- + 🔎 View Item Details

@@ -347,7 +347,7 @@ You can come to pick up the item at the confirmed time below.

- + 📄 View Pickup Details

@@ -385,7 +385,7 @@ public async Task AcceptTimeRescheduled(int requestId) if (result != null) { var post = await postDAO.GetPostById(result.PostId); - var admin = usersDAO.GetAdmin(); + var admin = usersDAO.GetSystemAdmin(); if (post != null && admin != null) { @@ -503,7 +503,7 @@ The pickup schedule is now fully confirmed.

- + 📄 View Pickup Details

@@ -644,7 +644,7 @@ The admin has updated your pickup schedule.

- + View Updated Details

diff --git a/Repository/PostRepository.cs b/Repository/PostRepository.cs index ed20eb3..3de0551 100644 --- a/Repository/PostRepository.cs +++ b/Repository/PostRepository.cs @@ -293,7 +293,7 @@ .details th {{

- 🔎 View Item + 🔎 View Item

We recommend checking as soon as possible to reclaim your lost item.

@@ -454,7 +454,7 @@ .details th {{

- 🔎 View Found Item + 🔎 View Found Item

Act quickly to reclaim your lost item before someone else does!

@@ -711,7 +711,7 @@ .details th {{

- 🔎 View Item + 🔎 View Item

We recommend checking as soon as possible to reclaim your lost item.

@@ -872,7 +872,7 @@ .details th {{

- 🔎 View Found Item + 🔎 View Found Item

Act quickly to reclaim your lost item before someone else does!

@@ -903,7 +903,7 @@ .details th {{ } // Send Email to all students - if (post.TypePost == TypePost.Found && user.Role == Role.Admin) + if (post.TypePost == TypePost.Found && user.Role == Role.SystemAdmin) { var listUsers = userDAO.AllUsers().ToList(); @@ -1029,7 +1029,7 @@ .details th {{

- 🔎 View Found Item + 🔎 View Found Item

Thank you for your quick attention!

diff --git a/Repository/UsersRepository.cs b/Repository/UsersRepository.cs index 9f88343..46b585a 100644 --- a/Repository/UsersRepository.cs +++ b/Repository/UsersRepository.cs @@ -40,14 +40,6 @@ public IQueryable SearchUserByEmail(string query) } #endregion - #region GET User By Student Id - public async Task GetUserByStudentId(int studentId) - { - var user = await usersDAO.GetUserByStudentId(studentId); - return user; - } - #endregion - #region Get user By Email public async Task GetUserByEmail(string email) { @@ -59,7 +51,7 @@ public async Task GetUserByEmail(string email) #region Get Admin public Users GetAdmin() { - var user = usersDAO.GetAdmin(); + var user = usersDAO.GetSystemAdmin(); return user; } #endregion @@ -87,29 +79,16 @@ public async Task SignUp(Users user) #endregion #region Sign In - public async Task SignIn(int studentId, string password, string? email) + public async Task SignIn(string password, string email) { - if (studentId == 0 && email != null) - { - var user = studentId > 0 - ? await usersDAO.GetUserByStudentId(studentId) - : await GetUserByEmail(email); - - if (user != null && BCrypt.Net.BCrypt.EnhancedVerify(password, user.Password)) - { - return user; - } - return null; - } - else + var user = await GetUserByEmail(email); + + if (user != null && BCrypt.Net.BCrypt.EnhancedVerify(password, user.Password)) { - var user = await GetUserByStudentId(studentId); - if (user != null && BCrypt.Net.BCrypt.EnhancedVerify(password, user.Password)) - { - return user; - } - return null; + return user; } + + return null; } #endregion diff --git a/Services/HolderReminderService.cs b/Services/HolderReminderService.cs index 18e996b..59bf96c 100644 --- a/Services/HolderReminderService.cs +++ b/Services/HolderReminderService.cs @@ -151,7 +151,7 @@ .details th {{

- 🔎 Review Found Item + 🔎 Review Found Item

Act quickly to ensure lost items are properly handled!