diff --git a/Components/Layout/NavMenu.razor b/Components/Layout/NavMenu.razor
index 4cefe6d..43782ea 100644
--- a/Components/Layout/NavMenu.razor
+++ b/Components/Layout/NavMenu.razor
@@ -38,6 +38,14 @@ protected override void OnInitialized()
Add
+ @if (nm.Uri.Contains("admin="))
+ {
+
+
+ Finanzen
+
+
+ }
@@ -53,4 +61,9 @@ protected override void OnInitialized()
{
return _prefix + "/add" + _suffix;
}
+
+ private string FinanceLink()
+ {
+ return _prefix + "/finance" + _suffix;
+ }
}
diff --git a/Components/Layout/NavMenu.razor.css b/Components/Layout/NavMenu.razor.css
index 4e15395..b8d8234 100644
--- a/Components/Layout/NavMenu.razor.css
+++ b/Components/Layout/NavMenu.razor.css
@@ -42,8 +42,8 @@
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
-.bi-list-nested-nav-menu {
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
+.bi-bank-nav-menu {
+ background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22white%22%20class%3D%22bi%20bi-bank%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%20%20%3Cpath%20d%3D%22m8%200%206.61%203h.89a.5.5%200%200%201%20.5.5v2a.5.5%200%200%201-.5.5H15v7a.5.5%200%200%201%20.485.38l.5%202a.498.498%200%200%201-.485.62H.5a.498.498%200%200%201-.485-.62l.5-2A.5.5%200%200%201%201%2013V6H.5a.5.5%200%200%201-.5-.5v-2A.5.5%200%200%201%20.5%203h.89zM3.777%203h8.447L8%201zM2%206v7h1V6zm2%200v7h2.5V6zm3.5%200v7h1V6zm2%200v7H12V6zM13%206v7h1V6zm2-1V4H1v1zm-.39%209H1.39l-.25%201h13.72z%22%2F%3E%0A%3C%2Fsvg%3E");
}
.nav-item {
diff --git a/Components/Pages/GroupAdd.razor b/Components/Pages/GroupAdd.razor
index 1d6ca0e..e12866a 100644
--- a/Components/Pages/GroupAdd.razor
+++ b/Components/Pages/GroupAdd.razor
@@ -80,6 +80,7 @@
return;
}
+ Order!.PaymentStatus = PaymentStatus.Unpaid;
Order!.Group = group;
context.Add(Order!);
context.SaveChanges();
diff --git a/Components/Pages/GroupFinanze.razor b/Components/Pages/GroupFinanze.razor
new file mode 100644
index 0000000..a47b71f
--- /dev/null
+++ b/Components/Pages/GroupFinanze.razor
@@ -0,0 +1,127 @@
+@page "/group/{GroupSlug}/finance"
+
+@using GroupOrder.Data
+@using Microsoft.AspNetCore.WebUtilities
+@using Microsoft.EntityFrameworkCore
+@rendermode InteractiveServer
+
+@inject IDbContextFactory DbFactory
+@inject NavigationManager nm
+
+Mampf.Link @Group?.GroupName Finances
+
+@if (Group != null)
+{
+ Finance Overview: @Group?.GroupName
+
+
+
+
+
+
+ Name
+ |
+
+ Price
+ |
+
+ Payment Status
+ |
+
+
+
+ @foreach (Order order in Group!.Orders)
+ {
+
+
+ @order.Name
+ |
+
+ @order.getPrice()€
+ |
+
+
+
+
+
+
+ |
+
+ }
+
+
+
+
+}
+
+@code {
+
+ private Group? Group { get; set; }
+
+ private bool Loading { get; set; } = false;
+ private bool NotFound { get; set; } = false;
+
+ [Parameter]
+ public string? GroupSlug { get; set; }
+
+ private string? AdminCode { get; set; }
+
+ protected override Task OnInitializedAsync()
+ {
+ _context = DbFactory.CreateDbContext();
+ return base.OnInitializedAsync();
+ }
+
+ protected override async Task OnParametersSetAsync()
+ {
+ await LoadGroupAsync();
+ await base.OnParametersSetAsync();
+ }
+
+ GroupContext? _context;
+
+ // Loads the contact.
+ private async Task LoadGroupAsync()
+ {
+ if (nm.Uri.Contains("?") && QueryHelpers.ParseQuery(nm.Uri.Split("?")[1]).TryGetValue("admin", out var code))
+ {
+ AdminCode = code;
+ }
+ else
+ {
+ NotFound = true;
+ return;
+ }
+
+ if (Loading)
+ {
+ return; //avoid concurrent requests.
+ }
+
+ Group = null;
+ Loading = true;
+
+ if (_context!.Groups is not null)
+ {
+ Group = await _context!.Groups
+ .Include(group => group.Orders)
+ .SingleOrDefaultAsync(
+ c => c.GroupSlug == GroupSlug && c.AdminCode == AdminCode);
+
+ if (Group is null)
+ {
+ NotFound = true;
+ }
+ }
+
+ Loading = false;
+ }
+
+ private async void Save()
+ {
+ if (Group == null) return;
+ if (Group.AdminCode != AdminCode) return; // this theoretically should not happen
+ await _context!.SaveChangesAsync();
+ }
+
+}
diff --git a/Components/Pages/GroupOverview.razor b/Components/Pages/GroupOverview.razor
index f176a49..f27c368 100644
--- a/Components/Pages/GroupOverview.razor
+++ b/Components/Pages/GroupOverview.razor
@@ -42,12 +42,12 @@
@order.Food
- @getPrice(order)€
+ @order.getPrice()€
|
- @if (Group.PaypalUsername != null)
+ @if (!string.IsNullOrEmpty(Group.PaypalUsername))
{
- 💳
+ 💳
}
|
@@ -101,10 +101,5 @@
Loading = false;
}
-
- private String getPrice(Order order)
- {
- return (order.Price / 100) + "." + System.String.Format("{0:D2}", order.Price % 100);
- }
}
diff --git a/Data/Order.cs b/Data/Order.cs
index 435715b..e51b689 100644
--- a/Data/Order.cs
+++ b/Data/Order.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
@@ -20,4 +21,20 @@ public class Order
[Required]
public int? Price { get; set; } // in cent
+
+ [Required]
+ [DefaultValue(PaymentStatus.Unpaid)]
+ public PaymentStatus PaymentStatus { get; set; }
+
+ public String getPrice()
+ {
+ return (this.Price / 100) + "." + System.String.Format("{0:D2}", this.Price % 100);
+ }
+}
+
+public enum PaymentStatus
+{
+ Unpaid,
+ PaymentPending,
+ Paid
}
\ No newline at end of file
diff --git a/Migrations/20240629204340_Finance.Designer.cs b/Migrations/20240629204340_Finance.Designer.cs
new file mode 100644
index 0000000..17d27f5
--- /dev/null
+++ b/Migrations/20240629204340_Finance.Designer.cs
@@ -0,0 +1,111 @@
+//
+using System;
+using GroupOrder.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace GroupOrder.Migrations
+{
+ [DbContext(typeof(GroupContext))]
+ [Migration("20240629204340_Finance")]
+ partial class Finance
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
+
+ modelBuilder.Entity("GroupOrder.Data.Group", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AdminCode")
+ .IsRequired()
+ .HasMaxLength(15)
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("GroupName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("GroupSlug")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PaypalUsername")
+ .HasMaxLength(30)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GroupSlug")
+ .IsUnique();
+
+ b.ToTable("Groups");
+ });
+
+ modelBuilder.Entity("GroupOrder.Data.Order", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Food")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("GroupId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PaymentStatus")
+ .HasColumnType("INTEGER");
+
+ b.Property("Price")
+ .IsRequired()
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GroupId");
+
+ b.ToTable("Orders");
+ });
+
+ modelBuilder.Entity("GroupOrder.Data.Order", b =>
+ {
+ b.HasOne("GroupOrder.Data.Group", "Group")
+ .WithMany("Orders")
+ .HasForeignKey("GroupId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Group");
+ });
+
+ modelBuilder.Entity("GroupOrder.Data.Group", b =>
+ {
+ b.Navigation("Orders");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20240629204340_Finance.cs b/Migrations/20240629204340_Finance.cs
new file mode 100644
index 0000000..d052c62
--- /dev/null
+++ b/Migrations/20240629204340_Finance.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace GroupOrder.Migrations
+{
+ ///
+ public partial class Finance : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "PaymentStatus",
+ table: "Orders",
+ type: "INTEGER",
+ nullable: false,
+ defaultValue: 0);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "PaymentStatus",
+ table: "Orders");
+ }
+ }
+}
diff --git a/Migrations/GroupContextModelSnapshot.cs b/Migrations/GroupContextModelSnapshot.cs
index 92cada9..db1a2e2 100644
--- a/Migrations/GroupContextModelSnapshot.cs
+++ b/Migrations/GroupContextModelSnapshot.cs
@@ -73,6 +73,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasMaxLength(100)
.HasColumnType("TEXT");
+ b.Property("PaymentStatus")
+ .HasColumnType("INTEGER");
+
b.Property("Price")
.IsRequired()
.HasColumnType("INTEGER");
diff --git a/Program.cs b/Program.cs
index ac0d82c..a6f5801 100644
--- a/Program.cs
+++ b/Program.cs
@@ -11,13 +11,13 @@
var app = builder.Build();
-app.UseExceptionHandler("/Error", createScopeForErrors: true);
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
+ app.UseExceptionHandler("/Error", createScopeForErrors: true);
}
app.UseHttpsRedirection();