Complete tasks 4.1-4.2: Page management service and HTTP endpoints
- Implemented PageService with full CRUD operations - Added GetPages, CreatePage, UpdatePage, DeletePage, ReorderPages methods - Cascade deletion of widgets when page is deleted - Prevention of last page deletion - Created page HTTP endpoints (GET, POST, PUT, DELETE, reorder) - HTMX-friendly HTML fragment responses - Comprehensive unit tests for service and handlers - Updated dashboard to use PageService and create default pages
This commit is contained in:
10
templates/partials/page-tab.html
Normal file
10
templates/partials/page-tab.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{{define "page-tab.html"}}
|
||||
<button hx-get="/pages/{{.Page.ID}}"
|
||||
hx-target="#widget-grid"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="false"
|
||||
data-page-id="{{.Page.ID}}"
|
||||
class="page-tab px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800 border-b-2 border-transparent hover:border-gray-300 transition-colors">
|
||||
{{.Page.Name}}
|
||||
</button>
|
||||
{{end}}
|
||||
21
templates/partials/page-tabs.html
Normal file
21
templates/partials/page-tabs.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{{define "page-tabs.html"}}
|
||||
{{range .Pages}}
|
||||
<button hx-get="/pages/{{.ID}}"
|
||||
hx-target="#widget-grid"
|
||||
hx-swap="innerHTML"
|
||||
hx-push-url="false"
|
||||
data-page-id="{{.ID}}"
|
||||
class="page-tab px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800 border-b-2 border-transparent hover:border-gray-300 transition-colors">
|
||||
{{.Name}}
|
||||
</button>
|
||||
{{end}}
|
||||
<button hx-get="/pages/new-form"
|
||||
hx-target="#page-form-modal"
|
||||
hx-swap="innerHTML"
|
||||
class="px-4 py-2 text-sm font-medium text-gray-400 hover:text-gray-600 transition-colors"
|
||||
title="Add new page">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
</button>
|
||||
{{end}}
|
||||
34
templates/partials/widget-grid.html
Normal file
34
templates/partials/widget-grid.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{{define "widget-grid.html"}}
|
||||
{{if .Widgets}}
|
||||
{{range .Widgets}}
|
||||
<div class="widget bg-white rounded-lg shadow-sm border border-gray-200 p-4" data-widget-id="{{.ID}}">
|
||||
<div class="widget-handle cursor-move mb-2 flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
{{if .Title}}{{.Title}}{{else}}{{.Type}} Widget{{end}}
|
||||
</h3>
|
||||
<button hx-delete="/widgets/{{.ID}}"
|
||||
hx-target="closest .widget"
|
||||
hx-swap="outerHTML swap:1s"
|
||||
hx-confirm="Are you sure you want to delete this widget?"
|
||||
class="text-gray-400 hover:text-red-600 transition-colors">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="widget-content">
|
||||
<!-- Widget-specific content will be loaded here -->
|
||||
<p class="text-gray-500 text-sm">{{.Type}} widget content</p>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="col-span-full text-center text-gray-500 py-12">
|
||||
<svg class="w-16 h-16 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/>
|
||||
</svg>
|
||||
<p class="text-lg font-medium mb-2">No widgets yet</p>
|
||||
<p class="text-sm">Click the + button to add your first widget</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user