Files
bookhoard/internal/docs/navigation.go
T
john-okeefe 253f56399d docs: restructure documentation into audience-based portals
BREAKING CHANGE: Documentation URLs have changed

New structure:
- user/ - End-user documentation (device setup, sync guides, frontend)
- developer/ - Developer documentation (API reference, protocols, specs)
- operations/ - Operations documentation (deployment, troubleshooting)
- contributing/ - Contribution guides

Changes:
- Created portal INDEX.md files for each audience section
- Moved device guides to user/devices/ (kobo-setup.md, koreader-setup.md)
- Moved API docs to developer/ (api-reference.md, collections-api.md)
- Moved sync guide to user/sync-guide.md
- Moved troubleshooting to operations/troubleshooting.md
- Moved all split API docs to developer/api/
- Renamed protocol files (kobo-protocol.md, koreader-protocol.md)
- Added placeholder user guides (frontend, user-areas, settings, admin)
- Updated all internal links to new paths
- Updated Go code (http_handler.go, navigation.go) for new paths
- Updated main INDEX.md for audience-based navigation

Benefits:
- Clear separation of user and developer documentation
- Scalable structure for future user guide expansion
- Better organization and discoverability
- Audience-specific landing pages

Related to DOCS_IMPLEMENTATION_PLAN.md Phase 2 completion
2026-02-02 15:58:34 -05:00

231 lines
6.7 KiB
Go

package docs
import (
"sort"
"strings"
"bookhoard/templates"
)
// BuildNavigation creates the navigation structure from the docs filesystem
func (h *DocsHandler) BuildNavigation() *templates.Navigation {
docs, err := h.ListDocuments()
if err != nil {
// Return minimal navigation on error
return &templates.Navigation{
Sections: []templates.NavSection{
{
Title: "Getting Started",
Items: []templates.NavItem{
{Title: "Overview", URL: "/docs"},
},
},
},
}
}
// Group documents by category
categories := make(map[string][]templates.NavItem)
for _, doc := range docs {
item := h.createNavItem(doc)
category := h.getCategoryForNav(doc)
categories[category] = append(categories[category], item)
}
// Build navigation sections
var sections []templates.NavSection
// Define section order
sectionOrder := []string{
"Getting Started",
"User Guide",
"Device Setup",
"API Reference",
"Contributing",
}
// Add sections in order
for _, sectionTitle := range sectionOrder {
if items, exists := categories[sectionTitle]; exists {
sections = append(sections, templates.NavSection{
Title: sectionTitle,
Items: items,
Collapsed: sectionTitle != "Getting Started", // Expand first section
})
delete(categories, sectionTitle)
}
}
// Add any remaining sections
for title, items := range categories {
sections = append(sections, templates.NavSection{
Title: title,
Items: items,
Collapsed: true,
})
}
return &templates.Navigation{Sections: sections}
}
// createNavItem creates a navigation item from a document path
func (h *DocsHandler) createNavItem(docPath string) templates.NavItem {
title := h.getDocTitle(docPath)
url := "/docs/" + strings.TrimSuffix(docPath, ".md")
icon := h.getDocIcon(docPath)
return templates.NavItem{
Title: title,
URL: url,
Icon: icon,
}
}
// getDocTitle extracts a readable title from a document path
func (h *DocsHandler) getDocTitle(docPath string) string {
// Remove .md extension
title := strings.TrimSuffix(docPath, ".md")
// Split by /
parts := strings.Split(title, "/")
// Get the last part (filename)
filename := parts[len(parts)-1]
// Convert to title case
title = strings.Title(strings.ReplaceAll(filename, "-", " "))
// Handle special cases
switch filename {
case "INDEX.md":
return "Documentation Index"
case "developer/api-reference.md":
return "API Reference"
case "developer/collections-api.md":
return "Collections API"
case "developer/websocket-api.md":
return "WebSocket API"
case "operations/troubleshooting.md":
return "Troubleshooting"
case "user/sync-guide.md":
return "Sync Guide"
case "user/devices/kobo-setup.md":
return "Kobo Setup"
case "user/devices/koreader-setup.md":
return "KOReader Setup"
case "DEVELOPMENT.md":
return "Development Guide"
}
return title
}
// getDocIcon returns an appropriate icon for a document
func (h *DocsHandler) getDocIcon(docPath string) string {
if strings.Contains(docPath, "api") || strings.Contains(docPath, "API") {
return "🔌"
}
if strings.Contains(docPath, "device") {
return "📱"
}
if strings.Contains(docPath, "sync") || strings.Contains(docPath, "SYNC") {
return "🔄"
}
if strings.Contains(docPath, "contributing") || strings.Contains(docPath, "DEVELOPMENT") {
return "🛠️"
}
if strings.Contains(docPath, "INDEX") {
return "📚"
}
if strings.Contains(docPath, "troubleshooting") {
return "🔧"
}
return "📄"
}
// getCategoryForNav determines the navigation category for a document
func (h *DocsHandler) getCategoryForNav(docPath string) string {
// API documentation
if strings.Contains(docPath, "api") || strings.Contains(docPath, "API") {
return "API Reference"
}
// Device setup
if strings.HasPrefix(docPath, "devices/") {
return "Device Setup"
}
// Contributing
if strings.HasPrefix(docPath, "contributing/") {
return "Contributing"
}
// Core user documentation
docName := strings.ToLower(docPath)
switch {
case strings.Contains(docName, "sync"):
return "User Guide"
case strings.Contains(docName, "troubleshoot"):
return "Getting Started"
case strings.Contains(docName, "index"):
return "Getting Started"
default:
return "Getting Started"
}
}
// GetAPIEndpoints returns a list of API endpoints for the API documentation
func (h *DocsHandler) GetAPIEndpoints() []APIEndpoint {
// This would be better auto-generated from Go code comments
// For now, return a static list based on your API
endpoints := []APIEndpoint{
// Authentication
{Method: "POST", Path: "/api/auth/register", Category: "Authentication", Title: "Register User"},
{Method: "POST", Path: "/api/auth/login", Category: "Authentication", Title: "Login"},
{Method: "POST", Path: "/api/auth/refresh", Category: "Authentication", Title: "Refresh Token"},
{Method: "POST", Path: "/api/auth/logout", Category: "Authentication", Title: "Logout"},
// Users
{Method: "GET", Path: "/api/users/me", Category: "Users", Title: "Get Current User"},
{Method: "PUT", Path: "/api/users/me/profile", Category: "Users", Title: "Update Profile"},
{Method: "PUT", Path: "/api/users/me/theme", Category: "Users", Title: "Update Theme"},
{Method: "PUT", Path: "/api/users/me/password", Category: "Users", Title: "Change Password"},
// Libraries
{Method: "GET", Path: "/api/libraries/visible", Category: "Libraries", Title: "Get Visible Libraries"},
{Method: "GET", Path: "/api/libraries/{id}", Category: "Libraries", Title: "Get Library Details"},
{Method: "POST", Path: "/api/libraries", Category: "Libraries", Title: "Create Library"},
// Media Items
{Method: "GET", Path: "/api/media-items", Category: "Media Items", Title: "List Media Items"},
{Method: "GET", Path: "/api/media-items/{id}", Category: "Media Items", Title: "Get Media Item"},
{Method: "POST", Path: "/api/media-items/search", Category: "Media Items", Title: "Search Media Items"},
// Progress
{Method: "GET", Path: "/api/media-items/{id}/progress", Category: "Progress", Title: "Get Reading Progress"},
{Method: "PUT", Path: "/api/media-items/{id}/progress", Category: "Progress", Title: "Update Reading Progress"},
// Devices
{Method: "POST", Path: "/api/devices/register", Category: "Devices", Title: "Register Device"},
{Method: "GET", Path: "/api/devices", Category: "Devices", Title: "List User Devices"},
{Method: "DELETE", Path: "/api/devices/{id}", Category: "Devices", Title: "Revoke Device"},
}
sort.Slice(endpoints, func(i, j int) bool {
if endpoints[i].Category == endpoints[j].Category {
return endpoints[i].Title < endpoints[j].Title
}
return endpoints[i].Category < endpoints[j].Category
})
return endpoints
}
type APIEndpoint struct {
Method string
Path string
Category string
Title string
}