Fancified the template selector

This commit is contained in:
Nathan Schneider
2025-05-12 22:44:24 -06:00
parent 41aeffa81b
commit 8aabd66666
3 changed files with 279 additions and 131 deletions

View File

@ -738,25 +738,65 @@ input:checked + .toggle-slider:before {
}
.protocol-template-selector {
padding: 2rem;
border-bottom: 1px solid var(--border-color);
background-color: rgba(0, 0, 0, 0.02);
}
.protocol-template-selector label {
display: block;
margin-bottom: 0.75rem;
font-weight: 500;
font-size: 1.1rem;
.protocol-template-selector .stage-header {
background-color: rgba(0, 0, 0, 0.02);
}
.protocol-template-select {
width: 100%;
padding: 0.75rem;
.template-body {
padding: 0 2rem 2rem;
}
.template-options {
display: flex;
flex-direction: column;
gap: 1rem;
padding-top: 1rem;
}
.template-option {
border: 1px solid var(--border-color);
font-family: inherit;
background-color: var(--light-color);
margin-bottom: 1rem;
padding: 1.5rem;
background-color: white;
border-radius: 4px;
transition: border-color 0.3s, box-shadow 0.3s;
cursor: pointer;
position: relative;
}
.template-option:hover {
border-color: #aaa;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.template-option.selected {
border-color: var(--dark-color);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.template-select-btn {
display: block;
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 0.75rem;
color: var(--dark-color);
background: none;
border: none;
padding: 0;
text-align: left;
cursor: inherit;
}
/* Remove the underline effect */
.template-description {
font-size: 0.95rem;
color: var(--secondary-color);
margin: 0;
line-height: 1.4;
}
.protocol-metadata {
@ -773,11 +813,11 @@ input:checked + .toggle-slider:before {
margin-bottom: 0;
}
.metadata-field label {
display: block;
margin-bottom: 0.75rem;
font-weight: 500;
font-size: 1.1rem;
.metadata-field h2 {
font-size: 1.4rem;
margin: 0 0 0.75rem 0;
color: var(--dark-color);
font-weight: 600;
}
.metadata-field input[type="text"] {
@ -786,6 +826,8 @@ input:checked + .toggle-slider:before {
border: 1px solid var(--border-color);
font-family: inherit;
background-color: var(--light-color);
font-size: 1.25rem; /* Larger font size for community name */
font-weight: 500;
}
.metadata-field textarea {

View File

@ -82,6 +82,7 @@ document.addEventListener('DOMContentLoaded', function() {
/* Hide template selector */
.protocol-template-selector { display: none !important; }
.template-body { display: none !important; }
/* Expand all sections */
.stage-body { display: block !important; }
@ -235,13 +236,8 @@ document.addEventListener('DOMContentLoaded', function() {
// Populate the template selector
populateTemplateSelector(templates);
// Add template selection event handler
if (protocolTemplateSelect) {
protocolTemplateSelect.addEventListener('change', handleTemplateSelection);
console.log('Template selector event handler attached');
} else {
console.error('Template select element not found');
}
// Template selection is now handled by buttons in the template options
console.log('Template selection will be handled by buttons in the template options');
} else {
console.error('Templates not available after loading script');
}
@ -261,13 +257,8 @@ document.addEventListener('DOMContentLoaded', function() {
// Populate the template selector
populateTemplateSelector(templates);
// Add template selection event handler
if (protocolTemplateSelect) {
protocolTemplateSelect.addEventListener('change', handleTemplateSelection);
console.log('Template selector event handler attached');
} else {
console.error('Template select element not found');
}
// Template selection is now handled by buttons in the template options
console.log('Template selection will be handled by buttons in the template options');
})
.catch(importError => {
console.error('ES module import also failed:', importError);
@ -279,25 +270,47 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
// Function to populate the template selector dropdown
// Function to populate the template selector with cards
function populateTemplateSelector(templatesList) {
if (!protocolTemplateSelect || !templatesList || templatesList.length === 0) {
console.error('Cannot populate template selector - missing element or templates');
if (!templatesList || templatesList.length === 0) {
console.error('Cannot populate template selector - missing templates');
return;
}
console.log('Populating template selector with', templatesList.length, 'templates');
// Clear all existing options
while (protocolTemplateSelect.options.length > 0) {
protocolTemplateSelect.remove(0);
// Find the template options container
const templateOptionsContainer = document.querySelector('.template-options');
if (!templateOptionsContainer) {
console.error('Template options container not found');
return;
}
// Add the default "Create Your Own" option
const defaultOption = document.createElement('option');
defaultOption.value = "";
defaultOption.textContent = "-- Create Your Own Protocol --";
protocolTemplateSelect.appendChild(defaultOption);
// Clear existing template options
templateOptionsContainer.innerHTML = '';
// Create the "Create Your Own" option first
const createYourOwnOption = document.createElement('div');
createYourOwnOption.className = 'template-option';
createYourOwnOption.setAttribute('data-template-id', '');
const createYourOwnBtn = document.createElement('button');
createYourOwnBtn.className = 'template-select-btn';
createYourOwnBtn.textContent = 'Create Your Own Protocol';
createYourOwnBtn.setAttribute('type', 'button');
const createYourOwnDesc = document.createElement('p');
createYourOwnDesc.className = 'template-description';
createYourOwnDesc.textContent = 'Start with a blank protocol and build it from scratch.';
createYourOwnOption.appendChild(createYourOwnBtn);
createYourOwnOption.appendChild(createYourOwnDesc);
createYourOwnOption.addEventListener('click', function() {
console.log('Create your own option clicked');
clearAllFields();
});
templateOptionsContainer.appendChild(createYourOwnOption);
// Verify templates have required properties
let validTemplateCount = 0;
@ -310,67 +323,143 @@ document.addEventListener('DOMContentLoaded', function() {
});
console.log(`Found ${validTemplateCount} valid templates out of ${templatesList.length} total`);
// Add template options
// Add template options as cards
templatesList.forEach(template => {
const option = document.createElement('option');
option.value = template.id;
option.textContent = template.title;
protocolTemplateSelect.appendChild(option);
console.log('Added template option:', template.title, 'with ID:', template.id);
if (!template.id || !template.title || !template.description) {
return; // Skip invalid templates
}
// Debugging template structure
const templateOption = document.createElement('div');
templateOption.className = 'template-option';
templateOption.setAttribute('data-template-id', template.id);
// Make the entire card clickable
templateOption.addEventListener('click', function() {
// For debugging
console.log('Template option clicked for:', template.id);
// Find and apply the template
const selectedTemplate = templates.find(t => t.id === template.id);
if (selectedTemplate) {
applyTemplate(selectedTemplate);
// Close the template section after selection
const templateSection = document.querySelector('.protocol-template-selector');
if (templateSection) {
const templateBody = templateSection.querySelector('.template-body');
const toggleBtn = templateSection.querySelector('.toggle-btn');
if (templateBody && toggleBtn) {
templateBody.style.display = 'none';
toggleBtn.textContent = '+';
}
}
} else {
console.error('Template not found:', template.id);
}
});
const selectButton = document.createElement('button');
selectButton.className = 'template-select-btn';
selectButton.textContent = template.title;
selectButton.setAttribute('type', 'button');
const description = document.createElement('p');
description.className = 'template-description';
description.textContent = template.description;
templateOption.appendChild(selectButton);
templateOption.appendChild(description);
templateOptionsContainer.appendChild(templateOption);
console.log('Added template option:', template.title, 'with ID:', template.id);
console.log(' > Description:', template.description ? template.description.substring(0, 50) + '...' : 'MISSING');
console.log(' > Has data:', template.data ? 'Yes' : 'No');
console.log(' > Has stages:', template.data?.stages ? 'Yes - ' + Object.keys(template.data.stages).length + ' stages' : 'No');
});
// We've already set up the click handler for "Create Your Own" when creating it
}
// Handle template selection
function handleTemplateSelection() {
const selectedTemplateId = this.value;
console.log('Template selection changed to:', selectedTemplateId);
// Function to apply a template by ID
function applyTemplateById(templateId) {
console.log('Applying template by ID:', templateId);
if (selectedTemplateId) {
if (templateId) {
// Find the selected template from our loaded templates
const selectedTemplate = templates.find(t => t.id === selectedTemplateId);
const selectedTemplate = templates.find(t => t.id === templateId);
if (selectedTemplate) {
console.log('Found template:', selectedTemplate.title);
applyTemplate(selectedTemplate);
} else {
console.error('Template not found:', selectedTemplateId);
}
} else {
// Clear all fields if "Create Your Own" is selected
document.querySelectorAll('textarea').forEach(textarea => {
textarea.value = '';
});
// Reset protocol data
protocol = {
metadata: {
communityName: "",
summary: ""
},
stages: {}
};
// Collapse all sections
stageContents.forEach(content => {
content.style.display = 'none';
const toggleBtn = content.parentElement.querySelector('.toggle-btn');
if (toggleBtn) {
toggleBtn.textContent = '+';
// Close the template section after selection
const templateSection = document.querySelector('.protocol-template-selector');
if (templateSection) {
const templateBody = templateSection.querySelector('.template-body');
const toggleBtn = templateSection.querySelector('.toggle-btn');
if (templateBody && toggleBtn) {
templateBody.style.display = 'none';
toggleBtn.textContent = '+';
}
}
});
// Update preview mode if active
if (previewModeActive) {
markComponentsWithContent();
} else {
console.error('Template not found:', templateId);
}
}
}
// Function to clear all fields
function clearAllFields() {
// Clear all textareas
document.querySelectorAll('textarea').forEach(textarea => {
textarea.value = '';
});
// Clear community name input
if (communityNameInput) {
communityNameInput.value = '';
}
// Clear protocol summary
if (protocolSummaryTextarea) {
protocolSummaryTextarea.value = '';
}
// Reset protocol data
protocol = {
metadata: {
communityName: "",
summary: ""
},
stages: {}
};
// Collapse all sections
stageContents.forEach(content => {
content.style.display = 'none';
const toggleBtn = content.parentElement.querySelector('.toggle-btn');
if (toggleBtn) {
toggleBtn.textContent = '+';
}
});
// Close the template section after clearing
const templateSection = document.querySelector('.protocol-template-selector');
if (templateSection) {
const templateBody = templateSection.querySelector('.template-body');
const toggleBtn = templateSection.querySelector('.toggle-btn');
if (templateBody && toggleBtn) {
templateBody.style.display = 'none';
toggleBtn.textContent = '+';
}
}
// Update preview mode if active
if (previewModeActive) {
markComponentsWithContent();
}
console.log('All fields cleared');
}
// Function to apply a template to the form
function applyTemplate(selectedTemplate) {
if (!selectedTemplate) {
@ -498,7 +587,6 @@ document.addEventListener('DOMContentLoaded', function() {
const moduleSelects = document.querySelectorAll('.module-select');
console.log('Found module selects:', moduleSelects.length);
const protocolTemplateSelect = document.getElementById('protocol-template');
const communityNameInput = document.getElementById('community-name');
const protocolSummaryTextarea = document.getElementById('protocol-summary');
@ -509,27 +597,10 @@ document.addEventListener('DOMContentLoaded', function() {
const importJsonInput = document.getElementById('import-json');
const importBtn = document.getElementById('import-btn');
// Function to initialize the template selector
// This function is no longer needed with the new template UI
// Keeping an empty function to avoid errors if it's called elsewhere
function initializeTemplateSelector(templatesList) {
if (!protocolTemplateSelect || !templatesList || templatesList.length === 0) {
return;
}
// Clear existing options
while (protocolTemplateSelect.options.length > 1) {
protocolTemplateSelect.remove(1);
}
// Add template options
templatesList.forEach(template => {
const option = document.createElement('option');
option.value = template.id;
option.textContent = template.title;
protocolTemplateSelect.appendChild(option);
});
// Add template selection event handler
protocolTemplateSelect.addEventListener('change', handleTemplateSelection);
console.log('initializeTemplateSelector is deprecated, using new UI instead');
}
// Hide module selectors since we're using templates directly
@ -931,15 +1002,18 @@ document.addEventListener('DOMContentLoaded', function() {
}
}
// If the imported protocol has template information, select that template
if (protocol.templateId && protocolTemplateSelect) {
protocolTemplateSelect.value = protocol.templateId;
// Update template description
if (protocol.templateDescription && templateDescription) {
templateDescription.textContent = protocol.templateDescription;
templateDescription.style.display = 'block';
}
// If the imported protocol has template information, highlight the template
if (protocol.templateId) {
// Highlight the selected template option
const templateOptions = document.querySelectorAll('.template-option');
templateOptions.forEach(option => {
const templateId = option.getAttribute('data-template-id');
if (templateId === protocol.templateId) {
option.classList.add('selected');
} else {
option.classList.remove('selected');
}
});
// Expand all sections
stageContents.forEach(content => {
@ -950,14 +1024,10 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
} else {
// If no template, reset the template selector
if (protocolTemplateSelect) {
protocolTemplateSelect.value = '';
}
if (templateDescription) {
templateDescription.textContent = '';
templateDescription.style.display = 'none';
}
// If no template, remove any highlights
document.querySelectorAll('.template-option').forEach(option => {
option.classList.remove('selected');
});
}
// Update preview mode if active

View File

@ -7,22 +7,32 @@
<div class="builder-main">
<div class="protocol-metadata">
<div class="metadata-field">
<label for="community-name">Community Name:</label>
<h2>Community Name</h2>
<input type="text" id="community-name" name="community-name" placeholder="Enter your community name...">
</div>
<div class="metadata-field">
<label for="protocol-summary">Summary:</label>
<h2>Summary</h2>
<textarea id="protocol-summary" name="protocol-summary" placeholder="Briefly describe this dispute resolution protocol and its purpose..."></textarea>
</div>
</div>
<div class="protocol-template-selector">
<label for="protocol-template">Select a Protocol Template:</label>
<select id="protocol-template" class="protocol-template-select">
<option value="">-- Create Your Own Protocol --</option>
<!-- Template options will be populated by JavaScript -->
</select>
<div class="stage-header" id="template-header">
<div class="stage-header-content">
<h2>Select a Template</h2>
</div>
<button type="button" class="toggle-btn" aria-label="Toggle section" onclick="toggleTemplateSection(this)">+</button>
</div>
<div class="template-body" style="display: none;">
<div class="template-options">
<div class="template-option" data-template-id="">
<button class="template-select-btn">Create Your Own Protocol</button>
<p class="template-description">Start with a blank protocol and build it from scratch.</p>
</div>
<!-- Template options will be populated by JavaScript -->
</div>
</div>
</div>
<div class="builder-content">
@ -154,6 +164,22 @@ function toggleStage(button) {
event.stopPropagation();
}
function toggleTemplateSection(button) {
const section = button.closest('.protocol-template-selector');
const body = section.querySelector('.template-body');
if (body.style.display === 'block') {
body.style.display = 'none';
button.textContent = '+';
} else {
body.style.display = 'block';
button.textContent = '-';
}
// Prevent event from propagating
event.stopPropagation();
}
// Make headers also toggle sections
document.addEventListener('DOMContentLoaded', function() {
const headers = document.querySelectorAll('.stage-header-content');
@ -164,6 +190,16 @@ document.addEventListener('DOMContentLoaded', function() {
toggleButton.click();
});
});
// Make template header also toggle section
const templateHeader = document.querySelector('#template-header .stage-header-content');
if (templateHeader) {
templateHeader.addEventListener('click', function() {
const header = this.closest('#template-header');
const toggleButton = header.querySelector('.toggle-btn');
toggleButton.click();
});
}
// Textarea auto-resizing is now handled by the script in head.html