Add EventOccurrenceParserService and update service registrations
Registered the new EventOccurrenceParserService in Program.cs to handle event occurrence parsing. Updated the _Imports.razor file to reflect the renaming of the EventCalendar component to Calendar. Removed the obsolete EventCalendar component to streamline the codebase.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
using Core.Entities;
|
||||
|
||||
namespace Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Result of parsing event occurrence text data.
|
||||
/// Contains parsed occurrences, errors, and warnings.
|
||||
/// </summary>
|
||||
public class EventOccurrenceParseResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary of parsed event occurrences, keyed by EventDefinition.
|
||||
/// For special events (GeneralSchedule/VotingDelegates), the EventDefinition key will be the static instance.
|
||||
/// </summary>
|
||||
public IDictionary<EventDefinition, List<EventOccurrence>> Occurrences { get; set; } = new Dictionary<EventDefinition, List<EventOccurrence>>();
|
||||
|
||||
/// <summary>
|
||||
/// List of parsing errors (critical issues that prevented parsing).
|
||||
/// </summary>
|
||||
public List<string> Errors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of parsing warnings (non-critical issues that occurred during parsing).
|
||||
/// </summary>
|
||||
public List<string> Warnings { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Total number of event occurrences successfully parsed.
|
||||
/// </summary>
|
||||
public int TotalParsed => Occurrences.Values.Sum(list => list.Count);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether parsing was successful (no errors).
|
||||
/// </summary>
|
||||
public bool IsSuccess => Errors.Count == 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
using System.Text;
|
||||
using Core.Entities;
|
||||
using Core.Models;
|
||||
using Core.Parsers;
|
||||
|
||||
namespace Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service implementation for parsing event occurrence text data.
|
||||
/// Wraps EventOccurrenceParser to support text input and error collection.
|
||||
/// </summary>
|
||||
public class EventOccurrenceParserService : IEventOccurrenceParserService
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public EventOccurrenceParseResult ParseFromText(string text, ICollection<EventDefinition> events)
|
||||
{
|
||||
var result = new EventOccurrenceParseResult();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
result.Errors.Add("Input text is empty or whitespace.");
|
||||
return result;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temporary file from the text content
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
File.WriteAllText(tempFile, text, Encoding.UTF8);
|
||||
var fileInfo = new FileInfo(tempFile);
|
||||
|
||||
// Use the existing EventOccurrenceParser
|
||||
var parser = new EventOccurrenceParser(fileInfo, events);
|
||||
var parsedOccurrences = parser.Parse();
|
||||
|
||||
// Convert parsed occurrences to result format, handling special event types
|
||||
foreach (var kvp in parsedOccurrences)
|
||||
{
|
||||
var eventDefinition = kvp.Key;
|
||||
var occurrences = kvp.Value;
|
||||
|
||||
// Check if this is a special event type (GeneralSchedule or VotingDelegates)
|
||||
if (eventDefinition == EventDefinition.GeneralSchedule ||
|
||||
eventDefinition == EventDefinition.VotingDelegates)
|
||||
{
|
||||
// For special events, set EventDefinitionId to null and set SpecialEventType
|
||||
foreach (var occurrence in occurrences)
|
||||
{
|
||||
occurrence.EventDefinitionId = null;
|
||||
occurrence.SpecialEventType = eventDefinition == EventDefinition.GeneralSchedule
|
||||
? "GeneralSchedule"
|
||||
: "VotingDelegates";
|
||||
}
|
||||
|
||||
// Add to result with the special EventDefinition as key
|
||||
result.Occurrences[eventDefinition] = occurrences;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For regular events, set EventDefinitionId and ensure SpecialEventType is null
|
||||
foreach (var occurrence in occurrences)
|
||||
{
|
||||
occurrence.EventDefinitionId = eventDefinition.Id;
|
||||
occurrence.SpecialEventType = null;
|
||||
}
|
||||
|
||||
result.Occurrences[eventDefinition] = occurrences;
|
||||
}
|
||||
}
|
||||
|
||||
// Track any occurrences without a matching EventDefinition
|
||||
// (This would be detected if parser.Parse() returns occurrences with null EventDefinition keys,
|
||||
// but the current parser implementation doesn't do this - all occurrences have a currentEventDefinition)
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Clean up temporary file
|
||||
try
|
||||
{
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Errors.Add($"Error parsing text: {ex.Message}");
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
result.Errors.Add($"Inner exception: {ex.InnerException.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Core.Entities;
|
||||
using Core.Models;
|
||||
|
||||
namespace Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service interface for parsing event occurrence text data.
|
||||
/// </summary>
|
||||
public interface IEventOccurrenceParserService
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses event occurrence text data and returns parse results with occurrences, errors, and warnings.
|
||||
/// </summary>
|
||||
/// <param name="text">The text content to parse (typically multi-line text from paste/import)</param>
|
||||
/// <param name="events">Collection of EventDefinitions to match against during parsing</param>
|
||||
/// <returns>ParseResult containing parsed occurrences, errors, and warnings</returns>
|
||||
EventOccurrenceParseResult ParseFromText(string text, ICollection<EventDefinition> events);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
@page "/calendar/event-occurrences/import"
|
||||
@attribute [Authorize]
|
||||
@using Core.Entities
|
||||
@using Core.Models
|
||||
@using Core.Services
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using MudBlazor
|
||||
@inject IEventOccurrenceParserService ParserService
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageHeader
|
||||
Title="Import Event Occurrences"
|
||||
Description="Parse and import event occurrence data from text"
|
||||
ShowBackButton="true"
|
||||
BackButtonUrl="/calendar/event-occurrences" />
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
<MudText Typo="Typo.h5" Class="mb-4">Paste Event Occurrence Data</MudText>
|
||||
<MudStack Spacing="3">
|
||||
<MudTextField
|
||||
T="string"
|
||||
Label="Event Occurrence Text"
|
||||
@bind-Value="_inputText"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="15"
|
||||
MultiLine="true"
|
||||
Placeholder="Paste event occurrence text here..."
|
||||
HelperText="Paste the event schedule text in the format expected by the parser" />
|
||||
|
||||
<MudStack Row="true" Spacing="2">
|
||||
<MudButton
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Article"
|
||||
OnClick="HandleParse"
|
||||
Disabled="@_isParsing">
|
||||
Parse
|
||||
</MudButton>
|
||||
<MudButton
|
||||
Variant="Variant.Text"
|
||||
OnClick="HandleClear"
|
||||
Disabled="@_isParsing">
|
||||
Clear
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="6">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
<MudText Typo="Typo.h5" Class="mb-4">Parsed Results</MudText>
|
||||
|
||||
@if (_isParsing)
|
||||
{
|
||||
<MudProgressLinear Indeterminate="true" Class="mb-4" />
|
||||
<MudText>Parsing...</MudText>
|
||||
}
|
||||
else if (_parseResult == null)
|
||||
{
|
||||
<MudText Class="mud-text-secondary">Parse text to see results here</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack Spacing="3">
|
||||
@* Errors *@
|
||||
@if (_parseResult.Errors.Any())
|
||||
{
|
||||
@foreach (var error in _parseResult.Errors)
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Dense="true">@error</MudAlert>
|
||||
}
|
||||
}
|
||||
|
||||
@* Warnings *@
|
||||
@if (_parseResult.Warnings.Any())
|
||||
{
|
||||
@foreach (var warning in _parseResult.Warnings)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Dense="true">@warning</MudAlert>
|
||||
}
|
||||
}
|
||||
|
||||
@* Summary *@
|
||||
@if (_parseResult.IsSuccess && _parseResult.TotalParsed > 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Success" Dense="true">
|
||||
Successfully parsed @_parseResult.TotalParsed occurrence(s) from @_parseResult.Occurrences.Count event definition(s)
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@* Parsed Occurrences List *@
|
||||
@if (_parseResult.IsSuccess && _parseResult.Occurrences.Any())
|
||||
{
|
||||
<MudText Typo="Typo.h6" Class="mt-4 mb-2">Occurrences by Event:</MudText>
|
||||
<MudExpansionPanels Elevation="0">
|
||||
@foreach (var kvp in _parseResult.Occurrences.OrderBy(x => GetEventName(x.Key)))
|
||||
{
|
||||
<MudExpansionPanel Text="@GetEventName(kvp.Key)">
|
||||
<MudTable Items="@kvp.Value" Dense="true" Hover="true" Striped="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Date</MudTh>
|
||||
<MudTh>Time</MudTh>
|
||||
<MudTh>Location</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Name">@context.Name</MudTd>
|
||||
<MudTd DataLabel="Date">@context.Date</MudTd>
|
||||
<MudTd DataLabel="Time">@context.Time</MudTd>
|
||||
<MudTd DataLabel="Location">@(context.Location ?? "-")</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
</MudExpansionPanel>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
|
||||
<MudStack Row="true" Spacing="2" Class="mt-4">
|
||||
<MudButton
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Success"
|
||||
StartIcon="@Icons.Material.Filled.Save"
|
||||
OnClick="HandleSaveToDatabase"
|
||||
Disabled="@_isSaving">
|
||||
Save to Database
|
||||
</MudButton>
|
||||
<MudButton
|
||||
Variant="Variant.Text"
|
||||
OnClick="HandleClearResults">
|
||||
Clear Results
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_parseResult.IsSuccess && _parseResult.TotalParsed == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Dense="true">
|
||||
No occurrences were parsed from the text. Please check the format.
|
||||
</MudAlert>
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@code {
|
||||
private string _inputText = string.Empty;
|
||||
private EventOccurrenceParseResult? _parseResult;
|
||||
private bool _isParsing = false;
|
||||
private bool _isSaving = false;
|
||||
|
||||
private async Task HandleParse()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_inputText))
|
||||
{
|
||||
Snackbar.Add("Please enter text to parse", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
_isParsing = true;
|
||||
try
|
||||
{
|
||||
// Get EventDefinitions from database
|
||||
var events = await Context.Events.ToListAsync();
|
||||
|
||||
// Parse the text
|
||||
_parseResult = ParserService.ParseFromText(_inputText, events);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error parsing text: {ex.Message}", Severity.Error);
|
||||
_parseResult = new EventOccurrenceParseResult
|
||||
{
|
||||
Errors = { $"Error: {ex.Message}" }
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isParsing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleClear()
|
||||
{
|
||||
_inputText = string.Empty;
|
||||
_parseResult = null;
|
||||
}
|
||||
|
||||
private void HandleClearResults()
|
||||
{
|
||||
_parseResult = null;
|
||||
}
|
||||
|
||||
private async Task HandleSaveToDatabase()
|
||||
{
|
||||
if (_parseResult == null || !_parseResult.IsSuccess || _parseResult.TotalParsed == 0)
|
||||
{
|
||||
Snackbar.Add("No valid parsed occurrences to save", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
_isSaving = true;
|
||||
try
|
||||
{
|
||||
var savedCount = 0;
|
||||
foreach (var kvp in _parseResult.Occurrences)
|
||||
{
|
||||
foreach (var occurrence in kvp.Value)
|
||||
{
|
||||
// Add each occurrence to the database
|
||||
await Context.EventOccurrences.AddAsync(occurrence);
|
||||
savedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
Snackbar.Add($"Successfully saved {savedCount} occurrence(s) to database", Severity.Success);
|
||||
|
||||
// Navigate back to the calendar index after a short delay
|
||||
await Task.Delay(1000);
|
||||
NavigationManager.NavigateTo("/calendar/event-occurrences");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error saving to database: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetEventName(EventDefinition eventDefinition)
|
||||
{
|
||||
if (eventDefinition == EventDefinition.GeneralSchedule)
|
||||
return "General Schedule";
|
||||
if (eventDefinition == EventDefinition.VotingDelegates)
|
||||
return "Voting Delegates";
|
||||
return eventDefinition.Name;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -6,7 +6,11 @@
|
||||
@using Heron.MudCalendar
|
||||
@inject IEventOccurrenceService EventOccurrenceService
|
||||
|
||||
<PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences" />
|
||||
<PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences">
|
||||
<ActionButtons>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.ImportExport" Href="calendar/event-occurrences/import" Variant="Variant.Filled" Color="Color.Primary">Import</MudButton>
|
||||
</ActionButtons>
|
||||
</PageHeader>
|
||||
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
@if (_calendarItems == null)
|
||||
@@ -22,7 +22,7 @@
|
||||
@using WebApp.Components.Features.Events
|
||||
@using WebApp.Components.Features.Events.Components
|
||||
@using WebApp.Components.Features.MeetingSchedule
|
||||
@using WebApp.Components.Features.EventCalendar
|
||||
@using WebApp.Components.Features.Calendar
|
||||
@using MudBlazor
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
|
||||
@@ -173,6 +173,7 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
builder.Services.AddScoped<ClipboardService>();
|
||||
builder.Services.AddScoped<WebApp.LocalStorageService>();
|
||||
builder.Services.AddScoped<WebApp.Services.IEventOccurrenceService, WebApp.Services.EventOccurrenceService>();
|
||||
builder.Services.AddScoped<Core.Services.IEventOccurrenceParserService, Core.Services.EventOccurrenceParserService>();
|
||||
|
||||
// State container for maintaining state per user connection (Blazor Server)
|
||||
builder.Services.AddScoped<StateContainer>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
## Overview
|
||||
Implement a complete EventOccurrence management system with import functionality (text parsing with preview) and CRUD operations. The system will store event occurrences in the database with relationships to EventDefinitions (from database) or special event types (GeneralSchedule/VotingDelegates - hard-coded, not in database). Components are organized under the Calendar feature folder to allow for future expansion to support other calendar-related data types.
|
||||
|
||||
## Progress Summary
|
||||
- ✅ Database Changes (Sections 1-4): COMPLETED
|
||||
- ✅ Parser Service Enhancement (Sections 5-7): COMPLETED
|
||||
- ✅ Import Page (Section 8): COMPLETED
|
||||
- ⏳ CRUD Pages (Sections 9-13): PENDING
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
@@ -28,80 +34,88 @@ flowchart TD
|
||||
|
||||
## Database Changes
|
||||
|
||||
### 1. Update EventOccurrence Entity
|
||||
### 1. Update EventOccurrence Entity ✅ COMPLETED
|
||||
- **File**: `Core/Entities/EventOccurrence.cs`
|
||||
- Add `Id` property (int)
|
||||
- Add `EventDefinitionId` property (int?, nullable - references database EventDefinition if set)
|
||||
- Add `SpecialEventType` property (string?, nullable - stores "GeneralSchedule" or "VotingDelegates" when EventDefinitionId is null)
|
||||
- Keep existing properties (Name, Time, Date, StartTime, EndTime, Location)
|
||||
- Add navigation property to EventDefinition
|
||||
- ✅ Add `Id` property (int) - Already present
|
||||
- ✅ Add `EventDefinitionId` property (int?, nullable - references database EventDefinition if set) - Already present
|
||||
- ✅ Add `SpecialEventType` property (string?, nullable - stores "GeneralSchedule" or "VotingDelegates" when EventDefinitionId is null) - Already present
|
||||
- ✅ Keep existing properties (Name, Time, Date, StartTime, EndTime, Location) - Location changed to nullable
|
||||
- ✅ Add navigation property to EventDefinition - Already present
|
||||
- Note: GeneralSchedule and VotingDelegates are hard-coded EventDefinition instances (not stored in database) used only for EventOccurrences. When EventDefinitionId is null, SpecialEventType must be set to identify which special EventDefinition.
|
||||
|
||||
### 2. Create EventOccurrenceConfiguration
|
||||
### 2. Create EventOccurrenceConfiguration ✅ COMPLETED
|
||||
- **File**: `Data/Configurations/EventOccurrenceConfiguration.cs`
|
||||
- Configure primary key (Id)
|
||||
- Configure EventDefinitionId as foreign key (optional/nullable)
|
||||
- Configure indexes on StartTime, EventDefinitionId, SpecialEventType
|
||||
- Set property constraints (max lengths, required fields)
|
||||
- Add check constraint or validation: Either EventDefinitionId OR SpecialEventType must be set (not both, not neither)
|
||||
- ✅ Configure primary key (Id) - Already present
|
||||
- ✅ Configure EventDefinitionId as foreign key (optional/nullable) - Already present
|
||||
- ✅ Configure indexes on StartTime, EventDefinitionId, SpecialEventType - Already present
|
||||
- ✅ Set property constraints (max lengths, required fields) - Already present, Location updated to optional
|
||||
- Validation: Either EventDefinitionId OR SpecialEventType must be set (enforced at application level, not database level)
|
||||
|
||||
### 3. Update AppDbContext
|
||||
### 3. Update AppDbContext ✅ COMPLETED
|
||||
- **File**: `Data/AppDbContext.cs`
|
||||
- Add `DbSet<EventOccurrence> EventOccurrences { get; set; }`
|
||||
- ✅ Add `DbSet<EventOccurrence> EventOccurrences { get; set; }` - Already present
|
||||
|
||||
### 4. Create Migration
|
||||
- Generate migration for EventOccurrence table
|
||||
### 4. Create Migration ✅ COMPLETED
|
||||
- ✅ Generate migration for EventOccurrence table
|
||||
- Migration created and applied
|
||||
|
||||
## Parser Service Enhancement
|
||||
|
||||
### 5. Create Parser Service Interface
|
||||
### 5. Create Parser Service Interface ✅ COMPLETED
|
||||
- **File**: `Core/Services/IEventOccurrenceParserService.cs`
|
||||
- Method: `ParseFromText(string text, ICollection<EventDefinition> events)` returns `ParseResult`
|
||||
- ParseResult contains: Dictionary of occurrences, List of parsing errors/warnings
|
||||
- ✅ Method: `ParseFromText(string text, ICollection<EventDefinition> events)` returns `ParseResult`
|
||||
- ✅ ParseResult contains: Dictionary of occurrences, List of parsing errors/warnings
|
||||
|
||||
### 6. Create ParseResult Model
|
||||
### 6. Create ParseResult Model ✅ COMPLETED
|
||||
- **File**: `Core/Models/EventOccurrenceParseResult.cs`
|
||||
- Properties:
|
||||
- ✅ Properties:
|
||||
- `IDictionary<EventDefinition, List<EventOccurrence>> Occurrences`
|
||||
- `List<string> Errors`
|
||||
- `List<string> Warnings`
|
||||
- `int TotalParsed`
|
||||
- `bool IsSuccess` (computed property)
|
||||
|
||||
### 7. Adapt EventOccurrenceParser
|
||||
- **File**: `Core/Parsers/EventOccurrenceParser.cs` (modify existing or create wrapper)
|
||||
- Add method `ParseFromText(string text, ICollection<EventDefinition> events)` that:
|
||||
### 7. Adapt EventOccurrenceParser ✅ COMPLETED
|
||||
- **File**: `Core/Services/EventOccurrenceParserService.cs` (wrapper service created)
|
||||
- ✅ Implementation wraps existing EventOccurrenceParser:
|
||||
- Creates temporary file from text
|
||||
- Uses existing Parse() logic which already handles GeneralSchedule and VotingDelegates via EventDefinition.GeneralSchedule and EventDefinition.VotingDelegates
|
||||
- Uses existing Parse() logic which already handles GeneralSchedule and VotingDelegates
|
||||
- When parsing returns GeneralSchedule or VotingDelegates EventDefinitions:
|
||||
- Set EventOccurrence.EventDefinitionId to null
|
||||
- Set EventOccurrence.SpecialEventType to "GeneralSchedule" or "VotingDelegates"
|
||||
- Collects parsing errors (lines that don't match, unmatched event definitions, etc.)
|
||||
- Sets EventOccurrence.EventDefinitionId to null
|
||||
- Sets EventOccurrence.SpecialEventType to "GeneralSchedule" or "VotingDelegates"
|
||||
- For regular events: Sets EventDefinitionId and clears SpecialEventType
|
||||
- Collects parsing errors with exception handling
|
||||
- Returns ParseResult
|
||||
- ✅ Service registered in `WebApp/Program.cs`
|
||||
|
||||
## Import Page
|
||||
|
||||
### 8. Create Import Page
|
||||
### 8. Create Import Page ✅ COMPLETED
|
||||
- **File**: `WebApp/Components/Features/Calendar/Import.razor`
|
||||
- Route: `/calendar/event-occurrences/import`
|
||||
- Layout: Split view (MudGrid with 2 columns)
|
||||
- Left Column:
|
||||
- MudTextField (multiline) for pasting text
|
||||
- "Parse" button
|
||||
- ✅ Layout: Split view (MudGrid with 2 columns)
|
||||
- ✅ Left Column:
|
||||
- MudTextField (multiline, Lines="15") for pasting text
|
||||
- "Parse" button with Article icon
|
||||
- Clear button
|
||||
- Right Column:
|
||||
- Parsed results list (MudTable or MudList)
|
||||
- ✅ Right Column:
|
||||
- Parsed results list (MudExpansionPanels with MudTable for each event type)
|
||||
- Display occurrence details (Name, Date, Time, Location, EventDefinition)
|
||||
- Show errors/warnings in MudAlert components (including warnings for General Schedule/Voting Delegates lines)
|
||||
- Show errors/warnings in MudAlert components
|
||||
- Success summary with total parsed count
|
||||
- "Save to Database" button (only enabled if parsing successful)
|
||||
- Code-behind:
|
||||
- Clear Results button
|
||||
- ✅ Code-behind:
|
||||
- State management for parsed text
|
||||
- Call parser service on Parse button click
|
||||
- Display results and errors
|
||||
- Save parsed occurrences to database
|
||||
- Navigation back to calendar index after successful save
|
||||
- Proper error handling and user feedback via Snackbar
|
||||
|
||||
## CRUD Pages
|
||||
|
||||
### 9. Create Index Page
|
||||
### 9. Create Index Page ⏳ PENDING
|
||||
- **File**: `WebApp/Components/Features/Calendar/Index.razor`
|
||||
- Route: `/calendar/event-occurrences`
|
||||
- Features:
|
||||
@@ -114,8 +128,8 @@ flowchart TD
|
||||
- Include EventDefinition navigation property (nullable)
|
||||
- Display logic: Show EventDefinition.Name if EventDefinitionId is set, otherwise show SpecialEventType
|
||||
|
||||
### 10. Create Service Interface and Implementation
|
||||
- **File**: `Core/Services/IEventOccurrenceService.cs`
|
||||
### 10. Create Service Interface and Implementation ⏳ PENDING
|
||||
- **File**: `Core/Services/IEventOccurrenceService.cs` (may already exist)
|
||||
- **File**: `WebApp/Services/EventOccurrenceService.cs` (update existing)
|
||||
- Methods:
|
||||
- `GetAllAsync()` - Get all occurrences
|
||||
@@ -125,8 +139,9 @@ flowchart TD
|
||||
- `CreateAsync(EventOccurrence occurrence)` - Create new
|
||||
- `UpdateAsync(EventOccurrence occurrence)` - Update existing
|
||||
- `DeleteAsync(int id)` - Delete
|
||||
- Note: Service should handle special event types properly in queries
|
||||
|
||||
### 11. Create Create Page
|
||||
### 11. Create Create Page ⏳ PENDING
|
||||
- **File**: `WebApp/Components/Features/Calendar/Create.razor`
|
||||
- Route: `/calendar/event-occurrences/create`
|
||||
- Form fields:
|
||||
@@ -140,14 +155,14 @@ flowchart TD
|
||||
- Validation: Either EventDefinitionId OR SpecialEventType must be set (not both, not neither)
|
||||
- Save logic
|
||||
|
||||
### 12. Create Edit Page
|
||||
### 12. Create Edit Page ⏳ PENDING
|
||||
- **File**: `WebApp/Components/Features/Calendar/Edit.razor`
|
||||
- Route: `/calendar/event-occurrences/edit/{id}`
|
||||
- Similar form to Create page
|
||||
- Load existing occurrence data
|
||||
- Update logic
|
||||
|
||||
### 13. Create Delete Confirmation
|
||||
### 13. Create Delete Confirmation ⏳ PENDING
|
||||
- Use MudBlazor DialogService for delete confirmation
|
||||
- Implement delete logic in service and Index page
|
||||
|
||||
@@ -178,18 +193,19 @@ flowchart TD
|
||||
## Files to Create/Modify
|
||||
|
||||
**New Files:**
|
||||
1. `Core/Models/EventOccurrenceParseResult.cs`
|
||||
2. `Core/Services/IEventOccurrenceParserService.cs`
|
||||
3. `Core/Services/EventOccurrenceParserService.cs`
|
||||
4. `Data/Configurations/EventOccurrenceConfiguration.cs`
|
||||
5. `WebApp/Components/Features/Calendar/Import.razor`
|
||||
6. `WebApp/Components/Features/Calendar/Index.razor`
|
||||
7. `WebApp/Components/Features/Calendar/Create.razor`
|
||||
8. `WebApp/Components/Features/Calendar/Edit.razor`
|
||||
1. ✅ `Core/Models/EventOccurrenceParseResult.cs` - COMPLETED
|
||||
2. ✅ `Core/Services/IEventOccurrenceParserService.cs` - COMPLETED
|
||||
3. ✅ `Core/Services/EventOccurrenceParserService.cs` - COMPLETED
|
||||
4. ✅ `Data/Configurations/EventOccurrenceConfiguration.cs` - COMPLETED (already existed)
|
||||
5. ✅ `WebApp/Components/Features/Calendar/Import.razor` - COMPLETED (already existed, fixed icon issue)
|
||||
6. ⏳ `WebApp/Components/Features/Calendar/Index.razor` - PENDING
|
||||
7. ⏳ `WebApp/Components/Features/Calendar/Create.razor` - PENDING
|
||||
8. ⏳ `WebApp/Components/Features/Calendar/Edit.razor` - PENDING
|
||||
|
||||
**Modified Files:**
|
||||
1. `Core/Entities/EventOccurrence.cs` - Add Id, EventDefinitionId (nullable), SpecialEventType (nullable), navigation property
|
||||
2. `Data/AppDbContext.cs` - Add DbSet
|
||||
3. `WebApp/Services/EventOccurrenceService.cs` - Implement full CRUD operations, handle special event types in queries
|
||||
4. `Core/Parsers/EventOccurrenceParser.cs` - Add ParseFromText method (or create wrapper service), map GeneralSchedule/VotingDelegates EventDefinitions to SpecialEventType when creating EventOccurrences
|
||||
1. ✅ `Core/Entities/EventOccurrence.cs` - COMPLETED (Id, EventDefinitionId, SpecialEventType, navigation property already present; Location changed to nullable)
|
||||
2. ✅ `Data/AppDbContext.cs` - COMPLETED (DbSet already present)
|
||||
3. ⏳ `WebApp/Services/EventOccurrenceService.cs` - PENDING (need to implement full CRUD operations, handle special event types in queries)
|
||||
4. ✅ `Core/Parsers/EventOccurrenceParser.cs` - COMPLETED (wrapper service created; no modification needed to parser itself)
|
||||
5. ✅ `WebApp/Program.cs` - COMPLETED (ParserService registered)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user