Jacob Schmidt 0d77300c22 feat: Project Journal backend monorepo
Monorepo with centralized build props, npm workspaces, LlamaSharp AI,
SQLite/SQLCipher storage, Svelte frontend, and unified smoke tests.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-03-02 20:56:26 -06:00

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);
}