Improve collection management: fix template selection UI and item positioning

This commit addresses multiple collection management issues to improve user experience:

## Template Selection Modal Improvements
- Replace inline styles with CSS classes for reliable visual feedback
- Fix default template selection conflicts that showed multiple templates as selected
- Add styled template previews that show actual CSS styling differences
- Improve modal responsiveness and visual hierarchy

## Collection Item Creation Fixes
- Fix empty collection items with no content/height that were unclickable
- Preserve template placeholder content during item creation instead of clearing it
- Implement proper positioning system using GetMaxPosition to place new items at collection end
- Add position calculation logic to prevent new items from jumping to beginning

## Backend Positioning System
- Add GetMaxPosition method to all repository implementations (SQLite, PostgreSQL, HTTPClient)
- Update CreateCollectionItemFromTemplate to calculate correct position (maxPos + 1)
- Maintain reconstruction ordering by position ASC for consistent item placement

## Frontend Template Selection
- CSS class-based selection states replace problematic inline style manipulation
- Template previews now render actual HTML with real page styling
- Improved hover states and selection visual feedback
- Fixed auto-selection interference with user interaction

These changes ensure collection items appear in expected order and template selection
provides clear visual feedback with actual styling previews.
This commit is contained in:
2025-10-30 22:06:44 +01:00
parent 00255cb105
commit 900f91bc25
7 changed files with 313 additions and 89 deletions

View File

@@ -204,6 +204,10 @@ func (c *HTTPClient) CreateCollectionItemAtomic(ctx context.Context, siteID, col
return nil, fmt.Errorf("collection operations not implemented in HTTPClient")
}
func (c *HTTPClient) GetMaxPosition(ctx context.Context, siteID, collectionID string) (int, error) {
return 0, fmt.Errorf("collection operations not implemented in HTTPClient")
}
func (c *HTTPClient) UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error) {
return nil, fmt.Errorf("content update operations not implemented in HTTPClient")
}

View File

@@ -282,6 +282,23 @@ func (r *PostgreSQLRepository) CreateCollectionItemAtomic(ctx context.Context, s
return nil, fmt.Errorf("CreateCollectionItemAtomic not yet implemented for PostgreSQL")
}
// GetMaxPosition returns the maximum position for items in a collection
func (r *PostgreSQLRepository) GetMaxPosition(ctx context.Context, siteID, collectionID string) (int, error) {
result, err := r.queries.GetMaxPosition(ctx, postgresql.GetMaxPositionParams{
CollectionID: collectionID,
SiteID: siteID,
})
if err != nil {
return 0, err
}
// Convert interface{} to int (PostgreSQL returns int64)
if maxPos, ok := result.(int64); ok {
return int(maxPos), nil
}
return 0, nil
}
// UpdateContent updates an existing content item
func (r *PostgreSQLRepository) UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error) {
content, err := r.queries.UpdateContent(ctx, postgresql.UpdateContentParams{

View File

@@ -22,6 +22,7 @@ type ContentRepository interface {
CreateCollectionTemplate(ctx context.Context, siteID, collectionID, name, htmlTemplate string, isDefault bool) (*CollectionTemplateItem, error)
CreateCollectionItem(ctx context.Context, siteID, collectionID, itemID string, templateID int, htmlContent string, position int, lastEditedBy string) (*CollectionItemWithTemplate, error)
CreateCollectionItemAtomic(ctx context.Context, siteID, collectionID string, templateID int, lastEditedBy string) (*CollectionItemWithTemplate, error)
GetMaxPosition(ctx context.Context, siteID, collectionID string) (int, error)
ReorderCollectionItems(ctx context.Context, siteID, collectionID string, items []CollectionItemPosition, lastEditedBy string) error
// Transaction support

View File

@@ -287,6 +287,23 @@ func (r *SQLiteRepository) CreateCollectionItemAtomic(ctx context.Context, siteI
return nil, fmt.Errorf("CreateCollectionItemAtomic not yet implemented for SQLite")
}
// GetMaxPosition returns the maximum position for items in a collection
func (r *SQLiteRepository) GetMaxPosition(ctx context.Context, siteID, collectionID string) (int, error) {
result, err := r.queries.GetMaxPosition(ctx, sqlite.GetMaxPositionParams{
CollectionID: collectionID,
SiteID: siteID,
})
if err != nil {
return 0, err
}
// Convert interface{} to int (SQLite returns int64)
if maxPos, ok := result.(int64); ok {
return int(maxPos), nil
}
return 0, nil
}
// UpdateContent updates an existing content item
func (r *SQLiteRepository) UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error) {
content, err := r.queries.UpdateContent(ctx, sqlite.UpdateContentParams{

View File

@@ -246,12 +246,9 @@ func (e *ContentEngine) processChildElementsAsContent(childElement *html.Node, s
// Set the data-content-id attribute
SetAttribute(n, "data-content-id", contentID)
// Clear content - this will be hydrated during reconstruction
for child := n.FirstChild; child != nil; {
next := child.NextSibling
n.RemoveChild(child)
child = next
}
// Keep content for initial display - don't clear it
// The content is already stored in the database and will be available for editing
// Preserving content ensures elements have height and are clickable
}
})
@@ -271,12 +268,8 @@ func (e *ContentEngine) generateStructuralTemplateFromChild(childElement *html.N
// Set the data-content-id attribute
SetAttribute(n, "data-content-id", contentEntries[entryIndex].ID)
// Clear content - this will be hydrated during reconstruction
for child := n.FirstChild; child != nil; {
next := child.NextSibling
n.RemoveChild(child)
child = next
}
// Keep content for structural template - ensures elements have height and are clickable
// Content is stored separately in database for editing
entryIndex++
}
@@ -352,9 +345,17 @@ func (e *ContentEngine) CreateCollectionItemFromTemplate(
return nil, fmt.Errorf("failed to generate structural template: %w", err)
}
// Create collection item with structural template
// Get next position to place new item at the end of collection
maxPosition, err := e.client.GetMaxPosition(context.Background(), siteID, collectionID)
if err != nil {
return nil, fmt.Errorf("failed to get max position for collection %s: %w", collectionID, err)
}
nextPosition := maxPosition + 1
fmt.Printf("🔢 Max position for collection %s: %d, assigning new item position: %d\n", collectionID, maxPosition, nextPosition)
// Create collection item with structural template at end position
collectionItem, err := e.client.CreateCollectionItem(context.Background(),
siteID, collectionID, itemID, templateID, structuralTemplate, 0, lastEditedBy,
siteID, collectionID, itemID, templateID, structuralTemplate, nextPosition, lastEditedBy,
)
if err != nil {
return nil, fmt.Errorf("failed to create collection item: %w", err)