diff --git a/src/NoteBookmark.BlazorApp.Tests/Tests/ReadingNotesReorderingTests.cs b/src/NoteBookmark.BlazorApp.Tests/Tests/ReadingNotesReorderingTests.cs new file mode 100644 index 0000000..2b6a2d9 --- /dev/null +++ b/src/NoteBookmark.BlazorApp.Tests/Tests/ReadingNotesReorderingTests.cs @@ -0,0 +1,115 @@ +using FluentAssertions; +using NoteBookmark.Domain; +using Xunit; + +namespace NoteBookmark.BlazorApp.Tests.Tests; + +public class ReadingNotesReorderingTests +{ + private ReadingNotes CreateSampleReadingNotes() + { + var rn = new ReadingNotes("1") + { + Title = "Reading Notes #1" + }; + rn.Notes["Category A"] = new List + { + new ReadingNote { Title = "Note A1", RowKey = "a1" }, + new ReadingNote { Title = "Note A2", RowKey = "a2" }, + new ReadingNote { Title = "Note A3", RowKey = "a3" } + }; + rn.Notes["Category B"] = new List + { + new ReadingNote { Title = "Note B1", RowKey = "b1" } + }; + rn.Notes["Category C"] = new List + { + new ReadingNote { Title = "Note C1", RowKey = "c1" } + }; + return rn; + } + + [Fact] + public void MoveCategoryUp_SwapsCategoryWithPrevious() + { + var rn = CreateSampleReadingNotes(); + + bool moved = rn.MoveCategoryUp("Category B"); + + moved.Should().BeTrue(); + rn.Notes.Keys.Should().ContainInConsecutiveOrder("Category B", "Category A", "Category C"); + } + + [Fact] + public void MoveCategoryUp_OnFirstCategory_ReturnsFalse() + { + var rn = CreateSampleReadingNotes(); + + bool moved = rn.MoveCategoryUp("Category A"); + + moved.Should().BeFalse(); + rn.Notes.Keys.Should().ContainInConsecutiveOrder("Category A", "Category B", "Category C"); + } + + [Fact] + public void MoveCategoryDown_SwapsCategoryWithNext() + { + var rn = CreateSampleReadingNotes(); + + bool moved = rn.MoveCategoryDown("Category A"); + + moved.Should().BeTrue(); + rn.Notes.Keys.Should().ContainInConsecutiveOrder("Category B", "Category A", "Category C"); + } + + [Fact] + public void MoveCategoryDown_OnLastCategory_ReturnsFalse() + { + var rn = CreateSampleReadingNotes(); + + bool moved = rn.MoveCategoryDown("Category C"); + + moved.Should().BeFalse(); + rn.Notes.Keys.Should().ContainInConsecutiveOrder("Category A", "Category B", "Category C"); + } + + [Fact] + public void MoveNoteUp_SwapsNoteWithPrevious() + { + var rn = CreateSampleReadingNotes(); + + bool moved = rn.MoveNoteUp("Category A", 1); + + moved.Should().BeTrue(); + rn.Notes["Category A"].Select(n => n.Title).Should().ContainInConsecutiveOrder("Note A2", "Note A1", "Note A3"); + } + + [Fact] + public void MoveNoteDown_SwapsNoteWithNext() + { + var rn = CreateSampleReadingNotes(); + + bool moved = rn.MoveNoteDown("Category A", 0); + + moved.Should().BeTrue(); + rn.Notes["Category A"].Select(n => n.Title).Should().ContainInConsecutiveOrder("Note A2", "Note A1", "Note A3"); + } + + [Fact] + public void ReorderedNotes_ReflectsInMarkdownGeneration() + { + var rn = CreateSampleReadingNotes(); + rn.MoveCategoryUp("Category B"); + rn.MoveNoteUp("Category A", 1); + + string md = rn.ToMarkDown(); + + int catBPos = md.IndexOf("## Category B"); + int catAPos = md.IndexOf("## Category A"); + catBPos.Should().BeLessThan(catAPos); + + int noteA2Pos = md.IndexOf("Note A2"); + int noteA1Pos = md.IndexOf("Note A1"); + noteA2Pos.Should().BeLessThan(noteA1Pos); + } +} diff --git a/src/NoteBookmark.Domain/ReadingNotes.cs b/src/NoteBookmark.Domain/ReadingNotes.cs index 299018d..156f83b 100644 --- a/src/NoteBookmark.Domain/ReadingNotes.cs +++ b/src/NoteBookmark.Domain/ReadingNotes.cs @@ -38,6 +38,86 @@ public ReadingNotes(string number) public string Intro { get; set; } = string.Empty; public Dictionary> Notes { get; set; } + public bool MoveCategoryUp(string category) + { + if (Notes == null || !Notes.ContainsKey(category)) return false; + + var keys = Notes.Keys.ToList(); + int index = keys.IndexOf(category); + if (index <= 0) return false; + + var newDict = new Dictionary>(); + for (int i = 0; i < keys.Count; i++) + { + if (i == index - 1) + { + newDict[category] = Notes[category]; + newDict[keys[i]] = Notes[keys[i]]; + } + else if (i == index) + { + continue; + } + else + { + newDict[keys[i]] = Notes[keys[i]]; + } + } + Notes = newDict; + return true; + } + + public bool MoveCategoryDown(string category) + { + if (Notes == null || !Notes.ContainsKey(category)) return false; + + var keys = Notes.Keys.ToList(); + int index = keys.IndexOf(category); + if (index < 0 || index >= keys.Count - 1) return false; + + var newDict = new Dictionary>(); + for (int i = 0; i < keys.Count; i++) + { + if (i == index) + { + newDict[keys[i + 1]] = Notes[keys[i + 1]]; + newDict[category] = Notes[category]; + } + else if (i == index + 1) + { + continue; + } + else + { + newDict[keys[i]] = Notes[keys[i]]; + } + } + Notes = newDict; + return true; + } + + public bool MoveNoteUp(string category, int noteIndex) + { + if (Notes == null || !Notes.TryGetValue(category, out var list)) return false; + if (noteIndex <= 0 || noteIndex >= list.Count) return false; + + var note = list[noteIndex]; + list.RemoveAt(noteIndex); + list.Insert(noteIndex - 1, note); + return true; + } + + public bool MoveNoteDown(string category, int noteIndex) + { + if (Notes == null || !Notes.TryGetValue(category, out var list)) return false; + if (noteIndex < 0 || noteIndex >= list.Count - 1) return false; + + var note = list[noteIndex]; + list.RemoveAt(noteIndex); + list.Insert(noteIndex + 1, note); + return true; + } + public string GetAllUniqueTags(){ var uniqueTags = new HashSet(); diff --git a/src/NoteBookmark.SharedUI/Components/Pages/SummaryEditor.razor b/src/NoteBookmark.SharedUI/Components/Pages/SummaryEditor.razor index b620153..4782f73 100644 --- a/src/NoteBookmark.SharedUI/Components/Pages/SummaryEditor.razor +++ b/src/NoteBookmark.SharedUI/Components/Pages/SummaryEditor.razor @@ -45,34 +45,49 @@ else{ IconStart="@(new Icons.Filled.Size20.StarEmphasis())">Generate
- @foreach (var note in readingNotes!.Notes) + @{ + var categoryKeys = readingNotes!.Notes.Keys.ToList(); + } + @for (int catIdx = 0; catIdx < categoryKeys.Count; catIdx++) { - string category = note.Key; - List rnList = note.Value; + string category = categoryKeys[catIdx]; + List rnList = readingNotes.Notes[category]; + bool isFirstCategory = catIdx == 0; + bool isLastCategory = catIdx == categoryKeys.Count - 1;
- +

@category

- + + +
- @foreach (ReadingNote rn in rnList) + @for (int noteIdx = 0; noteIdx < rnList.Count; noteIdx++) { -
- + var rn = rnList[noteIdx]; + int currentNoteIndex = noteIdx; + bool isFirstNote = noteIdx == 0; + bool isLastNote = noteIdx == rnList.Count - 1; +
+ + + + + + - +
- } Add Note
@@ -274,4 +289,23 @@ else{ } } + private void MoveCategoryUp(string category) + { + readingNotes?.MoveCategoryUp(category); + } + + private void MoveCategoryDown(string category) + { + readingNotes?.MoveCategoryDown(category); + } + + private void MoveNoteUp(string category, int index) + { + readingNotes?.MoveNoteUp(category, index); + } + + private void MoveNoteDown(string category, int index) + { + readingNotes?.MoveNoteDown(category, index); + } }