Compare commits

...

3 Commits

Author SHA1 Message Date
Namu
9c5d24354a Feat: Add project deletion
Some checks failed
build / build (push) Failing after 47s
SonarQube Scan / SonarQube Trigger (push) Successful in 34s
2026-01-06 17:17:27 +01:00
Namu
acea39fb6b Feat: Add Project and ProjectTask entities
Some checks failed
build / build (push) Failing after 28s
SonarQube Scan / SonarQube Trigger (push) Successful in 33s
2025-12-27 17:54:07 +01:00
Namu
0327d70657 Feat: Add docker support 2025-12-27 01:06:54 +01:00
32 changed files with 1709 additions and 56 deletions

30
.dockerignore Normal file
View File

@@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**

View File

@@ -1,3 +1,6 @@
<Solution>
<Project Path="docker-compose.dcproj">
<Build />
</Project>
<Project Path="WorkManagementTool/WorkManagementTool.csproj" />
</Solution>

View File

@@ -18,12 +18,6 @@
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="auth">
<span class="bi bi-lock-nav-menu" aria-hidden="true"></span> Auth Required
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="school-subjects">
School Subjects
@@ -36,6 +30,12 @@
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="projects">
Projects
</NavLink>
</div>
<AuthorizeView>
<Authorized>
<div class="nav-item px-3">

View File

@@ -1,13 +0,0 @@
@page "/auth"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Auth</PageTitle>
<h1>You are authenticated</h1>
<AuthorizeView>
Hello @context.User.Identity?.Name!
</AuthorizeView>

View File

@@ -2,6 +2,6 @@
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
<h1>Welcome to Work Management tool</h1>
Welcome to your new app.
This application aims to help you with your work by managing task, homework and more !

View File

@@ -0,0 +1,42 @@
@page "/projects/creation"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@rendermode InteractiveServer
@using WorkManagementTool.Services;
@using WorkManagementTool.Data.Entities;
@inject ProjectService ProjectService
@inject NavigationManager NavigationManager
<h3>ProjectCreation</h3>
<EditForm OnValidSubmit="CreateProject" Model="@Model">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label for="name" class="form-label">Name</label>
<InputText id="name" class="form-control" @bind-Value="Model.Name" />
</div>
<div class="form-group">
<label for="description" class="form-label">Description</label>
<InputTextArea id="description" class="form-control" @bind-Value="Model.Description" />
</div>
<button type="submit" class="btn btn-primary">Create Project</button>
</EditForm>
@code {
private Project Model = new();
public async Task CreateProject()
{
await ProjectService.AddProjectAsync(Model);
NavigationManager.NavigateTo("/projects");
}
}

View File

@@ -0,0 +1,30 @@
@using Microsoft.AspNetCore.Authorization
@using WorkManagementTool.Data.Entities;
@using WorkManagementTool.Services;
@inject ProjectService ProjectService
@attribute [Authorize]
@rendermode InteractiveServer
<button class="btn btn-danger" @onclick="DeleteProjectAsync">-</button>
@code {
[Parameter]
public int ProjectId { get; set; }
public async Task DeleteProjectAsync(MouseEventArgs e)
{
var project = await ProjectService.GetProjectByIdAsync(ProjectId);
if (project is null)
throw new ArgumentNullException(nameof(project));
await ProjectService.DeleteProjectAsync(project.Id);
await OnDeleted.InvokeAsync(ProjectId);
}
[Parameter]
public EventCallback<int> OnDeleted { get; set; }
}

View File

@@ -0,0 +1,50 @@
@page "/projects"
@using Microsoft.AspNetCore.Authorization
@using WorkManagementTool.Services;
@using WorkManagementTool.Data.Entities;
@rendermode InteractiveServer
@attribute [Authorize]
@inject ProjectService ProjectService
<NavLink class="btn btn-primary mb-3" href="/projects/creation">Create project</NavLink>
@if (projects is null || projects.Count == 0)
{
<p class="alert alert-warning">No projects found</p>
}
else
{
<h3>Project list</h3>
<ul class="list-unstyled">
@foreach (var project in projects)
{
<li>
<h4>@project.Name</h4>
<p>@project.Description</p>
<div class="btn-group">
<NavLink class="btn btn-primary" href="@($"/projects/tasks/{@project.Id}")">Details</NavLink>
<ProjectDeletion ProjectId="@project.Id" OnDeleted="HandleDeletion"></ProjectDeletion>
</div>
</li>
}
</ul>
}
@code {
private List<Project> projects = new();
protected override async Task OnInitializedAsync()
{
projects = await ProjectService.GetAllProjectsAsync();
}
private async Task HandleDeletion(int projectId)
{
projects.RemoveAll(p => p.Id == projectId);
StateHasChanged();
}
}

View File

@@ -0,0 +1,31 @@
@page "/projects/tasks/{ProjectId:int}"
@using Microsoft.AspNetCore.Authorization
@using WorkManagementTool.Data.Entities;
@using WorkManagementTool.Services;
@attribute [Authorize]
@rendermode InteractiveServer
@inject ProjectService ProjectService
@if (Model is null)
{
<p class="alert alert-warning">Project not found</p>
}
else
{
<p>@ProjectId</p>
<h3>@Model.Name</h3>
}
@code {
[Parameter]
public int ProjectId { get; set; }
public Project? Model { get; set; }
protected async override Task OnInitializedAsync()
{
Model = await ProjectService.GetProjectByIdAsync(ProjectId);
}
}

View File

@@ -15,20 +15,18 @@
}
else
{
<dl>
<dh>Name</dh>
<dd>@schoolSubject.Name</dd>
<dl class="row">
<dh class="col-3">Name</dh>
<dd class="col-9">@schoolSubject.Name</dd>
@if (!string.IsNullOrWhiteSpace(schoolSubject.Description))
{
<dh>Description:</dh>
<dd>@schoolSubject.Description</dd>
<dh class="col-3">Description:</dh>
<dd class="col-9">@schoolSubject.Description</dd>
}
<dh>Created At</dh>
<dd>@schoolSubject.CreatedAt.ToLocalTime()</dd>
<dh>Actions</dh>
<dh class="col-3">Created At</dh>
<dd class="col-9">@schoolSubject.CreatedAt.ToLocalTime()</dd>
</dl>
}
@@ -41,6 +39,5 @@ else
protected override async Task OnInitializedAsync()
{
schoolSubject = await SchoolSubjectService.GetSchoolSubjectByIdAsync(Id);
}
}

View File

@@ -11,7 +11,7 @@
<h3>SchoolSubjectListing</h3>
<div>
<NavLink class="btn btn-primary" href="school-subjects/creation" Match="NavLinkMatch.All">
<NavLink class="btn btn-primary mb-3" href="school-subjects/creation" Match="NavLinkMatch.All">
Create
</NavLink>
</div>
@@ -22,10 +22,10 @@
}
else
{
<ul>
<ul class="list-unstyled">
@foreach (var schoolSubject in schoolSubjects)
{
<li>
<li class="border text-center p-3">
<SchoolSubjectDisplay Id="@schoolSubject.Id" />
<div class="btn-group">
<SchoolSubjectDeletion Id="@schoolSubject.Id" OnDeleted="HandleDeletion" />

View File

@@ -8,5 +8,7 @@ namespace WorkManagementTool.Data
{
public DbSet<Homework> Homeworks { get; set; } = null!;
public DbSet<SchoolSubject> SchoolSubjects { get; set; } = null!;
public DbSet<Project> Projects { get; set; } = null!;
public DbSet<ProjectTask> ProjectTasks { get; set; } = null!;
}
}

View File

@@ -0,0 +1,11 @@
namespace WorkManagementTool.Data.Entities
{
public class Project
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public List<ProjectTask> Tasks { get; set; } = new();
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
}

View File

@@ -0,0 +1,14 @@
namespace WorkManagementTool.Data.Entities
{
public class ProjectTask
{
public int Id { get; set; }
public string Title { get; set; } = null!;
public string? Description { get; set; }
public DateTime DueDate { get; set; }
public bool IsCompleted { get; set; }
public List<ProjectTask>? NextTasks { get; set; }
public Project Project { get; set; } = null!;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
}

View File

@@ -0,0 +1,30 @@
# Consultez https://aka.ms/customizecontainer pour savoir comment personnaliser votre conteneur de débogage et comment Visual Studio utilise ce Dockerfile pour générer vos images afin daccélérer le débogage.
# Cet index est utilisé lors de lexécution à partir de VS en mode rapide (par défaut pour la configuration de débogage)
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
# Cette phase est utilisée pour générer le projet de service
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["WorkManagementTool/WorkManagementTool.csproj", "WorkManagementTool/"]
RUN dotnet restore "./WorkManagementTool/WorkManagementTool.csproj"
COPY . .
WORKDIR "/src/WorkManagementTool"
RUN dotnet build "./WorkManagementTool.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Cette étape permet de publier le projet de service à copier dans la phase finale
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./WorkManagementTool.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Cette phase est utilisée en production ou lors de lexécution à partir de VS en mode normal (par défaut quand la configuration de débogage nest pas utilisée)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WorkManagementTool.dll"]

View File

@@ -0,0 +1,522 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorkManagementTool.Data;
#nullable disable
namespace WorkManagementTool.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20251227164404_AddProjectAndProjectTask")]
partial class AddProjectAndProjectTask
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey<string>", b =>
{
b.Property<byte[]>("CredentialId")
.HasMaxLength(1024)
.HasColumnType("BLOB");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("CredentialId");
b.HasIndex("UserId");
b.ToTable("AspNetUserPasskeys", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("WorkManagementTool.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Homework", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedById")
.HasColumnType("TEXT");
b.Property<bool>("Deleted")
.HasColumnType("INTEGER");
b.Property<string>("DeliveryMethod")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("SchoolSubjectId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("SchoolSubjectId");
b.ToTable("Homeworks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Projects");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("ProjectId")
.HasColumnType("INTEGER");
b.Property<int?>("ProjectTaskId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProjectId");
b.HasIndex("ProjectTaskId");
b.ToTable("ProjectTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedById")
.HasColumnType("TEXT");
b.Property<bool>("Deleted")
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.ToTable("SchoolSubjects");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("Microsoft.AspNetCore.Identity.IdentityPasskeyData", "Data", b1 =>
{
b1.Property<byte[]>("IdentityUserPasskeyCredentialId");
b1.Property<byte[]>("AttestationObject")
.IsRequired();
b1.Property<byte[]>("ClientDataJson")
.IsRequired();
b1.Property<DateTimeOffset>("CreatedAt");
b1.Property<bool>("IsBackedUp");
b1.Property<bool>("IsBackupEligible");
b1.Property<bool>("IsUserVerified");
b1.Property<string>("Name");
b1.Property<byte[]>("PublicKey")
.IsRequired();
b1.Property<uint>("SignCount");
b1.PrimitiveCollection<string>("Transports");
b1.HasKey("IdentityUserPasskeyCredentialId");
b1.ToTable("AspNetUserPasskeys");
b1.ToJson("Data");
b1.WithOwner()
.HasForeignKey("IdentityUserPasskeyCredentialId");
});
b.Navigation("Data")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Homework", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.HasOne("WorkManagementTool.Data.Entities.SchoolSubject", "SchoolSubject")
.WithMany("Homeworks")
.HasForeignKey("SchoolSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreatedBy");
b.Navigation("SchoolSubject");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.HasOne("WorkManagementTool.Data.Entities.Project", "Project")
.WithMany("Tasks")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.Entities.ProjectTask", null)
.WithMany("NextTasks")
.HasForeignKey("ProjectTaskId");
b.Navigation("Project");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.Navigation("CreatedBy");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Navigation("Tasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Navigation("NextTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Navigation("Homeworks");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,78 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace WorkManagementTool.Migrations
{
/// <inheritdoc />
public partial class AddProjectAndProjectTask : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Projects",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Projects", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ProjectTasks",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Title = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: true),
DueDate = table.Column<DateTime>(type: "TEXT", nullable: false),
IsCompleted = table.Column<bool>(type: "INTEGER", nullable: false),
ProjectId = table.Column<int>(type: "INTEGER", nullable: false),
ProjectTaskId = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ProjectTasks", x => x.Id);
table.ForeignKey(
name: "FK_ProjectTasks_ProjectTasks_ProjectTaskId",
column: x => x.ProjectTaskId,
principalTable: "ProjectTasks",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ProjectTasks_Projects_ProjectId",
column: x => x.ProjectId,
principalTable: "Projects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ProjectTasks_ProjectId",
table: "ProjectTasks",
column: "ProjectId");
migrationBuilder.CreateIndex(
name: "IX_ProjectTasks_ProjectTaskId",
table: "ProjectTasks",
column: "ProjectTaskId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProjectTasks");
migrationBuilder.DropTable(
name: "Projects");
}
}
}

View File

@@ -0,0 +1,528 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using WorkManagementTool.Data;
#nullable disable
namespace WorkManagementTool.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20251227192424_AddCreatedAtOnProjectAndProjectTask")]
partial class AddCreatedAtOnProjectAndProjectTask
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey<string>", b =>
{
b.Property<byte[]>("CredentialId")
.HasMaxLength(1024)
.HasColumnType("BLOB");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("CredentialId");
b.HasIndex("UserId");
b.ToTable("AspNetUserPasskeys", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("WorkManagementTool.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Homework", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedById")
.HasColumnType("TEXT");
b.Property<bool>("Deleted")
.HasColumnType("INTEGER");
b.Property<string>("DeliveryMethod")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("SchoolSubjectId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.HasIndex("SchoolSubjectId");
b.ToTable("Homeworks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Projects");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("ProjectId")
.HasColumnType("INTEGER");
b.Property<int?>("ProjectTaskId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProjectId");
b.HasIndex("ProjectTaskId");
b.ToTable("ProjectTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("CreatedById")
.HasColumnType("TEXT");
b.Property<bool>("Deleted")
.HasColumnType("INTEGER");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatedById");
b.ToTable("SchoolSubjects");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("Microsoft.AspNetCore.Identity.IdentityPasskeyData", "Data", b1 =>
{
b1.Property<byte[]>("IdentityUserPasskeyCredentialId");
b1.Property<byte[]>("AttestationObject")
.IsRequired();
b1.Property<byte[]>("ClientDataJson")
.IsRequired();
b1.Property<DateTimeOffset>("CreatedAt");
b1.Property<bool>("IsBackedUp");
b1.Property<bool>("IsBackupEligible");
b1.Property<bool>("IsUserVerified");
b1.Property<string>("Name");
b1.Property<byte[]>("PublicKey")
.IsRequired();
b1.Property<uint>("SignCount");
b1.PrimitiveCollection<string>("Transports");
b1.HasKey("IdentityUserPasskeyCredentialId");
b1.ToTable("AspNetUserPasskeys");
b1.ToJson("Data");
b1.WithOwner()
.HasForeignKey("IdentityUserPasskeyCredentialId");
});
b.Navigation("Data")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Homework", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.HasOne("WorkManagementTool.Data.Entities.SchoolSubject", "SchoolSubject")
.WithMany("Homeworks")
.HasForeignKey("SchoolSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CreatedBy");
b.Navigation("SchoolSubject");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.HasOne("WorkManagementTool.Data.Entities.Project", "Project")
.WithMany("Tasks")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.Entities.ProjectTask", null)
.WithMany("NextTasks")
.HasForeignKey("ProjectTaskId");
b.Navigation("Project");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
.WithMany()
.HasForeignKey("CreatedById");
b.Navigation("CreatedBy");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Navigation("Tasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Navigation("NextTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Navigation("Homeworks");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace WorkManagementTool.Migrations
{
/// <inheritdoc />
public partial class AddCreatedAtOnProjectAndProjectTask : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "CreatedAt",
table: "ProjectTasks",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "CreatedAt",
table: "Projects",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CreatedAt",
table: "ProjectTasks");
migrationBuilder.DropColumn(
name: "CreatedAt",
table: "Projects");
}
}
}

View File

@@ -276,6 +276,64 @@ namespace WorkManagementTool.Migrations
b.ToTable("Homeworks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Projects");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasColumnType("TEXT");
b.Property<DateTime>("DueDate")
.HasColumnType("TEXT");
b.Property<bool>("IsCompleted")
.HasColumnType("INTEGER");
b.Property<int>("ProjectId")
.HasColumnType("INTEGER");
b.Property<int?>("ProjectTaskId")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProjectId");
b.HasIndex("ProjectTaskId");
b.ToTable("ProjectTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Property<int>("Id")
@@ -297,7 +355,6 @@ namespace WorkManagementTool.Migrations
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT");
b.HasKey("Id");
@@ -414,7 +471,7 @@ namespace WorkManagementTool.Migrations
.HasForeignKey("CreatedById");
b.HasOne("WorkManagementTool.Data.Entities.SchoolSubject", "SchoolSubject")
.WithMany()
.WithMany("Homeworks")
.HasForeignKey("SchoolSubjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@@ -424,6 +481,21 @@ namespace WorkManagementTool.Migrations
b.Navigation("SchoolSubject");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.HasOne("WorkManagementTool.Data.Entities.Project", "Project")
.WithMany("Tasks")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("WorkManagementTool.Data.Entities.ProjectTask", null)
.WithMany("NextTasks")
.HasForeignKey("ProjectTaskId");
b.Navigation("Project");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.HasOne("WorkManagementTool.Data.ApplicationUser", "CreatedBy")
@@ -432,6 +504,21 @@ namespace WorkManagementTool.Migrations
b.Navigation("CreatedBy");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.Project", b =>
{
b.Navigation("Tasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.ProjectTask", b =>
{
b.Navigation("NextTasks");
});
modelBuilder.Entity("WorkManagementTool.Data.Entities.SchoolSubject", b =>
{
b.Navigation("Homeworks");
});
#pragma warning restore 612, 618
}
}

View File

@@ -19,6 +19,8 @@ builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuth
// custom services
builder.Services.AddScoped<SchoolSubjectService>();
builder.Services.AddScoped<HomeworkService>();
builder.Services.AddScoped<ProjectService>();
builder.Services.AddScoped<ProjectTaskService>();
builder.Services.AddAuthentication(options =>
{

View File

@@ -1,23 +1,34 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5244",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5244"
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7148;http://localhost:5244",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7148;http://localhost:5244"
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"environmentVariables": {
"ASPNETCORE_HTTPS_PORTS": "8081",
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": true
}
}
}
}
},
"$schema": "https://json.schemastore.org/launchsettings.json"
}

View File

@@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore;
using WorkManagementTool.Data;
using WorkManagementTool.Data.Entities;
namespace WorkManagementTool.Services
{
public class ProjectService
{
private readonly ApplicationDbContext _context;
public ProjectService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Project>> GetAllProjectsAsync()
{
return await _context.Projects.ToListAsync();
}
public async Task<Project?> GetProjectByIdAsync(int projectId)
{
return await _context.Projects
.Include(p => p.Tasks)
.FirstOrDefaultAsync(p => p.Id == projectId);
}
public async Task AddProjectAsync(Project project)
{
_context.Projects.Add(project);
await _context.SaveChangesAsync();
}
public async Task UpdateProjectAsync(Project project)
{
_context.Projects.Update(project);
await _context.SaveChangesAsync();
}
public async Task DeleteProjectAsync(int projectId)
{
var project = await _context.Projects.FindAsync(projectId);
if (project != null)
{
_context.Projects.Remove(project);
await _context.SaveChangesAsync();
}
}
}
}

View File

@@ -0,0 +1,55 @@
using Microsoft.EntityFrameworkCore;
using WorkManagementTool.Data;
using WorkManagementTool.Data.Entities;
namespace WorkManagementTool.Services
{
public class ProjectTaskService
{
private readonly ApplicationDbContext _context;
public ProjectTaskService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<ProjectTask>> GetAllTasksAsync(int projectId)
{
return await _context.ProjectTasks
.Include(t => t.NextTasks)
.Include(t => t.Project)
.Where(w => w.Project.Id == projectId)
.ToListAsync();
}
public async Task<ProjectTask?> GetTaskByIdAsync(int taskId)
{
return await _context.ProjectTasks
.Include(t => t.NextTasks)
.Include(t => t.Project)
.FirstOrDefaultAsync(t => t.Id == taskId);
}
public async Task AddTaskAsync(ProjectTask task)
{
_context.ProjectTasks.Add(task);
await _context.SaveChangesAsync();
}
public async Task UpdateTaskAsync(ProjectTask task)
{
_context.ProjectTasks.Update(task);
await _context.SaveChangesAsync();
}
public async Task DeleteTaskAsync(int taskId)
{
var task = await _context.ProjectTasks.FindAsync(taskId);
if (task != null)
{
_context.ProjectTasks.Remove(task);
await _context.SaveChangesAsync();
}
}
}
}

View File

@@ -6,6 +6,8 @@
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-WorkManagementTool-90e0550e-5565-441a-8092-c4b9b07d34cf</UserSecretsId>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
<ItemGroup>
@@ -15,6 +17,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
</ItemGroup>
<ItemGroup>

18
docker-compose.dcproj Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk">
<PropertyGroup Label="Globals">
<ProjectVersion>2.1</ProjectVersion>
<DockerTargetOS>Linux</DockerTargetOS>
<ProjectGuid>81dded9d-158b-e303-5f62-77a2896d2a5a</ProjectGuid>
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}</DockerServiceUrl>
<DockerServiceName>workmanagementtool</DockerServiceName>
</PropertyGroup>
<ItemGroup>
<None Include="docker-compose.override.yml">
<DependentUpon>docker-compose.yml</DependentUpon>
</None>
<None Include="docker-compose.yml" />
<None Include=".dockerignore" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
services:
workmanagementtool:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_HTTP_PORTS=8080
- ASPNETCORE_HTTPS_PORTS=8081
ports:
- "8080"
- "8081"
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
- ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

6
docker-compose.yml Normal file
View File

@@ -0,0 +1,6 @@
services:
workmanagementtool:
image: ${DOCKER_REGISTRY-}workmanagementtool
build:
context: .
dockerfile: WorkManagementTool/Dockerfile

11
launchSettings.json Normal file
View File

@@ -0,0 +1,11 @@
{
"profiles": {
"Docker Compose": {
"commandName": "DockerCompose",
"commandVersion": "1.0",
"serviceActions": {
"workmanagementtool": "StartDebugging"
}
}
}
}