Page target navigation

This commit is contained in:
Simon Martens
2025-09-19 16:41:00 +02:00
parent 04d62d9e67
commit 2574124588
22 changed files with 1582 additions and 203 deletions

206
viewmodels/piece_view.go Normal file
View File

@@ -0,0 +1,206 @@
package viewmodels
import (
"fmt"
"sort"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
)
type PiecePageEntry struct {
PageNumber int
IssueYear int
IssueNumber int
ImagePath string
IsContinuation bool
IssueContext string // "1764 Nr. 37" for display
Available bool
}
type PieceImages struct {
AllPages []IssuePage // Sequential list of all pages across issues
HasImages bool
}
type PieceVM struct {
xmlmodels.Piece
AllIssueRefs []xmlmodels.IssueRef // All issues containing this piece
AllPages []PiecePageEntry // Flattened chronological page list
ContinuousPages IndividualPiecesByPage // For template compatibility
Images PieceImages // All page images across issues
TotalPageCount int
Title string // Extracted piece title
MainCategory string // Primary category
IssueContexts []string // List of issue contexts for display
}
func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, error) {
pvm := &PieceVM{
Piece: piece,
AllIssueRefs: piece.IssueRefs,
AllPages: []PiecePageEntry{},
IssueContexts: []string{},
}
// Extract title from piece
if len(piece.Title) > 0 {
pvm.Title = piece.Title[0]
}
// Extract main category
if len(piece.CategoryRefs) > 0 {
pvm.MainCategory = piece.CategoryRefs[0].Ref
}
// Sort issue refs chronologically
sort.Slice(pvm.AllIssueRefs, func(i, j int) bool {
refA := pvm.AllIssueRefs[i]
refB := pvm.AllIssueRefs[j]
if refA.When.Year != refB.When.Year {
return refA.When.Year < refB.When.Year
}
return refA.Nr < refB.Nr
})
// Process each issue reference
for partIndex, issueRef := range pvm.AllIssueRefs {
issueContext := fmt.Sprintf("%d Nr. %d", issueRef.When.Year, issueRef.Nr)
pvm.IssueContexts = append(pvm.IssueContexts, issueContext)
// Add pages for this issue reference
for pageNum := issueRef.Von; pageNum <= issueRef.Bis; pageNum++ {
pageEntry := PiecePageEntry{
PageNumber: pageNum,
IssueYear: issueRef.When.Year,
IssueNumber: issueRef.Nr,
IsContinuation: pageNum > issueRef.Von || partIndex > 0,
IssueContext: issueContext,
Available: true, // Will be updated when we load images
}
// Get actual image path from registry
pageEntry.ImagePath = getImagePathFromRegistry(issueRef.When.Year, pageNum)
pvm.AllPages = append(pvm.AllPages, pageEntry)
}
}
pvm.TotalPageCount = len(pvm.AllPages)
// Load images and update availability
if err := pvm.loadImages(); err != nil {
return nil, fmt.Errorf("failed to load images: %w", err)
}
// Create template-compatible structure
if err := pvm.createContinuousPages(lib); err != nil {
return nil, fmt.Errorf("failed to create continuous pages: %w", err)
}
return pvm, nil
}
// loadImages loads and validates all page images for the piece
func (pvm *PieceVM) loadImages() error {
// Initialize image registry if needed
if err := initImageRegistry(); err != nil {
return err
}
issuePages := []IssuePage{}
hasAnyImages := false
for i, pageEntry := range pvm.AllPages {
// Create IssuePage for template compatibility
issuePage := IssuePage{
PageNumber: pageEntry.PageNumber,
ImagePath: pageEntry.ImagePath,
Available: true, // Assume available for now
PageIcon: "single", // Simplified icon for piece view
}
// Check if image actually exists using the registry
key := fmt.Sprintf("%d-%d", pageEntry.IssueYear, pageEntry.PageNumber)
if imageRegistry != nil {
if _, exists := imageRegistry.ByYearPage[key]; exists {
hasAnyImages = true
} else {
issuePage.Available = false
pvm.AllPages[i].Available = false
}
}
issuePages = append(issuePages, issuePage)
}
pvm.Images = PieceImages{
AllPages: issuePages,
HasImages: hasAnyImages,
}
return nil
}
// createContinuousPages creates the template-compatible IndividualPiecesByPage structure
func (pvm *PieceVM) createContinuousPages(lib *xmlmodels.Library) error {
individual := IndividualPiecesByPage{
Items: make(map[int][]IndividualPieceByIssue),
Pages: []int{},
}
// Create a virtual piece entry for each page
for _, pageEntry := range pvm.AllPages {
// Create IssueRef for this specific page
issueRef := xmlmodels.IssueRef{
Nr: pageEntry.IssueNumber,
Von: pageEntry.PageNumber,
Bis: pageEntry.PageNumber,
}
issueRef.When.Year = pageEntry.IssueYear
// Create PieceByIssue
pieceByIssue := PieceByIssue{
Piece: pvm.Piece,
Reference: issueRef,
IsContinuation: pageEntry.IsContinuation,
}
// Create IndividualPieceByIssue
individualPiece := IndividualPieceByIssue{
PieceByIssue: pieceByIssue,
IssueRefs: pvm.AllIssueRefs,
PageIcon: "single", // Simplified icon for piece view
}
// Add to the page map
if individual.Items[pageEntry.PageNumber] == nil {
individual.Items[pageEntry.PageNumber] = []IndividualPieceByIssue{}
individual.Pages = append(individual.Pages, pageEntry.PageNumber)
}
individual.Items[pageEntry.PageNumber] = append(individual.Items[pageEntry.PageNumber], individualPiece)
}
// Sort pages
sort.Ints(individual.Pages)
pvm.ContinuousPages = individual
return nil
}
// getImagePathFromRegistry gets the actual image path from the image registry
func getImagePathFromRegistry(year, page int) string {
// Initialize registry if needed
if err := initImageRegistry(); err != nil {
return ""
}
// Look up the image by year and page
key := fmt.Sprintf("%d-%d", year, page)
if imageFile, exists := imageRegistry.ByYearPage[key]; exists {
return imageFile.Path
}
// Fallback: generate a default path (though this probably won't exist)
return fmt.Sprintf("/static/pictures/%d/seite_%d.jpg", year, page)
}