From 4782fd3f850d5562a32fe3c51125b25fcac6798d Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 23 Dec 2025 19:00:39 +0100 Subject: [PATCH] Fill to capacity works --- backend/pdfrender/pagination.go | 43 ++++++++++++++++++++++-- backend/pdfrender/pagination_test.go | 49 +++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/backend/pdfrender/pagination.go b/backend/pdfrender/pagination.go index fac693c..0193c57 100644 --- a/backend/pdfrender/pagination.go +++ b/backend/pdfrender/pagination.go @@ -201,8 +201,8 @@ func (p *Paginator) PaginateMultiList(dataMap map[string]interface{}, templateNa tracker, exists := listTrackers[trackerKey] if !exists { - // Block has no data, add empty slice - pageData[block.Name] = p.createEmptySlice(block.ListType) + // Block has no data, fill with empty items up to MAX + pageData[block.Name] = p.createEmptySliceWithCapacity(block.ListType, block.MaxItems) continue } @@ -213,8 +213,9 @@ func (p *Paginator) PaginateMultiList(dataMap map[string]interface{}, templateNa itemsToTake = remaining } - // Extract slice for this block + // Extract slice for this block and fill to capacity blockItems := p.extractSlice(tracker.items, tracker.currentIdx, itemsToTake) + blockItems = p.fillSliceToCapacity(blockItems, block.MaxItems) pageData[block.Name] = blockItems tracker.currentIdx += itemsToTake } @@ -495,6 +496,42 @@ func (p *Paginator) createEmptySlice(listType string) interface{} { } } +// createEmptySliceWithCapacity creates an empty slice filled to capacity with zero values +func (p *Paginator) createEmptySliceWithCapacity(listType string, capacity int) interface{} { + switch listType { + case "skills": + return FillToCapacity([]SkillViewModel{}, capacity) + case "weapons": + return FillToCapacity([]WeaponViewModel{}, capacity) + case "spells": + return FillToCapacity([]SpellViewModel{}, capacity) + case "equipment": + return FillToCapacity([]EquipmentViewModel{}, capacity) + case "magicItems": + return FillToCapacity([]MagicItemViewModel{}, capacity) + default: + return []interface{}{} + } +} + +// fillSliceToCapacity fills an existing slice to the specified capacity +func (p *Paginator) fillSliceToCapacity(items interface{}, capacity int) interface{} { + switch v := items.(type) { + case []SkillViewModel: + return FillToCapacity(v, capacity) + case []WeaponViewModel: + return FillToCapacity(v, capacity) + case []SpellViewModel: + return FillToCapacity(v, capacity) + case []EquipmentViewModel: + return FillToCapacity(v, capacity) + case []MagicItemViewModel: + return FillToCapacity(v, capacity) + default: + return items + } +} + // CalculatePagesNeeded calculates how many pages are needed for given data func (p *Paginator) CalculatePagesNeeded(templateName string, listType string, itemCount int) (int, error) { template := p.findTemplate(templateName) diff --git a/backend/pdfrender/pagination_test.go b/backend/pdfrender/pagination_test.go index 0490188..2c898c6 100644 --- a/backend/pdfrender/pagination_test.go +++ b/backend/pdfrender/pagination_test.go @@ -5,6 +5,39 @@ import ( "testing" ) +// Helper function to count non-empty skills (skills with a name) +func countNonEmptySkills(skills []SkillViewModel) int { + count := 0 + for _, skill := range skills { + if skill.Name != "" { + count++ + } + } + return count +} + +// Helper function to count non-empty weapons +func countNonEmptyWeapons(weapons []WeaponViewModel) int { + count := 0 + for _, weapon := range weapons { + if weapon.Name != "" { + count++ + } + } + return count +} + +// Helper function to count non-empty spells +func countNonEmptySpells(spells []SpellViewModel) int { + count := 0 + for _, spell := range spells { + if spell.Name != "" { + count++ + } + } + return count +} + // TestPaginateMultiList_SingleListType tests pagination with a single list type (skills only) func TestPaginateMultiList_SingleListType(t *testing.T) { // Arrange @@ -46,7 +79,7 @@ func TestPaginateMultiList_SingleListType(t *testing.T) { for _, blockName := range skillBlocks { if data, exists := page.Data[blockName]; exists { if skillsList, ok := data.([]SkillViewModel); ok { - totalSkills += len(skillsList) + totalSkills += countNonEmptySkills(skillsList) } } } @@ -105,9 +138,9 @@ func TestPaginateMultiList_MultipleListTypes(t *testing.T) { for _, data := range dist.Data { switch v := data.(type) { case []SkillViewModel: - totalSkills += len(v) + totalSkills += countNonEmptySkills(v) case []WeaponViewModel: - totalWeapons += len(v) + totalWeapons += countNonEmptyWeapons(v) } } } @@ -169,7 +202,7 @@ func TestPaginateMultiList_WithOverflow(t *testing.T) { if skillsList, ok := data.([]SkillViewModel); ok && (blockName == "skills_column1" || blockName == "skills_column2" || blockName == "skills_column3" || blockName == "skills_column4") { - totalSkills += len(skillsList) + totalSkills += countNonEmptySkills(skillsList) } } } @@ -240,6 +273,10 @@ func TestPaginateMultiList_WithFilters(t *testing.T) { if learnedData, ok := page.Data["skills_learned"]; ok { learned := learnedData.([]SkillViewModel) for _, skill := range learned { + // Skip empty items (padding) + if skill.Name == "" { + continue + } if !skill.IsLearned { t.Errorf("Found unlearned skill '%s' in learned block", skill.Name) } @@ -249,6 +286,10 @@ func TestPaginateMultiList_WithFilters(t *testing.T) { if languageData, ok := page.Data["skills_languages"]; ok { languages := languageData.([]SkillViewModel) for _, skill := range languages { + // Skip empty items (padding) + if skill.Name == "" { + continue + } if skill.Category != "Sprache" { t.Errorf("Found non-language skill '%s' in language block", skill.Name) }