Add Note and NoteHistory entities with configurations and service implementation
This commit introduces the Note and NoteHistory entities, along with their respective configurations for Entity Framework Core. The AppDbContext has been updated to include DbSet properties for both entities. A new INotesService interface and its implementation, NotesService, have been created to handle CRUD operations for notes, including history tracking. Additionally, several Blazor components have been added for note management, including dialogs for editing notes and viewing note history. The UI has been enhanced to support markdown content rendering and improved user interaction for note creation and editing. These changes contribute to a comprehensive note-taking feature within the application.
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
using Core.Entities;
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace WebApp.Services;
|
||||
|
||||
public class NotesService : INotesService
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<NotesService> _logger;
|
||||
|
||||
public NotesService(
|
||||
AppDbContext context,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<NotesService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private string? GetCurrentUserEmail()
|
||||
{
|
||||
var user = _httpContextAccessor.HttpContext?.User;
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
return user.FindFirstValue(ClaimTypes.Email) ?? user.Identity?.Name;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Note>> GetNotesAsync()
|
||||
{
|
||||
return await _context.Notes
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(n => n.UpdatedAt)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Note?> GetNoteAsync(int id)
|
||||
{
|
||||
return await _context.Notes
|
||||
.Include(n => n.NoteHistories.OrderByDescending(h => h.ModifiedAt))
|
||||
.FirstOrDefaultAsync(n => n.Id == id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NoteHistory>> GetNoteHistoryAsync(int noteId)
|
||||
{
|
||||
return await _context.NoteHistories
|
||||
.AsNoTracking()
|
||||
.Where(h => h.NoteId == noteId)
|
||||
.OrderByDescending(h => h.ModifiedAt)
|
||||
.Take(10)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Note> CreateNoteAsync(Note note)
|
||||
{
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
note.CreatedAt = now;
|
||||
note.UpdatedAt = now;
|
||||
note.CreatedBy = userEmail;
|
||||
note.LastModifiedBy = userEmail;
|
||||
|
||||
_context.Notes.Add(note);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Create initial history entry
|
||||
var history = new NoteHistory
|
||||
{
|
||||
NoteId = note.Id,
|
||||
Title = note.Title,
|
||||
Content = note.Content,
|
||||
ModifiedBy = userEmail,
|
||||
ModifiedAt = now,
|
||||
ChangeType = "Created"
|
||||
};
|
||||
|
||||
_context.NoteHistories.Add(history);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Note created: {NoteId} by {User}", note.Id, userEmail);
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
public async Task<Note> UpdateNoteAsync(Note note)
|
||||
{
|
||||
var existingNote = await _context.Notes
|
||||
.FirstOrDefaultAsync(n => n.Id == note.Id);
|
||||
|
||||
if (existingNote == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Note with ID {note.Id} not found.");
|
||||
}
|
||||
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Create history entry with previous state before updating
|
||||
var history = new NoteHistory
|
||||
{
|
||||
NoteId = existingNote.Id,
|
||||
Title = existingNote.Title,
|
||||
Content = existingNote.Content,
|
||||
ModifiedBy = userEmail,
|
||||
ModifiedAt = now,
|
||||
ChangeType = "Updated"
|
||||
};
|
||||
|
||||
_context.NoteHistories.Add(history);
|
||||
|
||||
// Update the note
|
||||
existingNote.Title = note.Title;
|
||||
existingNote.Content = note.Content;
|
||||
existingNote.UpdatedAt = now;
|
||||
existingNote.LastModifiedBy = userEmail;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Note updated: {NoteId} by {User}", note.Id, userEmail);
|
||||
|
||||
return existingNote;
|
||||
}
|
||||
|
||||
public async Task DeleteNoteAsync(int id)
|
||||
{
|
||||
var note = await _context.Notes
|
||||
.FirstOrDefaultAsync(n => n.Id == id);
|
||||
|
||||
if (note == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Note with ID {id} not found.");
|
||||
}
|
||||
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Create deletion history entry
|
||||
var history = new NoteHistory
|
||||
{
|
||||
NoteId = note.Id,
|
||||
Title = note.Title,
|
||||
Content = note.Content,
|
||||
ModifiedBy = userEmail,
|
||||
ModifiedAt = now,
|
||||
ChangeType = "Deleted"
|
||||
};
|
||||
|
||||
_context.NoteHistories.Add(history);
|
||||
|
||||
// Delete the note (cascade will handle history, but we want to keep history)
|
||||
_context.Notes.Remove(note);
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Note deleted: {NoteId} by {User}", id, userEmail);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user