template editor polish

This commit is contained in:
Andrew Brown 2025-07-04 00:27:55 -07:00
parent f9f19386de
commit 64de4eabfd
4 changed files with 126 additions and 23 deletions

View File

@ -13,4 +13,21 @@ module AttributesHelper
# todo: revisit logic for is_disabled -- doesn't disable empty tabs
content_tag(:li, link, class: "tab #{'disabled' if false}")
end
# Helper method to resolve linkable content types for link fields
def get_linkable_content_types(linkable_types_array)
return [] if linkable_types_array.blank?
# Get all available content types
all_content_types = Rails.application.config.content_types[:all]
# Filter to only include the types that are linkable for this field
all_content_types.select do |content_type|
linkable_types_array.include?(content_type.name)
end.compact
rescue => e
# Graceful degradation if there are any issues resolving content types
Rails.logger.warn "Error resolving linkable content types: #{e.message}"
[]
end
end

View File

@ -645,7 +645,21 @@ function handleRemoteFormSubmit(event) {
}
const finalKey = keyParts[keyParts.length - 1];
current[finalKey] = value;
// Handle checkbox arrays (multiple values with same key)
if (current[finalKey] !== undefined) {
// If key already exists, convert to array or append to existing array
if (Array.isArray(current[finalKey])) {
if (value !== '') { // Skip empty values (Rails hidden field)
current[finalKey].push(value);
}
} else {
current[finalKey] = [current[finalKey], value].filter(v => v !== '');
}
} else {
// First occurrence of this key - if it's empty, might be a checkbox array with no selections
current[finalKey] = value === '' && key.includes('[]') ? [] : value;
}
} else {
jsonData[key] = value;
}
@ -985,16 +999,19 @@ window.submitFieldForm = function(event) {
}
const finalKey = keyParts[keyParts.length - 1];
// Handle checkbox arrays (like linkable_types)
if (form.querySelectorAll(`[name="${key}"]`).length > 1) {
if (!current[finalKey]) {
current[finalKey] = [];
}
if (value !== '') {
current[finalKey].push(value);
// Handle checkbox arrays (multiple values with same key)
if (current[finalKey] !== undefined) {
// If key already exists, convert to array or append to existing array
if (Array.isArray(current[finalKey])) {
if (value !== '') { // Skip empty values (Rails hidden field)
current[finalKey].push(value);
}
} else {
current[finalKey] = [current[finalKey], value].filter(v => v !== '');
}
} else {
current[finalKey] = value;
// First occurrence of this key - if it's empty, might be a checkbox array with no selections
current[finalKey] = value === '' && key.includes('[]') ? [] : value;
}
} else {
jsonData[key] = value;
@ -1101,7 +1118,21 @@ window.submitCategoryForm = function(event) {
current = current[part];
}
const finalKey = keyParts[keyParts.length - 1];
current[finalKey] = value;
// Handle checkbox arrays (multiple values with same key)
if (current[finalKey] !== undefined) {
// If key already exists, convert to array or append to existing array
if (Array.isArray(current[finalKey])) {
if (value !== '') { // Skip empty values (Rails hidden field)
current[finalKey].push(value);
}
} else {
current[finalKey] = [current[finalKey], value].filter(v => v !== '');
}
} else {
// First occurrence of this key - if it's empty, might be a checkbox array with no selections
current[finalKey] = value === '' && key.includes('[]') ? [] : value;
}
} else {
jsonData[key] = value;
}
@ -1190,9 +1221,19 @@ function handleFieldFormSuccess(form, response) {
if (!matches) return;
const fieldId = matches[1];
const fieldItem = document.querySelector(`[data-field-id="${fieldId}"]`);
const fieldItem = document.querySelector(`[data-field-id="${fieldId}"]`);
if (!fieldItem) return;
// For field updates, reload the field item with fresh server-rendered HTML
// This ensures all changes (label, linkable_types, visibility, etc.) are reflected
if (response.field && response.html) {
// Replace the existing field item with the updated one
fieldItem.outerHTML = response.html;
console.log(`Updated field "${response.field.label}" UI with server-rendered HTML`);
return;
}
// Fallback: Manual updates if no HTML provided (legacy support)
// Update field display if label changed
if (response.field && response.field.label) {
const labelElement = fieldItem.querySelector('.field-label');

View File

@ -169,22 +169,49 @@
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Link to Content Types</label>
<div class="border border-gray-300 rounded-md shadow-sm p-2 max-h-32 overflow-y-auto">
<div class="flex items-center justify-between mb-2">
<label class="block text-sm font-medium text-gray-700">Link to Content Types</label>
<div class="flex space-x-2">
<button type="button"
onclick="selectAllLinkableTypes()"
class="px-2 py-1 text-xs font-medium text-blue-600 hover:text-blue-700 hover:bg-blue-50 rounded transition-colors">
Select All
</button>
<button type="button"
onclick="selectNoneLinkableTypes()"
class="px-2 py-1 text-xs font-medium text-gray-600 hover:text-gray-700 hover:bg-gray-50 rounded transition-colors">
Select None
</button>
</div>
</div>
<!-- Hidden field to ensure linkable_types parameter is always sent -->
<input type="hidden" name="attribute_field[field_options][linkable_types][]" value="" />
<div class="space-y-2 max-h-48 overflow-y-auto border border-gray-200 rounded-md p-3 bg-gray-50">
<% Rails.application.config.content_types[:all].each do |content_type| %>
<div class="flex items-center mb-1 last:mb-0">
<input type="checkbox" name="attribute_field[field_options][linkable_types][]"
<label class="flex items-center p-2 bg-white rounded-md border border-gray-200 hover:border-gray-300 hover:bg-gray-50 cursor-pointer transition-colors">
<input type="checkbox"
name="attribute_field[field_options][linkable_types][]"
value="<%= content_type.name %>"
id="link_type_<%= content_type.name.downcase %>_<%= category.id %>"
class="h-4 w-4 border-gray-300 rounded focus:ring-2 focus:ring-offset-2"
style="color: <%= content_type_class.hex_color %>; --tw-ring-color: <%= content_type_class.hex_color %>;"
<label for="link_type_<%= content_type.name.downcase %>_<%= category.id %>" class="ml-2 block text-sm text-gray-700">
<i class="material-icons text-xs align-middle mr-1"><%= content_type.icon %></i>
<%= content_type.name.pluralize %>
</label>
</div>
id="link_type_<%= content_type.name.downcase %>_new_<%= category.id %>"
class="h-4 w-4 border-gray-300 rounded focus:ring-2 focus:ring-offset-2 mr-3"
style="color: <%= content_type_class.hex_color %>; --tw-ring-color: <%= content_type_class.hex_color %>;">
<div class="flex items-center flex-grow">
<div class="w-6 h-6 rounded-full flex items-center justify-center mr-2"
style="background-color: <%= content_type.hex_color %>20; color: <%= content_type.hex_color %>;">
<i class="material-icons text-xs"><%= content_type.icon %></i>
</div>
<div class="text-sm font-medium text-gray-900">
<%= content_type.name.pluralize %>
</div>
</div>
</label>
<% end %>
</div>
<p class="text-xs text-gray-500 mt-2">Select which page types users can link to from this field.</p>
</div>
<div class="flex justify-end space-x-2">

View File

@ -38,6 +38,24 @@
<i class="material-icons text-amber-600 text-sm ml-1.5 align-middle">archive</i>
<% end %>
</div>
<!-- Linkable Content Types Visual Indicators (for link fields only) -->
<% if field.field_type == 'link' && field.field_options&.dig('linkable_types')&.any? %>
<div class="flex items-center text-xs text-gray-600 mt-1 mb-1">
<i class="material-icons text-xs mr-1" style="font-size: 14px;">link</i>
<% linkable_types = field.field_options['linkable_types'] || [] %>
<% content_types = get_linkable_content_types(linkable_types) %>
<% content_types.first(5).each do |content_type| %>
<i class="material-icons text-xs mr-0.5 <%= content_type.text_color %>" style="font-size: 14px;" title="<%= content_type.name.pluralize %>">
<%= content_type.icon %>
</i>
<% end %>
<% if content_types.length > 5 %>
<span class="text-xs text-gray-500 ml-1">+<%= content_types.length - 5 %> more</span>
<% end %>
</div>
<% end %>
<div class="text-xs text-gray-500">
<% if field.name_field? %>
Name field