Commit: a8946a3
Parent: b30f464

Add notification offset to participants

Mårten Åsberg committed on 2026-05-04 at 20:36
MatDenDagen/Components/Pages/Admin/Participants.razor +86 -13
diff --git a/MatDenDagen/Components/Pages/Admin/Participants.razor b/MatDenDagen/Components/Pages/Admin/Participants.razor
index 8d209bd..d58dc13 100644
@@ -10,11 +10,37 @@
<ul>
@foreach (var participant in questionnaireContext.Participants)
{
var updateFormName = "UpdateParticipant-" + participant.Id;
var removeFormName = "RemoveParticipant-" + participant.Id;
<li>
<span>@participant.Name (@participant.PhoneNumber)</span>
<EditForm FormName="@($"RemoveParticipant-{participant.Id}")" Model="@removeParticipantModel" OnSubmit="@RemoveParticipant"
<EditForm FormName="@updateFormName" Model="@updateParticipantModel" OnSubmit="@UpdateParticipant"
Enhance>
<input type="hidden" name="removeParticipantModel.ParticipantId" value="@participant.Id" />
<input type="hidden" name="updateParticipantModel.ParticipantId" value="@participant.Id.ToString()" />
<p>
<label>
<span>Namn:</span>
<input type="text" name="updateParticipantModel.Name" value="@participant.Name" required />
</label>
</p>
<p>
<label>
<span>Telefonnummer:</span>
<input type="text" name="updateParticipantModel.PhoneNumber" value="@participant.PhoneNumber" required />
</label>
</p>
<p>
<label>
<span>Notifikationsoffset (minuter från midnatt):</span>
<input type="number" name="updateParticipantModel.NotificationMinutesOffset" value="@participant.NotificationMinutesOffset" />
</label>
</p>
<p>
<input type="submit" value="Uppdatera" />
</p>
</EditForm>
<EditForm FormName="@removeFormName" Model="@removeParticipantModel" OnSubmit="@RemoveParticipant"
Enhance>
<input type="hidden" name="removeParticipantModel.ParticipantId" value="@participant.Id.ToString()" />
<input type="submit" value="Ta bort" />
</EditForm>
</li>
@@ -30,15 +56,21 @@
<input type="text" name="addParticipantModel.Name" required />
</label>
</p>
<p>
<label>
<span>Telefonnummer:</span>
<input type="text" name="addParticipantModel.PhoneNumber" required />
</label>
</p>
<p>
<input type="submit" value="Lägg till" />
</p>
<p>
<label>
<span>Telefonnummer:</span>
<input type="text" name="addParticipantModel.PhoneNumber" required />
</label>
</p>
<p>
<label>
<span>Notifikationsoffset (minuter från midnatt):</span>
<input type="number" name="addParticipantModel.NotificationMinutesOffset" />
</label>
</p>
<p>
<input type="submit" value="Lägg till" />
</p>
</EditForm>
@code {
@@ -48,10 +80,14 @@
[SupplyParameterFromForm]
private RemoveParticipantModel? removeParticipantModel { get; set; }
[SupplyParameterFromForm]
private UpdateParticipantModel? updateParticipantModel { get; set; }
protected override void OnInitialized()
{
addParticipantModel ??= new();
removeParticipantModel ??= new();
updateParticipantModel ??= new();
}
private async Task AddParticipant()
@@ -60,11 +96,39 @@
{
return;
}
var participant = new Participant { Id = Guid.CreateVersion7(timeProvider.GetUtcNow()), Name = name, PhoneNumber = phoneNumber };
var participant = new Participant {
Id = Guid.CreateVersion7(timeProvider.GetUtcNow()),
Name = name,
PhoneNumber = phoneNumber,
NotificationMinutesOffset = addParticipantModel?.NotificationMinutesOffset
};
questionnaireContext.Participants.Add(participant);
await questionnaireContext.SaveChangesAsync();
}
private async Task UpdateParticipant()
{
if (updateParticipantModel?.ParticipantId is not string participantId || !Guid.TryParse(participantId, out var id))
{
return;
}
var participant = questionnaireContext.Participants.SingleOrDefault(p => p.Id == id);
if (participant is null)
{
return;
}
if (updateParticipantModel.Name is string name)
{
participant.Name = name;
}
if (updateParticipantModel.PhoneNumber is string phoneNumber)
{
participant.PhoneNumber = phoneNumber;
}
participant.NotificationMinutesOffset = updateParticipantModel.NotificationMinutesOffset;
await questionnaireContext.SaveChangesAsync();
}
private async Task RemoveParticipant()
{
if (removeParticipantModel?.ParticipantId is not string participantId || !Guid.TryParse(participantId, out var id))
@@ -84,10 +148,19 @@
{
public string? Name { get; set; }
public string? PhoneNumber { get; set; }
public int? NotificationMinutesOffset { get; set; }
}
private sealed class RemoveParticipantModel
{
public string? ParticipantId { get; set; }
}
private sealed class UpdateParticipantModel
{
public string? ParticipantId { get; set; }
public string? Name { get; set; }
public string? PhoneNumber { get; set; }
public int? NotificationMinutesOffset { get; set; }
}
}
MatDenDagen/Infrastructure/Storage/Database/Migrations/20260504182253_AddNotificationMinutesOffsetToParticipant.Designer.cs +175 -0
diff --git a/MatDenDagen/Infrastructure/Storage/Database/Migrations/20260504182253_AddNotificationMinutesOffsetToParticipant.Designer.cs b/MatDenDagen/Infrastructure/Storage/Database/Migrations/20260504182253_AddNotificationMinutesOffsetToParticipant.Designer.cs
new file mode 100644
index 0000000..5742d7e
@@ -0,0 +1,175 @@
// <auto-generated />
using System;
using MatDenDagen.Infrastructure.Storage.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace MatDenDagen.Infrastructure.Storage.Database.Migrations
{
[DbContext(typeof(QuestionnaireContext))]
[Migration("20260504182253_AddNotificationMinutesOffsetToParticipant")]
partial class AddNotificationMinutesOffsetToParticipant
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
modelBuilder.Entity("MatDenDagen.Models.Answer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("QuestionId")
.HasColumnType("TEXT");
b.Property<Guid?>("SubmissionId")
.HasColumnType("TEXT");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.HasIndex("SubmissionId");
b.ToTable("Answer");
});
modelBuilder.Entity("MatDenDagen.Models.Config", b =>
{
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("Configs");
});
modelBuilder.Entity("MatDenDagen.Models.Participant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("NotificationMinutesOffset")
.HasColumnType("INTEGER");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PhoneNumber")
.IsUnique();
b.ToTable("Participants");
});
modelBuilder.Entity("MatDenDagen.Models.Question", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Questions");
});
modelBuilder.Entity("MatDenDagen.Models.Submission", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("Participant")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Participant");
b.ToTable("Submissions");
});
modelBuilder.Entity("MatDenDagen.Models.Upload", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid?>("SubmissionId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("SubmissionId");
b.ToTable("Upload");
});
modelBuilder.Entity("MatDenDagen.Models.Answer", b =>
{
b.HasOne("MatDenDagen.Models.Question", null)
.WithMany()
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MatDenDagen.Models.Submission", null)
.WithMany("Answers")
.HasForeignKey("SubmissionId");
});
modelBuilder.Entity("MatDenDagen.Models.Submission", b =>
{
b.HasOne("MatDenDagen.Models.Participant", null)
.WithMany()
.HasForeignKey("Participant")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("MatDenDagen.Models.Upload", b =>
{
b.HasOne("MatDenDagen.Models.Submission", null)
.WithMany("Uploads")
.HasForeignKey("SubmissionId");
});
modelBuilder.Entity("MatDenDagen.Models.Submission", b =>
{
b.Navigation("Answers");
b.Navigation("Uploads");
});
#pragma warning restore 612, 618
}
}
}
MatDenDagen/Infrastructure/Storage/Database/Migrations/20260504182253_AddNotificationMinutesOffsetToParticipant.cs +27 -0
diff --git a/MatDenDagen/Infrastructure/Storage/Database/Migrations/20260504182253_AddNotificationMinutesOffsetToParticipant.cs b/MatDenDagen/Infrastructure/Storage/Database/Migrations/20260504182253_AddNotificationMinutesOffsetToParticipant.cs
new file mode 100644
index 0000000..838fe83
@@ -0,0 +1,27 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MatDenDagen.Infrastructure.Storage.Database.Migrations
{
/// <inheritdoc />
public partial class AddNotificationMinutesOffsetToParticipant : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "NotificationMinutesOffset",
table: "Participants",
type: "INTEGER",
nullable: true
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(name: "NotificationMinutesOffset", table: "Participants");
}
}
}
MatDenDagen/Infrastructure/Storage/Database/Migrations/QuestionnaireContextModelSnapshot.cs +3 -0
diff --git a/MatDenDagen/Infrastructure/Storage/Database/Migrations/QuestionnaireContextModelSnapshot.cs b/MatDenDagen/Infrastructure/Storage/Database/Migrations/QuestionnaireContextModelSnapshot.cs
index adc9f71..c886c0b 100644
@@ -66,6 +66,9 @@ namespace MatDenDagen.Infrastructure.Storage.Database.Migrations
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("NotificationMinutesOffset")
.HasColumnType("INTEGER");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("TEXT");
MatDenDagen/Models/Participant.cs +3 -2
diff --git a/MatDenDagen/Models/Participant.cs b/MatDenDagen/Models/Participant.cs
index 0ff9ae1..cf38138 100644
@@ -5,6 +5,7 @@ namespace MatDenDagen.Models;
public sealed class Participant
{
public required Guid Id { get; init; }
public required string PhoneNumber { get; init; }
public required string Name { get; init; }
public required string PhoneNumber { get; set; }
public required string Name { get; set; }
public int? NotificationMinutesOffset { get; set; }
}
README.md +1 -1
diff --git a/README.md b/README.md
index 1aa7876..10438a2 100644
@@ -13,7 +13,7 @@
- [x] (Re)roll date of **the day**
- [x] Configure participants
- [x] Phone number for notifications
- [ ] Time of day the want to be notified
- [x] Time of day the want to be notified
- [x] Configure questions
- [ ] Export answers
- [ ] Internals