This commit introduces several new services and models to manage the meeting schedule state within localStorage. The MeetingScheduleState class is created to track scheduled teams, absent students, time slot counts, extended teams, and excluded students. Additionally, the IMeetingScheduleStateService interface and its implementation, MeetingScheduleStateService, are added to handle loading and saving state data. The MeetingScheduleClipboardService and MeetingScheduleDataService are also introduced to facilitate clipboard operations and data loading from the database, respectively. These enhancements improve the overall functionality and user experience of the meeting scheduling feature.
266 lines
8.2 KiB
Plaintext
266 lines
8.2 KiB
Plaintext
@namespace WebApp.Components.Shared.Components
|
|
@using Core.Services
|
|
@inject INotesService NotesService
|
|
@inject INoteNamingService NoteNamingService
|
|
@inject ISnackbar Snackbar
|
|
@inject MarkdownTablePasteService MarkdownTablePasteService
|
|
@implements IAsyncDisposable
|
|
|
|
<MudDialog Class="@MarkdownHelper.GetNoteColorClass(_note.Id)">
|
|
<DialogContent>
|
|
@if (_isLoading)
|
|
{
|
|
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="my-4" />
|
|
}
|
|
else if (_isEditMode)
|
|
{
|
|
<MudStack Spacing="3">
|
|
<MudText Typo="Typo.subtitle2" Class="mb-2">Content (Markdown)</MudText>
|
|
<MarkdownEditor Value="@_note.Content"
|
|
ValueChanged="@((string? value) => _note.Content = value)"
|
|
Placeholder="Enter your markdown content here..."
|
|
AutoSaveEnabled="false"
|
|
NativeSpellChecker="false" />
|
|
</MudStack>
|
|
}
|
|
else
|
|
{
|
|
<MudStack Spacing="3">
|
|
<MudText Typo="Typo.h6">@DisplayTitle</MudText>
|
|
@if (!string.IsNullOrWhiteSpace(_note.Content))
|
|
{
|
|
<MudPaper Elevation="0" Class="pa-3" Style="background-color: var(--mud-palette-background-grey);">
|
|
<div class="markdown-content">
|
|
@((MarkupString)MarkdownHelper.ToHtml(_note.Content))
|
|
</div>
|
|
</MudPaper>
|
|
}
|
|
else
|
|
{
|
|
<MudText Typo="Typo.body2" Color="Color.Secondary" Align="Align.Center" Class="my-4">
|
|
No content yet. Click Edit to add content.
|
|
</MudText>
|
|
}
|
|
</MudStack>
|
|
}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
@if (_isEditMode)
|
|
{
|
|
<MudButton OnClick="Cancel" Disabled="@_isLoading">Cancel</MudButton>
|
|
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="Save" Disabled="@_isLoading">Save</MudButton>
|
|
}
|
|
else
|
|
{
|
|
<MudSpacer />
|
|
<MudButton StartIcon="@Icons.Material.Filled.Edit"
|
|
Color="Color.Primary"
|
|
Variant="Variant.Filled"
|
|
OnClick="EnterEditMode"
|
|
Disabled="@_isLoading">
|
|
Edit
|
|
</MudButton>
|
|
<MudButton OnClick="Cancel">Cancel</MudButton>
|
|
}
|
|
</DialogActions>
|
|
</MudDialog>
|
|
|
|
@code {
|
|
[CascadingParameter]
|
|
IMudDialogInstance MudDialog { get; set; } = null!;
|
|
|
|
[Parameter]
|
|
public string PageIdentifier { get; set; } = null!;
|
|
|
|
private Note _note = null!;
|
|
private string _pageNoteTitle = null!;
|
|
private string DisplayTitle => $"{PageIdentifier} Note";
|
|
private bool _isLoading = true;
|
|
private bool _isEdit = false;
|
|
private bool _isEditMode = false;
|
|
private CancellationTokenSource? _cancellationTokenSource;
|
|
private bool _isDisposed = false;
|
|
private bool _pasteMarkdownInitialized = false;
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
_cancellationTokenSource = new CancellationTokenSource();
|
|
_pageNoteTitle = NoteNamingService.GetPageNoteTitle(PageIdentifier);
|
|
_note = new Note
|
|
{
|
|
Title = _pageNoteTitle,
|
|
Content = ""
|
|
};
|
|
}
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await LoadPageNote();
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender && _isEditMode && !_pasteMarkdownInitialized)
|
|
{
|
|
// Initialize paste-markdown after first render if already in edit mode
|
|
await Task.Delay(150); // Wait for EasyMDE to initialize
|
|
if (!_isDisposed)
|
|
{
|
|
await MarkdownTablePasteService.InitializeAsync();
|
|
_pasteMarkdownInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task LoadPageNote()
|
|
{
|
|
if (_isDisposed) return;
|
|
|
|
try
|
|
{
|
|
var existingNote = await NotesService.GetPageNoteAsync(PageIdentifier);
|
|
|
|
if (existingNote != null)
|
|
{
|
|
_note = new Note
|
|
{
|
|
Id = existingNote.Id,
|
|
Title = existingNote.Title,
|
|
Content = existingNote.Content ?? ""
|
|
};
|
|
_isEdit = true;
|
|
// If note has no content, open in edit mode
|
|
if (string.IsNullOrWhiteSpace(_note.Content))
|
|
{
|
|
_isEditMode = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_note = new Note
|
|
{
|
|
Title = _pageNoteTitle,
|
|
Content = ""
|
|
};
|
|
_isEdit = false;
|
|
// New note - open in edit mode
|
|
_isEditMode = true;
|
|
}
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
// Component was disposed, ignore
|
|
}
|
|
catch (JSDisconnectedException)
|
|
{
|
|
// JS connection lost, ignore
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
Snackbar.Add($"Error loading page note: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
_isLoading = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task Save()
|
|
{
|
|
if (_isDisposed) return;
|
|
|
|
try
|
|
{
|
|
if (_isEdit)
|
|
{
|
|
await NotesService.UpdateNoteAsync(_note);
|
|
Snackbar.Add("Page note updated successfully", Severity.Success);
|
|
}
|
|
else
|
|
{
|
|
// CreateNoteAsync returns the note with the ID populated
|
|
var createdNote = await NotesService.CreateNoteAsync(_note);
|
|
// Update _note with the returned note to get the ID immediately
|
|
_note = createdNote;
|
|
_isEdit = true; // Mark as existing note now
|
|
Snackbar.Add("Page note created successfully", Severity.Success);
|
|
}
|
|
|
|
if (!_isDisposed)
|
|
{
|
|
// After saving, switch back to view mode and reload the note to ensure we have latest data
|
|
_isEditMode = false;
|
|
await LoadPageNote();
|
|
// Ensure dialog re-renders with updated color class after note ID is set
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
// Component was disposed, ignore
|
|
}
|
|
catch (JSDisconnectedException)
|
|
{
|
|
// JS connection lost, ignore
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
Snackbar.Add($"Error saving page note: {ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task EnterEditMode()
|
|
{
|
|
if (_isDisposed) return;
|
|
_isEditMode = true;
|
|
StateHasChanged();
|
|
|
|
// Initialize paste-markdown after editor is rendered
|
|
if (!_pasteMarkdownInitialized)
|
|
{
|
|
await Task.Delay(150); // Wait for EasyMDE to initialize
|
|
if (!_isDisposed)
|
|
{
|
|
await MarkdownTablePasteService.InitializeAsync();
|
|
_pasteMarkdownInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Cancel()
|
|
{
|
|
if (_isDisposed) return;
|
|
if (_isEditMode)
|
|
{
|
|
// If in edit mode, cancel closes the dialog (discarding changes)
|
|
MudDialog.Close(DialogResult.Cancel());
|
|
}
|
|
else
|
|
{
|
|
MudDialog.Close(DialogResult.Cancel());
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
_isDisposed = true;
|
|
_cancellationTokenSource?.Cancel();
|
|
_cancellationTokenSource?.Dispose();
|
|
_cancellationTokenSource = null;
|
|
}
|
|
await ValueTask.CompletedTask;
|
|
}
|
|
}
|