feat(data): list_folders table plus lists.folder_id

Sidebar folders get their own table rather than a flag on lists, so the
places that enumerate lists (MCP tools, move-to-list, queue picker, repo
import) need no folder filtering. ON DELETE SET NULL keeps a folder's
lists alive when the folder goes.
This commit is contained in:
mika kuns
2026-09-10 08:29:56 +02:00
parent f231f08a27
commit 13e860f06b
9 changed files with 1334 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,74 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ClaudeDo.Data.Migrations
{
/// <inheritdoc />
public partial class AddListFolders : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "folder_id",
table: "lists",
type: "TEXT",
nullable: true);
migrationBuilder.CreateTable(
name: "list_folders",
columns: table => new
{
id = table.Column<string>(type: "TEXT", nullable: false),
name = table.Column<string>(type: "TEXT", nullable: false),
created_at = table.Column<DateTime>(type: "TEXT", nullable: false),
icon = table.Column<string>(type: "TEXT", nullable: true),
sort_order = table.Column<int>(type: "INTEGER", nullable: false, defaultValue: 0),
is_collapsed = table.Column<bool>(type: "INTEGER", nullable: false, defaultValue: false)
},
constraints: table =>
{
table.PrimaryKey("PK_list_folders", x => x.id);
});
migrationBuilder.CreateIndex(
name: "idx_lists_folder",
table: "lists",
column: "folder_id");
migrationBuilder.CreateIndex(
name: "idx_list_folders_sort",
table: "list_folders",
column: "sort_order");
migrationBuilder.AddForeignKey(
name: "FK_lists_list_folders_folder_id",
table: "lists",
column: "folder_id",
principalTable: "list_folders",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_lists_list_folders_folder_id",
table: "lists");
migrationBuilder.DropTable(
name: "list_folders");
migrationBuilder.DropIndex(
name: "idx_lists_folder",
table: "lists");
migrationBuilder.DropColumn(
name: "folder_id",
table: "lists");
}
}
}
@@ -324,6 +324,10 @@ namespace ClaudeDo.Data.Migrations
.HasDefaultValue(false)
.HasColumnName("findings_tracked");
b.Property<string>("FolderId")
.HasColumnType("TEXT")
.HasColumnName("folder_id");
b.Property<string>("Icon")
.HasColumnType("TEXT")
.HasColumnName("icon");
@@ -351,12 +355,54 @@ namespace ClaudeDo.Data.Migrations
b.HasKey("Id");
b.HasIndex("FolderId")
.HasDatabaseName("idx_lists_folder");
b.HasIndex("SortOrder")
.HasDatabaseName("idx_lists_sort");
b.ToTable("lists", (string)null);
});
modelBuilder.Entity("ClaudeDo.Data.Models.ListFolderEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT")
.HasColumnName("id");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT")
.HasColumnName("created_at");
b.Property<string>("Icon")
.HasColumnType("TEXT")
.HasColumnName("icon");
b.Property<bool>("IsCollapsed")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasDefaultValue(false)
.HasColumnName("is_collapsed");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("name");
b.Property<int>("SortOrder")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasDefaultValue(0)
.HasColumnName("sort_order");
b.HasKey("Id");
b.HasIndex("SortOrder")
.HasDatabaseName("idx_list_folders_sort");
b.ToTable("list_folders", (string)null);
});
modelBuilder.Entity("ClaudeDo.Data.Models.PrimeScheduleEntity", b =>
{
b.Property<Guid>("Id")
@@ -934,6 +980,14 @@ namespace ClaudeDo.Data.Migrations
b.Navigation("List");
});
modelBuilder.Entity("ClaudeDo.Data.Models.ListEntity", b =>
{
b.HasOne("ClaudeDo.Data.Models.ListFolderEntity", null)
.WithMany()
.HasForeignKey("FolderId")
.OnDelete(DeleteBehavior.SetNull);
});
modelBuilder.Entity("ClaudeDo.Data.Models.SubtaskEntity", b =>
{
b.HasOne("ClaudeDo.Data.Models.TaskEntity", "Task")