|
1 | 1 | // customize.templ renders the Campaign Customization Hub -- a centralized page |
2 | 2 | // for campaign owners to control dashboards, categories, page templates, |
3 | | -// sidebar navigation, and campaign extensions. |
| 3 | +// sidebar navigation, and visual appearance. |
4 | 4 | // |
5 | 5 | // Tabs: |
6 | | -// 1. Dashboard — Campaign dashboard layout editor (drag-and-drop blocks). |
7 | | -// 2. Categories — Per-category settings: identity, attributes, and category |
8 | | -// dashboard. Content lazy-loaded via HTMX from the entities |
9 | | -// plugin (one fragment per selected category). |
10 | | -// 3. Page Templates — Per-category page template editor (template-editor widget, |
11 | | -// lazy-loaded via HTMX). |
12 | | -// 4. Extensions — Enable/disable campaign addons (modules, widgets, |
13 | | -// integrations). Lazy-loaded via HTMX from the addons plugin. |
14 | | -// 5. Navigation — Sidebar ordering/visibility + custom sections & links. |
| 6 | +// 1. Dashboard — Campaign dashboard layout editor (drag-and-drop blocks). |
| 7 | +// 2. Categories — Per-category settings: identity, attributes, and category |
| 8 | +// dashboard. Content lazy-loaded via HTMX from the entities |
| 9 | +// plugin (one fragment per selected category). |
| 10 | +// 3. Page Templates — Per-category page template editor (template-editor widget, |
| 11 | +// lazy-loaded via HTMX). |
| 12 | +// 4. Navigation — Sidebar ordering/visibility + custom sections & links. |
| 13 | +// 5. Content Templates — Manage content templates for entity creation. |
| 14 | +// 6. Appearance — Branding, topbar, accent color, and backdrop customization. |
| 15 | +// |
| 16 | +// Features/addons are managed on the dedicated Plugin Hub page (/campaigns/:id/plugins). |
15 | 17 | // |
16 | 18 | // Route: GET /campaigns/:id/customize |
17 | 19 | // Permission: RoleOwner only. |
@@ -41,7 +43,7 @@ templ CustomizePage(cc *CampaignContext, entityTypes []SettingsEntityType, csrfT |
41 | 43 | </a> |
42 | 44 | <div> |
43 | 45 | <h1 class="text-base font-semibold text-fg">Campaign Customization</h1> |
44 | | - <p class="text-[11px] text-fg-secondary">Dashboards, categories, templates, navigation, and features</p> |
| 46 | + <p class="text-[11px] text-fg-secondary">Dashboards, categories, templates, navigation, and appearance</p> |
45 | 47 | </div> |
46 | 48 | </div> |
47 | 49 | </div> |
@@ -90,14 +92,7 @@ templ CustomizePage(cc *CampaignContext, entityTypes []SettingsEntityType, csrfT |
90 | 92 | > |
91 | 93 | <i class="fa-solid fa-palette mr-1.5 text-xs"></i> Appearance |
92 | 94 | </button> |
93 | | - <button |
94 | | - class="px-4 py-2.5 text-sm font-medium border-b-2 transition-colors whitespace-nowrap" |
95 | | - :class="tab === 'extensions' ? 'text-accent border-accent' : 'text-fg-secondary border-transparent hover:text-fg hover:border-edge'" |
96 | | - @click="tab = 'extensions'" |
97 | | - > |
98 | | - <i class="fa-solid fa-plug mr-1.5 text-xs"></i> Features & Packs |
99 | | - </button> |
100 | | - </div> |
| 95 | + </div> |
101 | 96 |
|
102 | 97 | <!-- Tab content --> |
103 | 98 | <div class="flex-1 overflow-hidden"> |
@@ -139,13 +134,7 @@ templ CustomizePage(cc *CampaignContext, entityTypes []SettingsEntityType, csrfT |
139 | 134 | </div> |
140 | 135 | </div> |
141 | 136 |
|
142 | | - <!-- Extensions tab: enable/disable campaign addons + attributes editor --> |
143 | | - <div x-show="tab === 'extensions'" x-cloak class="h-full overflow-y-auto"> |
144 | | - <div class="max-w-3xl mx-auto px-6 py-4"> |
145 | | - @extensionsTab(cc, entityTypes, csrfToken) |
146 | | - </div> |
147 | 137 | </div> |
148 | | - </div> |
149 | 138 | </div> |
150 | 139 | } |
151 | 140 | } |
@@ -234,6 +223,52 @@ templ categoriesTab(cc *CampaignContext, entityTypes []SettingsEntityType, csrfT |
234 | 223 | <p class="text-sm text-fg-secondary">Select a category above to configure it.</p> |
235 | 224 | </div> |
236 | 225 | </div> |
| 226 | + |
| 227 | + <!-- Attributes field editor (per-category) --> |
| 228 | + <div class="border-t border-edge pt-6 mt-6"> |
| 229 | + <div class="flex items-center gap-2 mb-1"> |
| 230 | + <i class="fa-solid fa-sliders text-sm text-accent"></i> |
| 231 | + <h3 class="text-sm font-semibold text-fg">Attributes</h3> |
| 232 | + </div> |
| 233 | + <p class="text-xs text-fg-secondary mb-3"> |
| 234 | + Define the custom fields that appear on entity pages for each category. |
| 235 | + </p> |
| 236 | + |
| 237 | + <!-- Category selector for attributes editor --> |
| 238 | + <div class="flex flex-wrap gap-2 mb-3" x-data="{ attrCat: 0 }"> |
| 239 | + for _, et := range entityTypes { |
| 240 | + <button |
| 241 | + type="button" |
| 242 | + class="flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors text-sm" |
| 243 | + :class={ fmt.Sprintf("attrCat === %d ? 'border-accent bg-accent/10 text-accent font-medium' : 'border-edge bg-surface hover:border-accent/30 text-fg-secondary'", et.ID) } |
| 244 | + @click={ fmt.Sprintf("attrCat = %d", et.ID) } |
| 245 | + hx-get={ fmt.Sprintf("/campaigns/%s/entity-types/%d/attributes-fragment", cc.Campaign.ID, et.ID) } |
| 246 | + hx-target="#attributes-editor-container" |
| 247 | + hx-swap="innerHTML" |
| 248 | + > |
| 249 | + <span |
| 250 | + class="w-6 h-6 rounded flex items-center justify-center text-[10px] shrink-0" |
| 251 | + style={ fmt.Sprintf("background-color: %s20; color: %s", et.Color, et.Color) } |
| 252 | + > |
| 253 | + if et.Icon != "" { |
| 254 | + <i class={ "fa-solid " + et.Icon }></i> |
| 255 | + } else { |
| 256 | + <i class="fa-solid fa-file"></i> |
| 257 | + } |
| 258 | + </span> |
| 259 | + { et.NamePlural } |
| 260 | + </button> |
| 261 | + } |
| 262 | + </div> |
| 263 | + |
| 264 | + <!-- Lazy-loaded attributes editor container --> |
| 265 | + <div id="attributes-editor-container"> |
| 266 | + <div class="card p-4 text-center text-sm text-fg-secondary"> |
| 267 | + <i class="fa-solid fa-hand-pointer text-lg text-fg-muted mb-2"></i> |
| 268 | + <p>Select a category above to configure its attributes.</p> |
| 269 | + </div> |
| 270 | + </div> |
| 271 | + </div> |
237 | 272 | } |
238 | 273 | </div> |
239 | 274 | } |
@@ -344,132 +379,6 @@ templ navigationTab(cc *CampaignContext, entityTypes []SettingsEntityType, csrfT |
344 | 379 | </div> |
345 | 380 | } |
346 | 381 |
|
347 | | -// extensionsTab renders the addon/extension management interface. The addon |
348 | | -// list is lazy-loaded via HTMX from the addons plugin fragment endpoint. |
349 | | -// Also includes the Attributes field editor (per-category), which is managed |
350 | | -// as a formal addon. |
351 | | -templ extensionsTab(cc *CampaignContext, entityTypes []SettingsEntityType, csrfToken string) { |
352 | | - <div class="space-y-6"> |
353 | | - <div> |
354 | | - <h2 class="text-base font-semibold text-fg mb-0.5">Features & Content Packs</h2> |
355 | | - <p class="text-xs text-fg-secondary"> |
356 | | - Enable or disable features and content packs for this campaign. Disabling does not delete your data. |
357 | | - </p> |
358 | | - </div> |
359 | | - |
360 | | - <!-- Addon toggle list (lazy-loaded from addons plugin) --> |
361 | | - <div |
362 | | - id="addons-list" |
363 | | - hx-get={ fmt.Sprintf("/campaigns/%s/addons/fragment", cc.Campaign.ID) } |
364 | | - hx-trigger="intersect once" |
365 | | - hx-swap="innerHTML" |
366 | | - > |
367 | | - <div class="text-sm text-fg-secondary text-center py-6"> |
368 | | - <i class="fa-solid fa-spinner fa-spin mr-1.5"></i> Loading extensions... |
369 | | - </div> |
370 | | - </div> |
371 | | - |
372 | | - <!-- Content extensions (lazy-loaded from extensions handler) --> |
373 | | - <div |
374 | | - hx-get={ fmt.Sprintf("/campaigns/%s/extensions", cc.Campaign.ID) } |
375 | | - hx-trigger="intersect once" |
376 | | - hx-swap="innerHTML" |
377 | | - > |
378 | | - <div class="text-sm text-fg-secondary text-center py-4"> |
379 | | - <i class="fa-solid fa-spinner fa-spin mr-1.5"></i> Loading content packs... |
380 | | - </div> |
381 | | - </div> |
382 | | - |
383 | | - <!-- Calendar display settings (shown when calendar addon exists) --> |
384 | | - <div class="border-t border-edge pt-6"> |
385 | | - <div class="flex items-center gap-2 mb-1"> |
386 | | - <i class="fa-solid fa-calendar-days text-sm text-accent"></i> |
387 | | - <h3 class="text-sm font-semibold text-fg">Calendar</h3> |
388 | | - </div> |
389 | | - <p class="text-xs text-fg-secondary mb-3"> |
390 | | - Add a calendar block to any of the three layout editors below. Each editor is available in its own tab on this page. |
391 | | - </p> |
392 | | - <div class="grid grid-cols-1 sm:grid-cols-3 gap-3"> |
393 | | - <div class="card p-3"> |
394 | | - <div class="flex items-center gap-2 mb-1"> |
395 | | - <i class="fa-solid fa-gauge text-xs text-fg-muted"></i> |
396 | | - <span class="text-sm font-medium text-fg">Dashboard</span> |
397 | | - </div> |
398 | | - <p class="text-xs text-fg-secondary"> |
399 | | - Add a "Calendar" block in the <strong>Dashboard</strong> tab to show upcoming events on your campaign home page. |
400 | | - </p> |
401 | | - </div> |
402 | | - <div class="card p-3"> |
403 | | - <div class="flex items-center gap-2 mb-1"> |
404 | | - <i class="fa-solid fa-layer-group text-xs text-fg-muted"></i> |
405 | | - <span class="text-sm font-medium text-fg">Category Pages</span> |
406 | | - </div> |
407 | | - <p class="text-xs text-fg-secondary"> |
408 | | - Add a "Calendar" block in the <strong>Categories</strong> tab to show upcoming events on category dashboards. |
409 | | - </p> |
410 | | - </div> |
411 | | - <div class="card p-3"> |
412 | | - <div class="flex items-center gap-2 mb-1"> |
413 | | - <i class="fa-solid fa-file-lines text-xs text-fg-muted"></i> |
414 | | - <span class="text-sm font-medium text-fg">Entity Pages</span> |
415 | | - </div> |
416 | | - <p class="text-xs text-fg-secondary"> |
417 | | - Add a "Calendar" block in the <strong>Page Templates</strong> tab, or events linked to an entity appear automatically at the bottom. |
418 | | - </p> |
419 | | - </div> |
420 | | - </div> |
421 | | - </div> |
422 | | - |
423 | | - <!-- Attributes field editor (per-category) --> |
424 | | - if len(entityTypes) > 0 { |
425 | | - <div class="border-t border-edge pt-6"> |
426 | | - <div class="flex items-center gap-2 mb-1"> |
427 | | - <i class="fa-solid fa-sliders text-sm text-accent"></i> |
428 | | - <h3 class="text-sm font-semibold text-fg">Attributes</h3> |
429 | | - </div> |
430 | | - <p class="text-xs text-fg-secondary mb-3"> |
431 | | - Define the custom fields that appear on entity pages for each category. |
432 | | - Toggle the Attributes extension above to show or hide them. |
433 | | - </p> |
434 | | - |
435 | | - <!-- Category selector for attributes editor --> |
436 | | - <div class="flex flex-wrap gap-2 mb-3" x-data="{ attrCat: 0 }"> |
437 | | - for _, et := range entityTypes { |
438 | | - <button |
439 | | - type="button" |
440 | | - class="flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors text-sm" |
441 | | - :class={ fmt.Sprintf("attrCat === %d ? 'border-accent bg-accent/10 text-accent font-medium' : 'border-edge bg-surface hover:border-accent/30 text-fg-secondary'", et.ID) } |
442 | | - @click={ fmt.Sprintf("attrCat = %d", et.ID) } |
443 | | - hx-get={ fmt.Sprintf("/campaigns/%s/entity-types/%d/attributes-fragment", cc.Campaign.ID, et.ID) } |
444 | | - hx-target="#attributes-editor-container" |
445 | | - hx-swap="innerHTML" |
446 | | - > |
447 | | - <span |
448 | | - class="w-6 h-6 rounded flex items-center justify-center text-[10px] shrink-0" |
449 | | - style={ fmt.Sprintf("background-color: %s20; color: %s", et.Color, et.Color) } |
450 | | - > |
451 | | - if et.Icon != "" { |
452 | | - <i class={ "fa-solid " + et.Icon }></i> |
453 | | - } else { |
454 | | - <i class="fa-solid fa-file"></i> |
455 | | - } |
456 | | - </span> |
457 | | - { et.NamePlural } |
458 | | - </button> |
459 | | - } |
460 | | - </div> |
461 | | - |
462 | | - <!-- Lazy-loaded attributes editor container --> |
463 | | - <div id="attributes-editor-container"> |
464 | | - <div class="card p-4 text-center text-sm text-fg-secondary"> |
465 | | - <i class="fa-solid fa-hand-pointer text-lg text-fg-muted mb-2"></i> |
466 | | - <p>Select a category above to configure its attributes.</p> |
467 | | - </div> |
468 | | - </div> |
469 | | - </div> |
470 | | - } |
471 | | - </div> |
472 | | -} |
473 | 382 |
|
474 | 383 | // LayoutEditorFragment renders the template-editor widget mount for a single |
475 | 384 | // entity type. Returned as an HTMX fragment in the Page Templates tab. Includes |
|
0 commit comments