Compare commits
3 Commits
81d6b70673
...
e81bf53def
| Author | SHA1 | Date | |
|---|---|---|---|
|
e81bf53def
|
|||
|
8875060917
|
|||
|
8300331276
|
@@ -12,6 +12,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -79,6 +79,23 @@ internal static class ExerciseEndpoints
|
|||||||
.WithSummary("Update exercise")
|
.WithSummary("Update exercise")
|
||||||
.WithDescription("Updates the name of an exercise for the specified user.");
|
.WithDescription("Updates the name of an exercise for the specified user.");
|
||||||
|
|
||||||
|
group.MapDelete("/users/{userId}/exercises/{exerciseId:int}", async (string userId, int exerciseId, AppDbContext db) =>
|
||||||
|
{
|
||||||
|
var user = await UserProvisioning.EnsureUserAsync(db, userId);
|
||||||
|
var exercise = await db.Exercises.FirstOrDefaultAsync(e => e.Id == exerciseId && e.UserId == user.Id);
|
||||||
|
if (exercise is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Exercises.Remove(exercise);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return Results.NoContent();
|
||||||
|
})
|
||||||
|
.WithSummary("Delete exercise")
|
||||||
|
.WithDescription("Deletes an exercise for the specified user.");
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,23 @@ internal static class RoutineEndpoints
|
|||||||
.WithSummary("Update routine")
|
.WithSummary("Update routine")
|
||||||
.WithDescription("Updates routine metadata and exercise ordering.");
|
.WithDescription("Updates routine metadata and exercise ordering.");
|
||||||
|
|
||||||
|
group.MapDelete("/users/{userId}/routines/{routineId:int}", async (string userId, int routineId, AppDbContext db) =>
|
||||||
|
{
|
||||||
|
var user = await UserProvisioning.EnsureUserAsync(db, userId);
|
||||||
|
var routine = await db.Routines.FirstOrDefaultAsync(r => r.Id == routineId && r.UserId == user.Id);
|
||||||
|
if (routine is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Routines.Remove(routine);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return Results.NoContent();
|
||||||
|
})
|
||||||
|
.WithSummary("Delete routine")
|
||||||
|
.WithDescription("Deletes a routine and its associated data for the specified user.");
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddDefaultPolicy(policy =>
|
options.AddDefaultPolicy(policy =>
|
||||||
@@ -30,6 +31,12 @@ var app = builder.Build();
|
|||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(options =>
|
||||||
|
{
|
||||||
|
options.SwaggerEndpoint("/openapi/v1.json", "ASTRAIN API");
|
||||||
|
options.RoutePrefix = "docs";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// app.UseHttpsRedirection();
|
// app.UseHttpsRedirection();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@inject ApiClient Api
|
@inject ApiClient Api
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject UserContext UserContext
|
@inject UserContext UserContext
|
||||||
|
@inject IJSRuntime JS
|
||||||
|
|
||||||
<PageTitle>Exercises</PageTitle>
|
<PageTitle>Exercises</PageTitle>
|
||||||
|
|
||||||
@@ -50,7 +51,10 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="item-title">@exercise.Name</div>
|
<div class="item-title">@exercise.Name</div>
|
||||||
<button class="ghost" @onclick="() => StartEdit(exercise)">Edit</button>
|
<div class="actions">
|
||||||
|
<button class="ghost" @onclick="() => StartEdit(exercise)" aria-label="Edit exercise">✏️</button>
|
||||||
|
<button class="ghost" @onclick="() => DeleteExerciseAsync(exercise.Id)" aria-label="Delete exercise">🗑️</button>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -148,4 +152,13 @@
|
|||||||
|
|
||||||
CancelEdit();
|
CancelEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DeleteExerciseAsync(int exerciseId)
|
||||||
|
{
|
||||||
|
var confirmed = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this exercise?");
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
await Api.DeleteExerciseAsync(UserContext.UserId, exerciseId);
|
||||||
|
ExerciseList.RemoveAll(e => e.Id == exerciseId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@inject ApiClient Api
|
@inject ApiClient Api
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject UserContext UserContext
|
@inject UserContext UserContext
|
||||||
|
@inject IJSRuntime JS
|
||||||
|
|
||||||
<PageTitle>Routines</PageTitle>
|
<PageTitle>Routines</PageTitle>
|
||||||
|
|
||||||
@@ -25,6 +26,8 @@
|
|||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (EditingRoutine is null)
|
||||||
|
{
|
||||||
@if (ShowCreateRoutine)
|
@if (ShowCreateRoutine)
|
||||||
{
|
{
|
||||||
<section class="card">
|
<section class="card">
|
||||||
@@ -43,6 +46,8 @@
|
|||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (ExerciseList.Count > 0 || RoutineList.Count > 0)
|
||||||
|
{
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<h2>Your Routines</h2>
|
<h2>Your Routines</h2>
|
||||||
@if (IsLoading)
|
@if (IsLoading)
|
||||||
@@ -64,7 +69,8 @@
|
|||||||
<div class="item-subtitle">@string.Join(" · ", routine.Exercises.Select(e => e.Name))</div>
|
<div class="item-subtitle">@string.Join(" · ", routine.Exercises.Select(e => e.Name))</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="ghost" @onclick="() => StartEdit(routine)">Edit</button>
|
<button class="ghost" @onclick="() => StartEdit(routine)" aria-label="Edit routine">✏️</button>
|
||||||
|
<button class="ghost" @onclick="() => DeleteRoutineAsync(routine.Id)" aria-label="Delete routine">🗑️</button>
|
||||||
<button class="primary" @onclick="() => StartRun(routine)">Start</button>
|
<button class="primary" @onclick="() => StartRun(routine)">Start</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,6 +78,8 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@if (EditingRoutine is not null)
|
@if (EditingRoutine is not null)
|
||||||
{
|
{
|
||||||
@@ -246,6 +254,15 @@
|
|||||||
CancelEdit();
|
CancelEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DeleteRoutineAsync(int routineId)
|
||||||
|
{
|
||||||
|
var confirmed = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this routine?");
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
await Api.DeleteRoutineAsync(UserContext.UserId, routineId);
|
||||||
|
RoutineList.RemoveAll(r => r.Id == routineId);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task StartRun(RoutineDto routine)
|
private async Task StartRun(RoutineDto routine)
|
||||||
{
|
{
|
||||||
ActiveRun = routine;
|
ActiveRun = routine;
|
||||||
@@ -275,8 +292,11 @@
|
|||||||
return ExerciseList.FirstOrDefault(e => e.Id == exerciseId)?.Name ?? "Exercise";
|
return ExerciseList.FirstOrDefault(e => e.Id == exerciseId)?.Name ?? "Exercise";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AbortRun()
|
private async Task AbortRun()
|
||||||
{
|
{
|
||||||
|
var confirmed = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to abort this routine run?");
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
ActiveRun = null;
|
ActiveRun = null;
|
||||||
RunEntries = new List<RoutineRunEntryDto>();
|
RunEntries = new List<RoutineRunEntryDto>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ public class ApiClient
|
|||||||
return await response.Content.ReadFromJsonAsync<ExerciseDto>();
|
return await response.Content.ReadFromJsonAsync<ExerciseDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteExerciseAsync(string userId, int exerciseId)
|
||||||
|
{
|
||||||
|
await _http.DeleteAsync($"/api/users/{userId}/exercises/{exerciseId}");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<RoutineDto>> GetRoutinesAsync(string userId)
|
public async Task<List<RoutineDto>> GetRoutinesAsync(string userId)
|
||||||
{
|
{
|
||||||
return await _http.GetFromJsonAsync<List<RoutineDto>>($"/api/users/{userId}/routines") ?? new List<RoutineDto>();
|
return await _http.GetFromJsonAsync<List<RoutineDto>>($"/api/users/{userId}/routines") ?? new List<RoutineDto>();
|
||||||
@@ -59,6 +64,11 @@ public class ApiClient
|
|||||||
return await response.Content.ReadFromJsonAsync<RoutineDto>();
|
return await response.Content.ReadFromJsonAsync<RoutineDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteRoutineAsync(string userId, int routineId)
|
||||||
|
{
|
||||||
|
await _http.DeleteAsync($"/api/users/{userId}/routines/{routineId}");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<RoutineRunSummaryDto> GetLastRunAsync(string userId, int routineId)
|
public async Task<RoutineRunSummaryDto> GetLastRunAsync(string userId, int routineId)
|
||||||
{
|
{
|
||||||
return await _http.GetFromJsonAsync<RoutineRunSummaryDto>($"/api/users/{userId}/routines/{routineId}/last-run")
|
return await _http.GetFromJsonAsync<RoutineRunSummaryDto>($"/api/users/{userId}/routines/{routineId}/last-run")
|
||||||
|
|||||||
Reference in New Issue
Block a user