diff --git a/solara/lib/core/dashboard/brand/BrandController.js b/solara/lib/core/dashboard/brand/BrandController.js index c63b942..5ede47a 100644 --- a/solara/lib/core/dashboard/brand/BrandController.js +++ b/solara/lib/core/dashboard/brand/BrandController.js @@ -77,19 +77,11 @@ class BrandController { const sectionInfo = sectionItems[i]; console.log('Processing section:', i, sectionInfo); - const sectionElement = this.view.createSection(sectionInfo); - - this.view.sectionsContainer.appendChild(sectionElement); - - console.log('Section element created:', sectionElement); - - this.view.populateSection(sectionInfo, sectionElement, sectionInfo.content, sectionInfo.name, sectionInfo.type, sectionInfo.inputType); - - const addButton = document.createElement('button'); - addButton.textContent = 'Add Field'; - addButton.className = 'add-field-btn'; - addButton.onclick = () => this.addNewField(sectionInfo, sectionElement, sectionInfo.name, sectionInfo.type, sectionInfo.inputType); - sectionElement.appendChild(addButton); + if (sectionInfo.key === 'theme') { + this.createThemeSections(sectionInfo) + } else { + this.createSection(sectionInfo, sectionInfo.content, sectionInfo.name, sectionInfo.inputType) + } } } catch (error) { console.error('Error loading configurations:', error); @@ -97,10 +89,34 @@ class BrandController { } } - async onSectionChanged(sectionItem, sectionElement, sectionName, sectionType) { - console.log('onSectionChanged called with:', this); + createThemeSections(sectionInfo) { + this.createSection(sectionInfo, sectionInfo.content.colors, 'Theme Colors', 'color', 'colors') + this.createSection(sectionInfo, sectionInfo.content.typography, 'Theme Typography', 'text', 'typography') + this.createSection(sectionInfo, sectionInfo.content.spacing, 'Theme Spacing', 'text', 'spacing') + this.createSection(sectionInfo, sectionInfo.content.borderRadius, 'Theme Border Radius', 'text', 'borderRadius') + this.createSection(sectionInfo, sectionInfo.content.elevation, 'Theme Elevation', 'text', 'elevation') + } + + createSection(sectionInfo, content, sectionName, inputType, propertiesGroupName = null) { + const sectionElement = this.view.createSection(sectionInfo.key, sectionName, inputType); + sectionElement.dataset.propertiesGroupName = propertiesGroupName + + this.view.sectionsContainer.appendChild(sectionElement); + + console.log('Section element created:', sectionElement); + + this.view.populateSection(sectionInfo, sectionElement, content, sectionName, inputType); + + const addButton = document.createElement('button'); + addButton.textContent = 'Add Field'; + addButton.className = 'add-field-btn'; + addButton.onclick = () => this.addNewField(sectionInfo, sectionElement, sectionInfo.name, sectionInfo.inputType); + sectionElement.appendChild(addButton); + } + + async onSectionChanged(sectionItem, sectionElement) { try { - const configuration = this.getSectionConfiguration(sectionElement); + const configuration = await this.getSectionData(sectionElement.dataset.key) await this.model.saveSection(sectionItem, configuration); this.view.showApplyChangesButton(); await this.checkBrandHealth(); @@ -131,6 +147,9 @@ class BrandController { let value = input.value; if (input.type === 'color') { value = `#${value.substring(1).toUpperCase()}`; + } else if (!isNaN(value) && value.trim() !== '') { + // Convert to number if it's a valid number string + value = parseFloat(value); } current[lastKey] = value; } @@ -138,17 +157,17 @@ class BrandController { return configuration; } - addNewField(sectionItem, sectionElement, sectionName, sectionType, inputType) { - this.view.showAddFieldForm(sectionItem, sectionElement, sectionName, sectionType, inputType); + addNewField(sectionItem, sectionElement, sectionName, inputType) { + this.view.showAddFieldForm(sectionItem, sectionElement, inputType); } - deleteField(sectionItem, fieldContainer, sectionName, sectionType) { + deleteField(sectionItem, fieldContainer) { this.view.showConfirmationDialog( 'Are you sure you want to delete this item?', () => { const sectionElement = fieldContainer.closest('.section'); fieldContainer.remove(); - this.onSectionChanged(sectionItem, sectionElement, sectionName, sectionType); + this.onSectionChanged(sectionItem, sectionElement); } ); } @@ -194,6 +213,44 @@ class BrandController { } } + async getSectionData(sectionKey) { + try { + const container = this.view.sectionsContainer; + + const sectionElements = Array.from(container.querySelectorAll('.section')) + .filter(element => element.dataset.key === sectionKey); + + if (sectionElements.length === 1) { + return this.getSectionConfiguration(sectionElements[0]) + } + + const configurations = sectionElements.map(sectionElement => { + const propertiesGroupName = sectionElement.dataset.propertiesGroupName; + const config = this.getSectionConfiguration(sectionElement); + + if (propertiesGroupName !== "null") { + return {[propertiesGroupName]: config}; + } else { + return config; + } + }); + + const result = configurations.reduce((acc, config) => { + const key = Object.keys(config)[0]; // Get the key from the configuration item + acc[key] = config[key]; // Assign the value to the dynamic object + return acc; + }, {}); + + const json = JSON.stringify(result, null, 2); // Pretty-printing JSON + console.log(json); + return result; + + } catch (error) { + console.error('Error downloading brand:', error); + alert(error.message); + } + } + async downloadBrand() { try { const sectionsContainer = this.view.sectionsContainer; diff --git a/solara/lib/core/dashboard/brand/BrandView.js b/solara/lib/core/dashboard/brand/BrandView.js index 8346a86..433bff9 100644 --- a/solara/lib/core/dashboard/brand/BrandView.js +++ b/solara/lib/core/dashboard/brand/BrandView.js @@ -133,7 +133,7 @@ class BrandView { "brandName": "", "applicationId": "", "versionName": "1.0.0", - "versionCode": "1", + "versionCode": 1, "sourceSets": [] } }, @@ -278,24 +278,24 @@ class BrandView { } } - createSection(sectionInfo) { + createSection(key, name, inputType) { const section = document.createElement('div'); section.className = 'section'; - section.dataset.key = sectionInfo.key - section.dataset.name = sectionInfo.name - section.dataset.inputType = sectionInfo.inputType + section.dataset.key = key + section.dataset.name = name + section.dataset.inputType = inputType const titleContainer = document.createElement('div'); titleContainer.className = 'section-title-container'; const title = document.createElement('h2'); - title.textContent = sectionInfo.name; + title.textContent = name; titleContainer.appendChild(title); const subtitleElement = document.createElement('p'); subtitleElement.className = 'section-subtitle'; - subtitleElement.textContent = `${sectionInfo.key}.json`; + subtitleElement.textContent = `${key}.json`; titleContainer.appendChild(subtitleElement); section.appendChild(titleContainer); @@ -303,23 +303,21 @@ class BrandView { return section; } - populateSection(sectionItem, sectionElement, sectionData, sectionName, sectionType, inputType) { -// TODO: Data should be sent correctly from server toa void getting colors with this workaround - let data = inputType === "color" ? sectionData.colors : sectionData; - for (const [key, value] of Object.entries(data)) { + populateSection(sectionItem, sectionElement, content, sectionName, inputType) { + for (const [key, value] of Object.entries(content)) { if (Array.isArray(value)) { - sectionElement.appendChild(this.createInputField(sectionItem, key, value, 'array', sectionName, sectionType)); + sectionElement.appendChild(this.createInputField(sectionItem, key, value, 'array', sectionName)); } else if (typeof value === 'object' && value !== null) { for (const [subKey, subValue] of Object.entries(value)) { - sectionElement.appendChild(this.createInputField(sectionItem, `${key}.${subKey}`, subValue, inputType, sectionName, sectionType)); + sectionElement.appendChild(this.createInputField(sectionItem, `${key}.${subKey}`, subValue, inputType, sectionName)); } } else { - sectionElement.appendChild(this.createInputField(sectionItem, key, value, inputType, sectionName, sectionType)); + sectionElement.appendChild(this.createInputField(sectionItem, key, value, inputType, sectionName)); } } } - createInputField(sectionItem, key, value, inputType, section, type) { + createInputField(sectionItem, key, value, inputType, section) { const container = document.createElement('div'); container.className = 'input-group'; const label = document.createElement('label'); @@ -354,12 +352,12 @@ class BrandView { if (Array.isArray(value)) { value.forEach(item => { - this.addArrayItem(sectionItem, arrayItemsContainer, item, section, type); + this.addArrayItem(sectionItem, arrayItemsContainer, item, section); }); } addButton.addEventListener('click', () => { - this.addArrayItem(sectionItem, arrayItemsContainer, input.value.trim(), section, type); + this.addArrayItem(sectionItem, arrayItemsContainer, input.value.trim(), section); input.value = ''; }); @@ -380,17 +378,17 @@ class BrandView { const deleteIcon = document.createElement('span'); deleteIcon.className = 'delete-icon'; deleteIcon.textContent = '×'; - deleteIcon.onclick = () => this.onDeleteField(sectionItem, container, section, type); + deleteIcon.onclick = () => this.onDeleteField(sectionItem, container, section); inputWrapper.appendChild(deleteIcon); container.appendChild(inputWrapper); - container.addEventListener('change', () => this.onSectionChanged(sectionItem, container.closest('.section'), section, type)); + container.addEventListener('change', () => this.onSectionChanged(sectionItem, container.closest('.section'), section)); return container; } - addArrayItem(sectionItem, container, value, section, type) { + addArrayItem(sectionItem, container, value, section) { const itemContainer = document.createElement('div'); itemContainer.classList.add('array-item'); @@ -404,17 +402,17 @@ class BrandView { deleteButton.textContent = '×'; deleteButton.addEventListener('click', () => { itemContainer.remove(); - this.onSectionChanged(sectionItem, container.closest('.section'), section, type); + this.onSectionChanged(sectionItem, container.closest('.section'), section); }); itemContainer.appendChild(itemInput); itemContainer.appendChild(deleteButton); container.appendChild(itemContainer); - this.onSectionChanged(sectionItem, container.closest('.section'), section, type); + this.onSectionChanged(sectionItem, container.closest('.section'), section); } - showAddFieldForm(sectionItem, sectionElement, sectionName, sectionType, inputType) { + showAddFieldForm(sectionItem, sectionElement, sectionName, inputType) { const fieldName = document.getElementById('fieldName'); const fieldValue = document.getElementById('fieldValue'); const colorPicker = document.getElementById('colorPicker'); @@ -447,9 +445,9 @@ class BrandView { defaultValue = '#' + defaultValue; } - const newField = this.createInputField(sectionItem, fieldName, defaultValue, inputType, sectionName, sectionType); + const newField = this.createInputField(sectionItem, fieldName, defaultValue, inputType, sectionName); sectionElement.insertBefore(newField, sectionElement.lastElementChild); - this.onSectionChanged(sectionItem, sectionElement, sectionName, sectionType); + this.onSectionChanged(sectionItem, sectionElement); this.hideAddFieldForm(); }; diff --git a/solara/lib/core/dashboard/handler/edit_section_handler.rb b/solara/lib/core/dashboard/handler/edit_section_handler.rb index 68c2988..34f2402 100644 --- a/solara/lib/core/dashboard/handler/edit_section_handler.rb +++ b/solara/lib/core/dashboard/handler/edit_section_handler.rb @@ -32,11 +32,7 @@ def update_section(key, data, brand_key) path = template[:path] if File.exist?(path) - if key == 'theme' - update_theme(path, data) - else - File.write(path, JSON.pretty_generate(data)) - end + File.write(path, JSON.pretty_generate(data)) Solara.logger.debug("Updated Config for #{path}: #{data}") else raise "Config file not found: #{path}" diff --git a/solara/lib/core/doctor/schema/platform/android/android_config.json b/solara/lib/core/doctor/schema/platform/android/android_config.json index 7c3ee5a..98aad6d 100644 --- a/solara/lib/core/doctor/schema/platform/android/android_config.json +++ b/solara/lib/core/doctor/schema/platform/android/android_config.json @@ -17,7 +17,7 @@ "type": "string" }, "versionCode": { - "type": "string" + "type": "number" } } } \ No newline at end of file diff --git a/solara/lib/core/doctor/schema/platform/shared/theme.json b/solara/lib/core/doctor/schema/platform/shared/theme.json index 3c7839a..1f6ceb7 100644 --- a/solara/lib/core/doctor/schema/platform/shared/theme.json +++ b/solara/lib/core/doctor/schema/platform/shared/theme.json @@ -1,18 +1,11 @@ { "type": "object", "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, "colors": { "type": "object", "properties": { }, - "required": [ - ] + "required": [] }, "typography": { "type": "object", @@ -20,21 +13,14 @@ "fontFamily": { "type": "object", "properties": { - "regular": { "type": "string" }, - "medium": { "type": "string" }, - "bold": { "type": "string" } }, - "required": ["regular", "medium", "bold"] + "required": [] }, "fontSize": { "type": "object", "properties": { - "small": { "type": "number" }, - "medium": { "type": "number" }, - "large": { "type": "number" }, - "extraLarge": { "type": "number" } }, - "required": ["small", "medium", "large", "extraLarge"] + "required": [] } }, "required": ["fontFamily", "fontSize"] @@ -42,32 +28,21 @@ "spacing": { "type": "object", "properties": { - "small": { "type": "number" }, - "medium": { "type": "number" }, - "large": { "type": "number" }, - "extraLarge": { "type": "number" } }, - "required": ["small", "medium", "large", "extraLarge"] + "required": [] }, "borderRadius": { "type": "object", "properties": { - "small": { "type": "number" }, - "medium": { "type": "number" }, - "large": { "type": "number" } }, - "required": ["small", "medium", "large"] + "required": [] }, "elevation": { "type": "object", "properties": { - "none": { "type": "number" }, - "low": { "type": "number" }, - "medium": { "type": "number" }, - "high": { "type": "number" } }, - "required": ["none", "low", "medium", "high"] + "required": [] } }, - "required": ["name", "version", "colors", "typography", "spacing", "borderRadius", "elevation"] + "required": ["colors", "typography", "spacing", "borderRadius", "elevation"] } \ No newline at end of file diff --git a/solara/lib/core/template/brands/android/android_config.json b/solara/lib/core/template/brands/android/android_config.json index 54f58fe..f1c4ea6 100644 --- a/solara/lib/core/template/brands/android/android_config.json +++ b/solara/lib/core/template/brands/android/android_config.json @@ -2,7 +2,7 @@ "brandName": "", "applicationId": "", "versionName": "1.0.0", - "versionCode": "1", + "versionCode": 1, "sourceSets": [ ] } \ No newline at end of file