Add 'Modified' tracking for Prospect entity

Introduce a 'Modified' field to track the last modification date of prospects. Automatically update this field on changes and integrate it in UI display for better transparency.
This commit is contained in:
Andre Beging
2025-03-31 08:21:23 +02:00
parent c2de397a0f
commit 5eb8b4b377
8 changed files with 280 additions and 6 deletions

View File

@@ -42,6 +42,11 @@ namespace FoodsharingSiegen.Contracts.Entity
/// </summary>
public string? Memo { get; set; }
/// <summary>
/// Gets or sets the value indicating the last modification date and time.
/// </summary>
public DateTime? Modified { get; set; }
/// <summary>
/// Gets or sets the value of the name (ab)
/// </summary>

View File

@@ -45,7 +45,7 @@
</Alert>
}
<table style="width: 100%;">
<table class="flex-column" style="width: 100%;">
<InteractionRow
Prospect="Prospect"
@@ -149,5 +149,7 @@
</table>
<div class="flex-grow-1"></div>
<small class="text-center" style="margin-top: .5rem;">Zuletzt geändert: @Prospect?.Modified?.ToLocalTime()</small>
</div>

View File

@@ -72,7 +72,7 @@ namespace FoodsharingSiegen.Server.Controls
private async Task EditProspectAsync()
{
await EditProspectDialog.ShowAsync(ModalService, () => InvokeAsync(StateHasChanged), Prospect);
await EditProspectDialog.ShowAsync(ModalService, OnDataChanged ?? (async () => await Task.CompletedTask), Prospect);
}
#endregion

View File

@@ -14,13 +14,15 @@
}
.pc-main {
display: flex;
flex-direction: column;
flex-basis: 0;
flex-grow: 1;
max-width: 480px;
border: 1px solid #533a20;
border-radius: 3px;
margin: 5px;
padding: 16px;
padding: .5rem .5rem 0 .5rem;
}
.pc-main.warning {

View File

@@ -44,6 +44,7 @@ namespace FoodsharingSiegen.Server.Data.Service
interaction.Created = DateTime.UtcNow;
targetProspect.Interactions.Add(interaction);
targetProspect.Modified = DateTime.UtcNow;
await Context.SaveChangesAsync();
@@ -77,6 +78,7 @@ namespace FoodsharingSiegen.Server.Data.Service
if (prospect == null) return new(new Exception("Cannot be empty"));
prospect.Created = DateTime.UtcNow;
prospect.Modified = DateTime.UtcNow;
prospect.Id = Guid.Empty;
await Context.Prospects!.AddAsync(prospect);
@@ -142,9 +144,20 @@ namespace FoodsharingSiegen.Server.Data.Service
{
try
{
Context.Interactions!.Remove(new() { Id = interactionId });
var interaction = await Context.Interactions!.AsNoTracking().FirstOrDefaultAsync(x => x.Id == interactionId);
if(interaction == null) return new(new Exception("Interaction not found"));
Context.Interactions!.Remove(new() { Id = interaction.Id });
await Context.SaveChangesAsync();
// Update prospect modified date
var prospect = await Context.Prospects!.FirstOrDefaultAsync(x => x.Id == interaction.ProspectID);
if (prospect != null)
{
prospect.Modified = DateTime.UtcNow;
await Context.SaveChangesAsync();
}
await AuditService.Insert(AuditType.RemoveInteraction, "?");
return new();
@@ -176,6 +189,7 @@ namespace FoodsharingSiegen.Server.Data.Service
entityProspect.FsId = prospect.FsId;
entityProspect.Warning = prospect.Warning;
entityProspect.RecordState = prospect.RecordState;
entityProspect.Modified = DateTime.UtcNow;
var saveR = await Context.SaveChangesAsync();

View File

@@ -0,0 +1,208 @@
// <auto-generated />
using System;
using FoodsharingSiegen.Server.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace FoodsharingSiegen.Server.Migrations
{
[DbContext(typeof(FsContext))]
[Migration("20250330182300_Proposal-Modified")]
partial class ProposalModified
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.3");
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Audit", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("Data1")
.HasColumnType("TEXT");
b.Property<string>("Data2")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<Guid?>("UserID")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserID");
b.ToTable("Audits");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Interaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<bool>("Alert")
.HasColumnType("INTEGER");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<DateTime>("Date")
.HasColumnType("TEXT");
b.Property<string>("Info")
.HasColumnType("TEXT");
b.Property<bool>("NotNeeded")
.HasColumnType("INTEGER");
b.Property<Guid>("ProspectID")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<Guid>("UserID")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ProspectID");
b.HasIndex("UserID");
b.ToTable("Interactions");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Prospect", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<int>("FsId")
.HasColumnType("INTEGER");
b.Property<string>("Memo")
.HasColumnType("TEXT");
b.Property<DateTime?>("Modified")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("RecordState")
.HasColumnType("INTEGER");
b.Property<bool>("Warning")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Prospects");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("Created")
.HasColumnType("TEXT");
b.Property<string>("EncryptedPassword")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("ForceLogout")
.HasColumnType("INTEGER");
b.Property<string>("Groups")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Mail")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Memo")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Network")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<bool>("Verified")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Audit", b =>
{
b.HasOne("FoodsharingSiegen.Contracts.Entity.User", "User")
.WithMany()
.HasForeignKey("UserID");
b.Navigation("User");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Interaction", b =>
{
b.HasOne("FoodsharingSiegen.Contracts.Entity.Prospect", "Prospect")
.WithMany("Interactions")
.HasForeignKey("ProspectID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("FoodsharingSiegen.Contracts.Entity.User", "User")
.WithMany("Interactions")
.HasForeignKey("UserID")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Prospect");
b.Navigation("User");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.Prospect", b =>
{
b.Navigation("Interactions");
});
modelBuilder.Entity("FoodsharingSiegen.Contracts.Entity.User", b =>
{
b.Navigation("Interactions");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FoodsharingSiegen.Server.Migrations
{
/// <inheritdoc />
public partial class ProposalModified : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "Modified",
table: "Prospects",
type: "TEXT",
nullable: true);
// Fill Modified column of existing rows
migrationBuilder.Sql(@"
UPDATE Prospects
SET Modified = COALESCE(
(SELECT MAX(Created)
FROM Interactions
WHERE Interactions.ProspectID = Prospects.Id),
Prospects.Created
);
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Modified",
table: "Prospects");
}
}
}

View File

@@ -99,6 +99,9 @@ namespace FoodsharingSiegen.Server.Migrations
b.Property<string>("Memo")
.HasColumnType("TEXT");
b.Property<DateTime?>("Modified")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");