diff --git a/.gitignore b/.gitignore index fd441c5..654072c 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,7 @@ yarn-error.log* *.sln.docstates # JetBrains IDEs -.idea/ \ No newline at end of file +.idea/ + +# Claude Code +.claude \ No newline at end of file diff --git a/.vs/JudoWeb/DesignTimeBuild/.dtbcache.v2 b/.vs/JudoWeb/DesignTimeBuild/.dtbcache.v2 index 0246481..803c5ea 100644 Binary files a/.vs/JudoWeb/DesignTimeBuild/.dtbcache.v2 and b/.vs/JudoWeb/DesignTimeBuild/.dtbcache.v2 differ diff --git a/.vs/JudoWeb/FileContentIndex/8c05cf6d-77be-490d-9450-d64de0268f4b.vsidx b/.vs/JudoWeb/FileContentIndex/8c05cf6d-77be-490d-9450-d64de0268f4b.vsidx deleted file mode 100644 index 70aef67..0000000 Binary files a/.vs/JudoWeb/FileContentIndex/8c05cf6d-77be-490d-9450-d64de0268f4b.vsidx and /dev/null differ diff --git a/API/API.csproj b/API/API.csproj index 0c56541..93fcf8a 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -7,11 +7,29 @@ + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - + diff --git a/API/API.csproj.Backup (1).tmp b/API/API.csproj.Backup (1).tmp new file mode 100644 index 0000000..e071122 --- /dev/null +++ b/API/API.csproj.Backup (1).tmp @@ -0,0 +1,32 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/API/API.csproj.Backup.tmp b/API/API.csproj.Backup.tmp new file mode 100644 index 0000000..9d39161 --- /dev/null +++ b/API/API.csproj.Backup.tmp @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/API/Controllers/AgeGroupController.cs b/API/Controllers/AgeGroupController.cs new file mode 100644 index 0000000..89267d1 --- /dev/null +++ b/API/Controllers/AgeGroupController.cs @@ -0,0 +1,73 @@ +using API.Models.Ingoing.Altersgruppen; +using API.Repository.AgeGroup; +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers +{ + [ApiController] + [Route("api/ageGroups/")] + public class AgeGroupController : ControllerBase + { + private IAgeGroupService _ageGroupService; + + public AgeGroupController(IAgeGroupService ageGroupService) + { + _ageGroupService = ageGroupService; + } + + [HttpGet()] + public async Task GetAll() + { + var allAgeGroups = await _ageGroupService.GetAllAsync(); + + return Ok(allAgeGroups); + } + + [HttpGet("{id}")] + public async Task GetOne([FromRoute] int id) + { + var group = await _ageGroupService.GetAsync(id); + + if (group == null) + { + return NotFound(); + } + + return Ok(group); + } + + [HttpPost()] + public async Task Create([FromBody] AltersGruppeIngoing groupDto) + { + var group = await _ageGroupService.CreateAsync(groupDto.ToInternalFromIngoing()); + + return CreatedAtAction(nameof(GetOne), new { Id = group.Id }, group); + } + + [HttpPut("{id}")] + public async Task Update([FromRoute] int id, [FromBody] AltersGruppeIngoing groupDto) + { + var group = await _ageGroupService.UpdateAsync(id, groupDto.ToInternalFromIngoing()); + + if(group == null) + { + return NotFound(); + } + + return Ok(group); + } + + [HttpDelete("{id}")] + public async Task Delete([FromRoute] int Id) + { + var group = await _ageGroupService.DeleteAsync(Id); + + if (group == null) + { + return NotFound(); + } + + return NoContent(); + } + } +} diff --git a/API/Database/ApplicationDbContext.cs b/API/Database/ApplicationDbContext.cs new file mode 100644 index 0000000..1a9f69b --- /dev/null +++ b/API/Database/ApplicationDbContext.cs @@ -0,0 +1,14 @@ +using API.Models.Internal.Altersgruppen; +using Microsoft.EntityFrameworkCore; + +namespace API.Database +{ + public class ApplicationDbContext : DbContext + { + public ApplicationDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet Altersgruppen { get; set; } + } +} diff --git a/API/Migrations/20251206113128_InitialCreate.Designer.cs b/API/Migrations/20251206113128_InitialCreate.Designer.cs new file mode 100644 index 0000000..8220cb4 --- /dev/null +++ b/API/Migrations/20251206113128_InitialCreate.Designer.cs @@ -0,0 +1,47 @@ +// +using API.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251206113128_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); + + modelBuilder.Entity("API.Models.Internal.Altersgruppen.Altergruppe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndingAge") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartingAge") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Altersgruppen"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/API/Migrations/20251206113128_InitialCreate.cs b/API/Migrations/20251206113128_InitialCreate.cs new file mode 100644 index 0000000..fa435ab --- /dev/null +++ b/API/Migrations/20251206113128_InitialCreate.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Altersgruppen", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + StartingAge = table.Column(type: "TEXT", nullable: false), + EndingAge = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Altersgruppen", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Altersgruppen"); + } + } +} diff --git a/API/Migrations/20251206220303_ChangeAgeFieldsToInt.Designer.cs b/API/Migrations/20251206220303_ChangeAgeFieldsToInt.Designer.cs new file mode 100644 index 0000000..1997ead --- /dev/null +++ b/API/Migrations/20251206220303_ChangeAgeFieldsToInt.Designer.cs @@ -0,0 +1,45 @@ +// +using API.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251206220303_ChangeAgeFieldsToInt")] + partial class ChangeAgeFieldsToInt + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); + + modelBuilder.Entity("API.Models.Internal.Altersgruppen.AltersGruppe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndingAge") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartingAge") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Altersgruppen"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/API/Migrations/20251206220303_ChangeAgeFieldsToInt.cs b/API/Migrations/20251206220303_ChangeAgeFieldsToInt.cs new file mode 100644 index 0000000..fccb536 --- /dev/null +++ b/API/Migrations/20251206220303_ChangeAgeFieldsToInt.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class ChangeAgeFieldsToInt : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "StartingAge", + table: "Altersgruppen", + type: "INTEGER", + nullable: false, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "EndingAge", + table: "Altersgruppen", + type: "INTEGER", + nullable: false, + oldClrType: typeof(string), + oldType: "TEXT"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "StartingAge", + table: "Altersgruppen", + type: "TEXT", + nullable: false, + oldClrType: typeof(int), + oldType: "INTEGER"); + + migrationBuilder.AlterColumn( + name: "EndingAge", + table: "Altersgruppen", + type: "TEXT", + nullable: false, + oldClrType: typeof(int), + oldType: "INTEGER"); + } + } +} diff --git a/API/Migrations/ApplicationDbContextModelSnapshot.cs b/API/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000..4e1d4ce --- /dev/null +++ b/API/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,42 @@ +// +using API.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); + + modelBuilder.Entity("API.Models.Internal.Altersgruppen.AltersGruppe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EndingAge") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartingAge") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Altersgruppen"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/API/Models/Ingoing/Altersgruppen/AltersGruppeIngoing.cs b/API/Models/Ingoing/Altersgruppen/AltersGruppeIngoing.cs new file mode 100644 index 0000000..d382c45 --- /dev/null +++ b/API/Models/Ingoing/Altersgruppen/AltersGruppeIngoing.cs @@ -0,0 +1,28 @@ +using API.Models.Internal.Altersgruppen; +using System.ComponentModel.DataAnnotations; + +namespace API.Models.Ingoing.Altersgruppen +{ + public class AltersGruppeIngoing + { + [Required] + public string Name { get; set; } + [Required] + public int StartingAge { get; set; } + [Required] + public int EndingAge { get; set; } + } + + public static class AltersgruppeMapper + { + public static AltersGruppe ToInternalFromIngoing(this AltersGruppeIngoing group) + { + return new AltersGruppe + { + Name = group.Name, + StartingAge = group.StartingAge, + EndingAge = group.EndingAge, + }; + } + } +} diff --git a/API/Models/Internal/Altersgruppen/AltersGruppe.cs b/API/Models/Internal/Altersgruppen/AltersGruppe.cs new file mode 100644 index 0000000..5914805 --- /dev/null +++ b/API/Models/Internal/Altersgruppen/AltersGruppe.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace API.Models.Internal.Altersgruppen +{ + public class AltersGruppe + { + public int Id { get; set; } + public string Name { get; set; } + public int StartingAge { get; set; } + public int EndingAge { get; set; } + } +} diff --git a/API/Program.cs b/API/Program.cs index 48863a6..3a9fcab 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -1,12 +1,29 @@ +using API.Database; +using API.Repository.AgeGroup; +using Microsoft.EntityFrameworkCore; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. - builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +// Database +var postgreConnection = builder.Configuration.GetConnectionString("PostgresConnection"); +if (!string.IsNullOrEmpty(postgreConnection)) +{ + // Nutze PostgresSQL + builder.Services.AddDbContext(options => options.UseNpgsql(postgreConnection)); +} +else +{ + builder.Services.AddDbContext(options => options.UseSqlite("Data Source=app.db")); +} + + +builder.Services.AddScoped(); + +// Add Database Services var app = builder.Build(); // Configure the HTTP request pipeline. @@ -16,6 +33,12 @@ if (app.Environment.IsDevelopment()) app.UseSwaggerUI(); } +using(var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Database.Migrate(); +} + app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/API/Repository/AgeGroup/AgeGroupService.cs b/API/Repository/AgeGroup/AgeGroupService.cs new file mode 100644 index 0000000..5bdee6d --- /dev/null +++ b/API/Repository/AgeGroup/AgeGroupService.cs @@ -0,0 +1,68 @@ +using API.Database; +using API.Models.Internal.Altersgruppen; +using Microsoft.EntityFrameworkCore; + +namespace API.Repository.AgeGroup +{ + public class AgeGroupService : IAgeGroupService + { + private ApplicationDbContext _context; + + public AgeGroupService(ApplicationDbContext context) + { + _context = context; + } + + public async Task CreateAsync(AltersGruppe altersGruppe) + { + await _context.Altersgruppen.AddAsync(altersGruppe); + await _context.SaveChangesAsync(); + return altersGruppe; + } + + public async Task DeleteAsync(int id) + { + var group = await _context.Altersgruppen.FirstOrDefaultAsync(x => x.Id == id); + + if (group == null) + { + return null; + } + + _context.Altersgruppen.Remove(group); + _context.SaveChanges(); + + return group; + } + + public async Task> GetAllAsync() + { + var allGroups = await _context.Altersgruppen.ToListAsync(); + + return allGroups; + } + + public async Task GetAsync(int id) + { + return await _context.Altersgruppen.FindAsync(id); + } + + public async Task UpdateAsync(int id, AltersGruppe altersGruppe) + { + var existingGroup = await _context.Altersgruppen.FirstOrDefaultAsync(x => x.Id == id); + + if (existingGroup == null) + { + return null; + } + + existingGroup.Name = altersGruppe.Name; + existingGroup.StartingAge = altersGruppe.StartingAge; + existingGroup.EndingAge = altersGruppe.EndingAge; + + await _context.SaveChangesAsync(); + + return existingGroup; + } + } +} diff --git a/API/Repository/AgeGroup/IAgeGroupService.cs b/API/Repository/AgeGroup/IAgeGroupService.cs new file mode 100644 index 0000000..a350be0 --- /dev/null +++ b/API/Repository/AgeGroup/IAgeGroupService.cs @@ -0,0 +1,14 @@ +using API.Models.Internal.Altersgruppen; + +namespace API.Repository.AgeGroup +{ + public interface IAgeGroupService + { + public Task> GetAllAsync(); + + public Task GetAsync(int id); + public Task CreateAsync(AltersGruppe altersGruppe); + public Task DeleteAsync(int id); + public Task UpdateAsync(int id, AltersGruppe altersGruppe); + } +} diff --git a/API/app.db-shm b/API/app.db-shm new file mode 100644 index 0000000..97f3b41 Binary files /dev/null and b/API/app.db-shm differ diff --git a/API/app.db-wal b/API/app.db-wal new file mode 100644 index 0000000..25974fc Binary files /dev/null and b/API/app.db-wal differ diff --git a/GUI/src/components/ResponsiveTabsOrList.vue b/GUI/src/components/ResponsiveTabsOrList.vue new file mode 100644 index 0000000..0627541 --- /dev/null +++ b/GUI/src/components/ResponsiveTabsOrList.vue @@ -0,0 +1,43 @@ + + + diff --git a/GUI/src/routes/Home.vue b/GUI/src/routes/Home.vue index ea2e05c..cad6ce7 100644 --- a/GUI/src/routes/Home.vue +++ b/GUI/src/routes/Home.vue @@ -2,9 +2,13 @@ import CarouselItemWithTitle from '@/components/CarouselItemWithTitle.vue'; import HomeEntrie from '@/components/HomeEntrie.vue'; import HomeEntrieWithImagePreset from '@/components/HomeEntrieWithImagePreset.vue'; -import { ref } from 'vue'; +import ResponsiveTabsOrList from '@/components/ResponsiveTabsOrList.vue'; -const tab = ref("1"); +const tabItems = [ + { value: '1', label: 'Nikolausturnier' }, + { value: '2', label: 'Wettkämpfe' }, + { value: '3', label: 'Kinoabende' }, +];