2 Commits

Author SHA1 Message Date
426288d728 feat: enable adding relevant skills to events 2025-06-01 03:13:53 +02:00
72fbfe982f feat: skills endpoint, stylistic changes 2025-06-01 02:15:20 +02:00
7 changed files with 160 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using WebApp.Data; using WebApp.Data;
@@ -18,6 +18,7 @@ namespace WebApp.Endpoints
var group = app.MapGroup("api/auth") var group = app.MapGroup("api/auth")
.WithParameterValidation(); .WithParameterValidation();
// POST /api/auth/login
group.MapPost("/login", async (LoginDto dto, ApplicationDbContext context, GeneralUseHelpers guh) => group.MapPost("/login", async (LoginDto dto, ApplicationDbContext context, GeneralUseHelpers guh) =>
{ {
var user = await context.WebUsers.FirstOrDefaultAsync(u => u.Email == dto.Email); var user = await context.WebUsers.FirstOrDefaultAsync(u => u.Email == dto.Email);
@@ -38,6 +39,7 @@ namespace WebApp.Endpoints
}); });
}); });
// POST /api/auth/logout
group.MapPost("/logout", async (HttpContext httpContext, GeneralUseHelpers guh) => group.MapPost("/logout", async (HttpContext httpContext, GeneralUseHelpers guh) =>
{ {
var token = await guh.GetTokenFromHTTPContext(httpContext); var token = await guh.GetTokenFromHTTPContext(httpContext);
@@ -54,6 +56,7 @@ namespace WebApp.Endpoints
return Results.Ok(new { success = true }); return Results.Ok(new { success = true });
}); });
// GET /api/auth/my_account
group.MapGet("/my_account", async (HttpContext httpContext, GeneralUseHelpers guh) => group.MapGet("/my_account", async (HttpContext httpContext, GeneralUseHelpers guh) =>
{ {
var token = await guh.GetTokenFromHTTPContext(httpContext); var token = await guh.GetTokenFromHTTPContext(httpContext);
@@ -77,6 +80,7 @@ namespace WebApp.Endpoints
}) })
.WithName(GetUserEndpointName); .WithName(GetUserEndpointName);
// GET /api/auth/my_events
group.MapGet("/my_events", async (HttpContext httpContext, GeneralUseHelpers guh, ApplicationDbContext context) => group.MapGet("/my_events", async (HttpContext httpContext, GeneralUseHelpers guh, ApplicationDbContext context) =>
{ {
var token = await guh.GetTokenFromHTTPContext(httpContext); var token = await guh.GetTokenFromHTTPContext(httpContext);
@@ -122,13 +126,14 @@ namespace WebApp.Endpoints
}); });
// POST /api/auth/add_skill
group.MapPost("/add_skill", async (SingleSkillDto dto, HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) => group.MapPost("/add_skill", async (SingleSkillDto dto, HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) =>
{ {
// Uzyskaj użytkownika z tokenu // Uzyskaj użytkownika z tokenu
Token? token = await guh.GetTokenFromHTTPContext(httpContext); Token? token = await guh.GetTokenFromHTTPContext(httpContext);
User? user = await guh.GetUserFromToken(token); User? user = await guh.GetUserFromToken(token);
// Tylko wolontariusze powinno móc dodawać swoje skille // Tylko wolontariusze powinni móc dodawać swoje skille
if (user == null || user.IsOrganisation) { if (user == null || user.IsOrganisation) {
return Results.Json(new { message = "Unauthorized" }, statusCode: 401); return Results.Json(new { message = "Unauthorized" }, statusCode: 401);
} }
@@ -158,14 +163,14 @@ namespace WebApp.Endpoints
return Results.Json(new { message = "Skill added successfully!" }, statusCode: 201); return Results.Json(new { message = "Skill added successfully!" }, statusCode: 201);
}); });
// POST /api/auth/remove_skill
group.MapPost("/remove_skill", async (SingleSkillDto dto, HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) => group.MapPost("/remove_skill", async (SingleSkillDto dto, HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) =>
{ {
// Uzyskaj użytkownika z tokenu // Uzyskaj użytkownika z tokenu
Token? token = await guh.GetTokenFromHTTPContext(httpContext); Token? token = await guh.GetTokenFromHTTPContext(httpContext);
User? user = await guh.GetUserFromToken(token); User? user = await guh.GetUserFromToken(token);
// Tylko wolontariusze powinien móc usuwac swoje skille // Tylko wolontariusze powinni móc usuwać swoje skille
if (user == null || user.IsOrganisation) if (user == null || user.IsOrganisation)
{ {
return Results.Json(new { message = "Unauthorized" }, statusCode: 401); return Results.Json(new { message = "Unauthorized" }, statusCode: 401);
@@ -178,28 +183,28 @@ namespace WebApp.Endpoints
return Results.Json(new { message = "Skill not found" }, statusCode: 404); return Results.Json(new { message = "Skill not found" }, statusCode: 404);
} }
// Sprawdzamy, czy ten użytkownik ma już taki skill. Jeżeli nie ma, nie ma sensu usuwac go kilkukrotnie. // Sprawdzamy, czy ten użytkownik ma już taki skill. Jeżeli nie ma, to nie ma sensu usuwać czegoś, czego nie ma.
VolunteerSkill? vs = await context.VolunteerSkills.FirstOrDefaultAsync(v => v.UserId == user.UserId && v.SkillId == dto.Skill); VolunteerSkill? vs = await context.VolunteerSkills.FirstOrDefaultAsync(v => v.UserId == user.UserId && v.SkillId == dto.Skill);
if (vs is not null) if (vs is not null)
{ {
// Nie ma - zatem musimy dodać nowy VolunteerSkill do bazy // Ma - zatem musimy usunąć otrzymany VolunteerSkill z bazy
VolunteerSkill newVs = dto.ToVolunteerSkillEntity(user.UserId); VolunteerSkill newVs = dto.ToVolunteerSkillEntity(user.UserId);
await context.VolunteerSkills.Where(v => v.SkillId == dto.Skill) await context.VolunteerSkills.Where(v => v.SkillId == dto.Skill)
.ExecuteDeleteAsync(); .ExecuteDeleteAsync();
} }
else else
{ {
// Ma - (ta para UserId <-> SkillId już istnieje w bazie) użytkownik już ma ten skill // Nie ma - (ta para UserId <-> SkillId nie istnieje w bazie). Zwracamy błąd.
return Results.Json(new { message = "You don't have this skill" }, statusCode: 400); return Results.Json(new { message = "You don't have this skill" }, statusCode: 400);
} }
return Results.Json(new { message = "Skill deleted successfully!" }, statusCode: 201); return Results.Json(new { message = "Skill deleted successfully!" }, statusCode: 201);
}); });
group.MapGet("/get_skills", async (HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) => // GET /api/auth/skills
group.MapGet("/skills", async (HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) =>
{ {
// Uzyskaj użytkownika z tokenu // Uzyskaj użytkownika z tokenu
Token? token = await guh.GetTokenFromHTTPContext(httpContext); Token? token = await guh.GetTokenFromHTTPContext(httpContext);

View File

@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using WebApp.Data; using WebApp.Data;
using WebApp.DTOs; using WebApp.DTOs;
@@ -134,7 +134,7 @@ namespace WebApp.Endpoints
// Uzyskaj organizację z tokenu // Uzyskaj organizację z tokenu
Token? token = await guhf.GetTokenFromHTTPContext(httpContext); Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
Organisation? org = await guhf.GetOrganisationFromToken(token); Organisation? org = await guhf.GetOrganisationFromToken(token);
if (org is null) return Results.StatusCode(403); if (org is null) return Results.Unauthorized();
// Sprawdź, czy organizacja ma prawo // Sprawdź, czy organizacja ma prawo
// do usunięcia tego (EventId = id) eventu. // do usunięcia tego (EventId = id) eventu.
@@ -161,7 +161,6 @@ namespace WebApp.Endpoints
List<EventSummaryDto> SearchResults = []; List<EventSummaryDto> SearchResults = [];
List<Event> AllEvents = await dbContext.Events.ToListAsync(); List<Event> AllEvents = await dbContext.Events.ToListAsync();
if (sort is null || sort.ToUpper() != "ASC") if (sort is null || sort.ToUpper() != "ASC")
{ {
@@ -223,6 +222,91 @@ namespace WebApp.Endpoints
return Results.Ok(SearchResults); return Results.Ok(SearchResults);
}); });
// POST /events/1/add_skill
group.MapPost("/{id}/add_skill/",
async (int id, SingleSkillDto dto, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
{
Event? Eve = await dbContext.Events.FindAsync(id);
if (Eve is null) return Results.Json(new { message = "Event not found" }, statusCode: 404);
// Sprawdź, czy token należy do organizacji, a jeżeli tak, to do której.
Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
Organisation? org = await guhf.GetOrganisationFromToken(token);
// Jeśli token należy do organizacji, która utworzyła to wydarzenie,
// to zwróć także EventRegistrations. W przeciwnym razie usuń to pole
// przed jego wysłaniem!
if (org is null || org.OrganisationId != Eve.OrganisationId) return Results.Unauthorized();
// Szukamy skilla w bazie o ID takim, jak w otrzymanym DTO
Skill? skill = await dbContext.Skills.FindAsync(dto.Skill);
if (skill is null)
{
return Results.Json(new { message = "Skill not found" }, statusCode: 404);
}
// Sprawdzamy, czy to wydarzenie nie ma już takiego skilla. Jeżeli ma, nie ma sensu dodawać go kilkukrotnie.
EventSkill? es = await dbContext.EventSkills.FirstOrDefaultAsync(e => e.EventId == id && e.SkillId == dto.Skill);
if (es is null)
{
// Nie ma - zatem musimy dodać nowy EventSkill do bazy
EventSkill newEs = dto.ToEventSkillEntity(Eve.EventId);
dbContext.EventSkills.Add(newEs);
await dbContext.SaveChangesAsync();
}
else
{
// Ma - (ta para EventId <-> SkillId już istnieje w bazie); ten Event posiada już ten skill
return Results.Json(new { message = "Skill already assinged to this event!" }, statusCode: 400);
}
return Results.Json(new { message = "Skill added to event successfully!" }, statusCode: 201);
});
// POST /events/1/renive_skill
group.MapPost("/{id}/remove_skill/",
async (int id, SingleSkillDto dto, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
{
Event? Eve = await dbContext.Events.FindAsync(id);
if (Eve is null) return Results.Json(new { message = "Event not found" }, statusCode: 404);
// Sprawdź, czy token należy do organizacji, a jeżeli tak, to do której.
Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
Organisation? org = await guhf.GetOrganisationFromToken(token);
// Jeśli token należy do organizacji, która utworzyła to wydarzenie,
// to zwróć także EventRegistrations. W przeciwnym razie usuń to pole
// przed jego wysłaniem!
if (org is null || org.OrganisationId != Eve.OrganisationId) return Results.Unauthorized();
// Szukamy skilla w bazie o ID takim, jak w otrzymanym DTO
Skill? skill = await dbContext.Skills.FindAsync(dto.Skill);
if (skill is null)
{
return Results.Json(new { message = "Skill not found" }, statusCode: 404);
}
// Sprawdzamy, czy to wydarzenie nie ma już takiego skilla. Jeżeli nie ma, to nie ma sensu kasować czegoś, czego nie ma.
EventSkill? es = await dbContext.EventSkills.FirstOrDefaultAsync(e => e.EventId == id && e.SkillId == dto.Skill);
if (es is not null)
{
// Ma - zatem musimy usunąć ten EventSkill z bazy
await dbContext.EventSkills.Where(e => e.SkillId == dto.Skill)
.ExecuteDeleteAsync();
}
else
{
// Nie ma - (ta para EventId <-> SkillId nie istnieje w bazie); ten Event nie posiada tego skill'a
return Results.Json(new { message = "This skill isn't assinged to this event!" }, statusCode: 400);
}
return Results.Json(new { message = "Skill removed from event successfully!" }, statusCode: 201);
});
return group; return group;
} }
} }

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore;
using WebApp.Data;
using WebApp.Mapping;
namespace WebApp.Endpoints;
public static class SkillsEndpoints
{
const string GetSkillEndpointName = "GetSkill";
public static RouteGroupBuilder MapSkillsEndpoints(this WebApplication app)
{
var group = app.MapGroup("api/skills").WithParameterValidation();
// GET /skills
group.MapGet("/",
async (ApplicationDbContext dbContext) =>
await dbContext.Skills
.OrderBy(Sk => Sk.SkillId)
.Select(Sk => Sk.ToSkillSummaryDto()) // SkillSummaryDto
.AsNoTracking()
.ToListAsync());
return group;
}
}

View File

@@ -0,0 +1,16 @@
using WebApp.DTOs;
using WebApp.Entities;
namespace WebApp.Mapping;
public static class EventSkillMapping
{
public static EventSkill ToEventSkillEntity(this SingleSkillDto SSDto, int eid)
{
return new EventSkill()
{
EventId = eid,
SkillId = SSDto.Skill,
};
}
}

View File

@@ -1,8 +1,8 @@
using WebApp.DTOs; using WebApp.DTOs;
using WebApp.Entities; using WebApp.Entities;
namespace WebApp.Mapping namespace WebApp.Mapping;
{
public static class VolunteerSkillMapping public static class VolunteerSkillMapping
{ {
public static VolunteerSkill ToVolunteerSkillEntity(this SingleSkillDto SSDto, int uid) public static VolunteerSkill ToVolunteerSkillEntity(this SingleSkillDto SSDto, int uid)
@@ -14,4 +14,3 @@ namespace WebApp.Mapping
}; };
} }
} }
}

View File

@@ -53,5 +53,6 @@ app.UseRouting(); // Enables routing to match incoming request to endpoints
app.MapEventsEndpoints(); app.MapEventsEndpoints();
app.MapOrganizationsEndpoints(); app.MapOrganizationsEndpoints();
app.MapAuthEndpoints(); app.MapAuthEndpoints();
app.MapSkillsEndpoints();
app.Run(); app.Run();

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -74,6 +74,10 @@
<span>Log in</span> <span>Log in</span>
<span>&#11166;</span> <span>&#11166;</span>
</button> </button>
<button id="signUpBtn" class="button" type="button" onclick="alert('Coming soon!')">
<span>Sign up</span>
<span>&#11166;</span>
</button>
<p id="message" style="color: red;"></p> <p id="message" style="color: red;"></p>
</form> </form>