- Wire up lists and todos to C# backend with full CRUD persistence - Add models, DTOs, repositories, and services for lists and todo lists/items - Preserve SQLite DB across vault rebuild/load cycles - Add session store for vault password persistence across navigation - Add inline name input for creating lists and todo lists in SidePanel - Clear editor panel on section change with empty state placeholder - Default markdown editor to preview mode on item selection - Decompose EditorPanel into sub-components: - editor/FragmentEditor, editor/TodoEditor, editor/MarkdownEditor - Shared markdown utilities in utils/markdown.ts - Strip verbose console/eprintln logging from frontend and Tauri backend - Add graceful shutdown with vault persistence on window close Co-Authored-By: Oz <oz-agent@warp.dev>
92 lines
2.6 KiB
C#
92 lines
2.6 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using Journal.Core.Dtos;
|
|
using Journal.Core.Models;
|
|
using Journal.Core.Repositories;
|
|
|
|
namespace Journal.Core.Services.Todos;
|
|
|
|
public class TodoService(ITodoRepository repo) : ITodoService
|
|
{
|
|
private readonly ITodoRepository _repo = repo ?? throw new ArgumentNullException(nameof(repo));
|
|
|
|
private static TodoItemDto MapItem(TodoItem i) => new(
|
|
i.Id,
|
|
i.ListId,
|
|
i.Text,
|
|
i.Done,
|
|
i.SortOrder
|
|
);
|
|
|
|
private TodoListDto MapList(TodoList l)
|
|
{
|
|
var items = _repo.GetItemsByListId(l.Id);
|
|
return new TodoListDto(
|
|
l.Id,
|
|
l.Label,
|
|
l.CreatedAt,
|
|
[.. items.Select(MapItem)]
|
|
);
|
|
}
|
|
|
|
public List<TodoListDto> GetAllLists()
|
|
{
|
|
var lists = _repo.GetAllLists();
|
|
return [.. lists.Select(MapList)];
|
|
}
|
|
|
|
public TodoListDto? GetListById(Guid id)
|
|
{
|
|
var l = _repo.GetListById(id);
|
|
return l is null ? null : MapList(l);
|
|
}
|
|
|
|
public TodoListDto CreateList(CreateTodoListDto dto)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(dto);
|
|
|
|
var ctx = new ValidationContext(dto);
|
|
Validator.ValidateObject(dto, ctx, validateAllProperties: true);
|
|
|
|
var list = new TodoList(dto.Label);
|
|
_repo.AddList(list);
|
|
return new TodoListDto(list.Id, list.Label, list.CreatedAt, []);
|
|
}
|
|
|
|
public bool UpdateList(Guid id, UpdateTodoListDto dto)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(dto);
|
|
if (dto.Label is not null && string.IsNullOrWhiteSpace(dto.Label))
|
|
throw new ValidationException("Label cannot be empty");
|
|
|
|
return _repo.UpdateList(id, dto.Label?.Trim());
|
|
}
|
|
|
|
public bool RemoveList(Guid id) => _repo.RemoveList(id);
|
|
|
|
public TodoItemDto CreateItem(CreateTodoItemDto dto)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(dto);
|
|
|
|
var ctx = new ValidationContext(dto);
|
|
Validator.ValidateObject(dto, ctx, validateAllProperties: true);
|
|
|
|
if (dto.ListId == Guid.Empty)
|
|
throw new ValidationException("ListId is required");
|
|
|
|
var item = new TodoItem(dto.ListId, dto.Text, dto.SortOrder ?? 0);
|
|
_repo.AddItem(item);
|
|
return MapItem(item);
|
|
}
|
|
|
|
public bool UpdateItem(Guid id, UpdateTodoItemDto dto)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(dto);
|
|
if (dto.Text is not null && string.IsNullOrWhiteSpace(dto.Text))
|
|
throw new ValidationException("Text cannot be empty");
|
|
|
|
return _repo.UpdateItem(id, dto.Text?.Trim(), dto.Done, dto.SortOrder);
|
|
}
|
|
|
|
public bool RemoveItem(Guid id) => _repo.RemoveItem(id);
|
|
}
|