solara 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/solara/lib/.DS_Store +0 -0
- data/solara/lib/core/.DS_Store +0 -0
- data/solara/lib/core/brands/brand_onboarder.rb +1 -1
- data/solara/lib/core/brands/brand_switcher.rb +92 -1
- data/solara/lib/core/dashboard/brand/BrandDetail.js +34 -2
- data/solara/lib/core/dashboard/brand/BrandDetailController.js +27 -234
- data/solara/lib/core/dashboard/brand/BrandDetailModel.js +14 -5
- data/solara/lib/core/dashboard/brand/BrandDetailView.js +16 -200
- data/solara/lib/core/dashboard/brand/SectionsFormManager.js +293 -0
- data/solara/lib/core/dashboard/brand/brand.html +223 -174
- data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +2 -5
- data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +36 -133
- data/solara/lib/core/dashboard/brands/Brands.js +31 -0
- data/solara/lib/core/dashboard/brands/BrandsController.js +0 -5
- data/solara/lib/core/dashboard/brands/BrandsView.js +2 -2
- data/solara/lib/core/dashboard/brands/brands.html +71 -52
- data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +6 -6
- data/solara/lib/core/dashboard/component/BrandOptionsBottomSheet.js +4 -4
- data/solara/lib/core/dashboard/component/ConfirmationDialog.js +15 -10
- data/solara/lib/core/dashboard/component/EditJsonSheet.js +160 -0
- data/solara/lib/core/dashboard/component/MessageBottomSheet.js +5 -5
- data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +9 -3
- data/solara/lib/core/dashboard/handler/base_handler.rb +1 -0
- data/solara/lib/core/dashboard/handler/edit_section_handler.rb +1 -5
- data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +0 -15
- data/solara/lib/core/doctor/schema/brand_configurations.json +0 -8
- data/solara/lib/core/doctor/schema/platform/global/resources_manifest.json +30 -0
- data/solara/lib/core/doctor/schema/platform/json_manifest.json +39 -0
- data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +35 -1
- data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +30 -1
- data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +35 -1
- data/solara/lib/core/doctor/validator/template/template_validator.rb +9 -9
- data/solara/lib/core/scripts/brand_config_manager.rb +1 -1
- data/solara/lib/core/scripts/brand_configurations_manager.rb +41 -0
- data/solara/lib/core/scripts/code_generator.rb +342 -118
- data/solara/lib/core/scripts/file_manager.rb +11 -15
- data/solara/lib/core/scripts/file_path.rb +21 -1
- data/solara/lib/core/scripts/gitignore_manager.rb +12 -6
- data/solara/lib/core/scripts/json_manifest_processor.rb +136 -0
- data/solara/lib/core/scripts/platform/ios/infoplist_string_catalog_manager.rb +11 -1
- data/solara/lib/core/scripts/resource_manifest_processor.rb +151 -0
- data/solara/lib/core/scripts/solara_status_manager.rb +1 -1
- data/solara/lib/core/scripts/theme_generator.rb +21 -242
- data/solara/lib/core/solara_configurator.rb +1 -1
- data/solara/lib/core/template/brands/global/resources_manifest.json +10 -0
- data/solara/lib/core/template/brands/json/Json-Manifest.md +59 -0
- data/solara/lib/core/template/brands/json/json_manifest.json +16 -0
- data/solara/lib/core/template/brands/shared/theme.json +213 -29
- data/solara/lib/core/template/config/android_template_config.json +50 -0
- data/solara/lib/core/template/config/flutter_template_config.json +35 -0
- data/solara/lib/core/template/config/ios_template_config.json +50 -0
- data/solara/lib/core/template/configurations.json +46 -0
- data/solara/lib/core/template/project_template_generator.rb +2 -0
- data/solara/lib/solara/version.rb +1 -1
- data/solara/lib/solara.rb +19 -0
- data/solara/lib/solara_manager.rb +21 -13
- metadata +13 -4
- data/solara/lib/core/dashboard/component/AddFieldSheet.js +0 -175
- data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acd7dd411828a00b018a7ead20fc5f21853c45682a7b21f419377385b7e5cbb4
|
4
|
+
data.tar.gz: 187461f358743a466bcd31971622a32091fc4368264f51f73c7ef73b048ae6c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3e85254b364be2888f9e9a760967bf64e4672df8ae4dde907b6f8a8928afa7266ad9a26f1ff35867ae8b7fb2fc362aa0be6f6a70c7aafb3b29e83fa2c456e72
|
7
|
+
data.tar.gz: ee5abac576555d1415bcaa33cec324bf21dcf0519b28eefc1687b6b129b6a74c1f160eb5f2b30f3037facda8ca163e0867c4a7acd8ecdfe2ef2ab88fcf84e330
|
data/solara/lib/.DS_Store
CHANGED
Binary file
|
data/solara/lib/core/.DS_Store
CHANGED
Binary file
|
@@ -30,6 +30,9 @@ class BrandSwitcher
|
|
30
30
|
def switch
|
31
31
|
BrandFontSwitcher.new(@brand_key).switch
|
32
32
|
|
33
|
+
ResourceManifestSwitcher.new(@brand_key, ignore_health_check: @ignore_health_check).switch
|
34
|
+
JsonManifestSwitcher.new(@brand_key).switch
|
35
|
+
|
33
36
|
case @platform
|
34
37
|
when Platform::Flutter
|
35
38
|
IOSBrandSwitcher.new(@brand_key).switch
|
@@ -42,6 +45,94 @@ class BrandSwitcher
|
|
42
45
|
else
|
43
46
|
raise ArgumentError, "Invalid platform: #{@platform}"
|
44
47
|
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class ResourceManifestSwitcher
|
54
|
+
def initialize(brand_key, ignore_health_check:)
|
55
|
+
@brand_key = brand_key
|
56
|
+
@ignore_health_check = ignore_health_check
|
57
|
+
end
|
58
|
+
|
59
|
+
def switch
|
60
|
+
Solara.logger.start_step("Process resource manifest: #{FilePath.resources_manifest}")
|
61
|
+
brand_resource_copier = ResourceManifestProcessor.new(@brand_key, ignore_health_check: @ignore_health_check)
|
62
|
+
brand_resource_copier.copy
|
63
|
+
Solara.logger.debug("#{@brand_key} resources copied successfully according to the manifest: #{FilePath.resources_manifest}.")
|
64
|
+
Solara.logger.end_step("Process resource manifest: #{FilePath.resources_manifest}")
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class JsonManifestSwitcher
|
70
|
+
PLATFORM_CONFIGS = {
|
71
|
+
Platform::Flutter => {
|
72
|
+
language: Language::Dart,
|
73
|
+
output_path: :flutter_lib_artifacts
|
74
|
+
},
|
75
|
+
Platform::IOS => {
|
76
|
+
language: Language::Swift,
|
77
|
+
output_path: :ios_project_root_artifacts
|
78
|
+
},
|
79
|
+
Platform::Android => {
|
80
|
+
language: Language::Kotlin,
|
81
|
+
output_path: :android_project_java_artifacts
|
82
|
+
}
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
def initialize(brand_key)
|
86
|
+
@brand_key = brand_key
|
87
|
+
@brand_manifest_path = FilePath.brand_json_dir(brand_key)
|
88
|
+
@platform = SolaraSettingsManager.instance.platform
|
89
|
+
validate_inputs
|
90
|
+
end
|
91
|
+
|
92
|
+
def switch
|
93
|
+
with_logging do
|
94
|
+
process_manifests
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def validate_inputs
|
101
|
+
raise ArgumentError, "Brand key cannot be nil" if @brand_key.nil?
|
102
|
+
raise ArgumentError, "Invalid platform: #{@platform}" unless PLATFORM_CONFIGS.key?(@platform)
|
103
|
+
raise ArgumentError, "Manifest path not found: #{@brand_manifest_path}" unless File.exist?(@brand_manifest_path)
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_logging
|
107
|
+
log_message = "Process JSON manifest: #{@brand_manifest_path}"
|
108
|
+
Solara.logger.start_step(log_message)
|
109
|
+
yield
|
110
|
+
Solara.logger.end_step(log_message)
|
111
|
+
rescue StandardError => e
|
112
|
+
Solara.logger.error("Failed to process manifest: #{e.message}")
|
113
|
+
raise
|
114
|
+
end
|
115
|
+
|
116
|
+
def process_manifests
|
117
|
+
config = PLATFORM_CONFIGS[@platform]
|
118
|
+
output_path = FilePath.send(config[:output_path])
|
119
|
+
|
120
|
+
[
|
121
|
+
@brand_manifest_path,
|
122
|
+
FilePath.brand_global_json_dir
|
123
|
+
].each do |manifest_path|
|
124
|
+
process_manifest(config[:language], manifest_path, output_path)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def process_manifest(language, manifest_path, output_path)
|
129
|
+
JsonManifestProcessor.new(
|
130
|
+
manifest_path,
|
131
|
+
language,
|
132
|
+
output_path
|
133
|
+
).process
|
134
|
+
rescue StandardError => e
|
135
|
+
raise StandardError, "Failed to process #{manifest_path}: #{e.message}"
|
45
136
|
end
|
46
137
|
end
|
47
138
|
|
@@ -225,7 +316,7 @@ class ThemeSwitcher
|
|
225
316
|
def generate_theme(name, language, platform)
|
226
317
|
Solara.logger.start_step("Generate #{name} for #{platform}")
|
227
318
|
|
228
|
-
theme_manager =
|
319
|
+
theme_manager = ThemeGenerator.new(FilePath.brand_theme(@brand_key))
|
229
320
|
theme_manager.generate(language, FilePath.generated_config(name, platform))
|
230
321
|
|
231
322
|
Solara.logger.end_step("Generate #{name} for #{platform}")
|
@@ -2,6 +2,37 @@ import BrandDetailModel from './BrandDetailModel.js';
|
|
2
2
|
import BrandDetailView from './BrandDetailView.js';
|
3
3
|
import BrandDetailController from './BrandDetailController.js';
|
4
4
|
|
5
|
+
const modeToggle = document.getElementById('modeToggle');
|
6
|
+
const body = document.body;
|
7
|
+
const icon = modeToggle.querySelector('i');
|
8
|
+
|
9
|
+
function applyMode(mode) {
|
10
|
+
if (mode === 'dark') {
|
11
|
+
body.classList.add('dark-mode');
|
12
|
+
icon.classList.remove('fa-sun');
|
13
|
+
icon.classList.add('fa-moon');
|
14
|
+
} else {
|
15
|
+
body.classList.remove('dark-mode');
|
16
|
+
icon.classList.remove('fa-moon');
|
17
|
+
icon.classList.add('fa-sun');
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
const savedMode = localStorage.getItem('mode');
|
22
|
+
if (savedMode) {
|
23
|
+
applyMode(savedMode);
|
24
|
+
} else {
|
25
|
+
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
26
|
+
applyMode(systemPrefersDark ? 'dark' : 'light');
|
27
|
+
}
|
28
|
+
|
29
|
+
modeToggle.addEventListener('click', () => {
|
30
|
+
const currentMode = body.classList.contains('dark-mode') ? 'dark' : 'light';
|
31
|
+
const newMode = currentMode === 'dark' ? 'light' : 'dark';
|
32
|
+
applyMode(newMode);
|
33
|
+
localStorage.setItem('mode', newMode);
|
34
|
+
});
|
35
|
+
|
5
36
|
window.onload = function () {
|
6
37
|
document.getElementById('loadingOverlay').style.display = 'none';
|
7
38
|
};
|
@@ -20,10 +51,11 @@ window.addEventListener('scroll', function () {
|
|
20
51
|
}
|
21
52
|
lastScrollTop = scrollTop;
|
22
53
|
});
|
23
|
-
|
54
|
+
|
24
55
|
document.addEventListener('DOMContentLoaded', async () => {
|
25
56
|
const model = new BrandDetailModel();
|
26
57
|
const view = new BrandDetailView(model);
|
27
58
|
const controller = new BrandDetailController(model, view);
|
28
59
|
await controller.initializeApp();
|
29
|
-
});
|
60
|
+
});
|
61
|
+
|
@@ -1,15 +1,10 @@
|
|
1
1
|
import {DataSource} from './BrandDetailModel.js';
|
2
|
-
import InfoPlistStringCatalogManager from "./InfoPlistStringCatalogManager.js";
|
3
2
|
|
4
3
|
class BrandDetailController {
|
5
4
|
constructor(model, view) {
|
6
5
|
this.model = model;
|
7
6
|
this.view = view;
|
8
|
-
this.onSectionChanged = this.onSectionChanged.bind(this);
|
9
|
-
this.deleteField = this.deleteField.bind(this);
|
10
7
|
this.initializeEventListeners();
|
11
|
-
this.view.setOnSectionChangedHandler(this.onSectionChanged.bind(this));
|
12
|
-
this.view.setOnDeleteFieldHandler(this.deleteField.bind(this));
|
13
8
|
}
|
14
9
|
|
15
10
|
async initializeApp() {
|
@@ -80,7 +75,7 @@ class BrandDetailController {
|
|
80
75
|
|
81
76
|
async addNewBrand() {
|
82
77
|
this.view.showOnboardBrandForm(async (key, name) => {
|
83
|
-
const configurations = await this.model.
|
78
|
+
const configurations = await this.model.createNewBrandConfigurations()
|
84
79
|
await this.addBrand(key, name, configurations)
|
85
80
|
})
|
86
81
|
}
|
@@ -96,11 +91,12 @@ class BrandDetailController {
|
|
96
91
|
const response = await this.model.fetchBrandDetails();
|
97
92
|
await this.onLoadSections(response.result);
|
98
93
|
const {isCurrentBrand, contentChanged} = await this.model.fetchCurrentBrand();
|
94
|
+
this.model.isCurrentBrand = isCurrentBrand
|
99
95
|
|
100
|
-
if (
|
96
|
+
if (isCurrentBrand) {
|
97
|
+
this.view.setupSyncBrandButton(contentChanged ? '#ff4136' : '#4A90E2');
|
98
|
+
} else {
|
101
99
|
this.view.showSwitchButton();
|
102
|
-
} else if (contentChanged) {
|
103
|
-
this.view.showApplyChangesButton();
|
104
100
|
}
|
105
101
|
|
106
102
|
await this.checkBrandHealth();
|
@@ -114,7 +110,7 @@ class BrandDetailController {
|
|
114
110
|
try {
|
115
111
|
this.view.addBrandOverlay.style.display = 'none'
|
116
112
|
this.view.header.style.display = 'flex';
|
117
|
-
this.view.updateAppNameTitle(`${configuraationsResult.brand.
|
113
|
+
this.view.updateAppNameTitle(`${configuraationsResult.brand.name} - ${configuraationsResult.brand.key}`);
|
118
114
|
await this.showSections(configuraationsResult);
|
119
115
|
this.view.showIndex();
|
120
116
|
} catch (error) {
|
@@ -128,7 +124,12 @@ class BrandDetailController {
|
|
128
124
|
window.location.href = `../brands/brands.html?source=${this.model.source}`;
|
129
125
|
});
|
130
126
|
|
131
|
-
document.getElementById('
|
127
|
+
document.getElementById('syncBrandButton').addEventListener(
|
128
|
+
'click',
|
129
|
+
async () => {
|
130
|
+
await this.switchToBrand()
|
131
|
+
await this.view.toast("Synced successfully")
|
132
|
+
});
|
132
133
|
document.getElementById('switchButton').addEventListener('click', () => this.switchToBrand());
|
133
134
|
|
134
135
|
this.view.exportBrandBtn.addEventListener('click', () => this.exportBrand());
|
@@ -141,188 +142,39 @@ class BrandDetailController {
|
|
141
142
|
|
142
143
|
const sectionItems = configuraationsResult.configurations
|
143
144
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
this.createSection(
|
151
|
-
sectionData.key,
|
152
|
-
sectionData,
|
153
|
-
new InfoPlistStringCatalogManager(sectionData.content).extractLocalizations(),
|
154
|
-
sectionData.name,
|
155
|
-
sectionData.inputType)
|
156
|
-
} else {
|
157
|
-
this.createSection(sectionData.key, sectionData, sectionData.content, sectionData.name, sectionData.inputType)
|
158
|
-
}
|
159
|
-
}
|
145
|
+
this.view.sectionsFormManager.display(
|
146
|
+
sectionItems,
|
147
|
+
(section, container) => {
|
148
|
+
this.onSectionChanged(section, container)
|
149
|
+
})
|
150
|
+
|
160
151
|
} catch (error) {
|
161
152
|
console.error('Error loading configurations:', error);
|
162
153
|
alert(error.message);
|
163
154
|
}
|
164
155
|
}
|
165
156
|
|
166
|
-
|
167
|
-
this.
|
168
|
-
sectionData,
|
169
|
-
sectionData.content.colors,
|
170
|
-
'Theme Colors',
|
171
|
-
'color',
|
172
|
-
'colors')
|
173
|
-
this.createSection(`${sectionData.key}_typography`,
|
174
|
-
sectionData,
|
175
|
-
sectionData.content.typography,
|
176
|
-
'Theme Typography',
|
177
|
-
'text',
|
178
|
-
'typography')
|
179
|
-
this.createSection(`${sectionData.key}_spacing`,
|
180
|
-
sectionData,
|
181
|
-
sectionData.content.spacing,
|
182
|
-
'Theme Spacing', 'text',
|
183
|
-
'spacing')
|
184
|
-
this.createSection(
|
185
|
-
`${sectionData.key}_borderRadius`,
|
186
|
-
sectionData,
|
187
|
-
sectionData.content.borderRadius,
|
188
|
-
'Theme Border Radius',
|
189
|
-
'text',
|
190
|
-
'borderRadius')
|
191
|
-
this.createSection(
|
192
|
-
`${sectionData.key}_elevation`,
|
193
|
-
sectionData,
|
194
|
-
sectionData.content.elevation,
|
195
|
-
'Theme Elevation',
|
196
|
-
'text',
|
197
|
-
'elevation')
|
198
|
-
}
|
199
|
-
|
200
|
-
createSection(id, sectionData, content, sectionName, inputType, propertiesGroupName = null) {
|
201
|
-
const sectionElement = this.view.createSection(sectionData.key, sectionName, inputType);
|
202
|
-
sectionElement.id = id;
|
203
|
-
sectionElement.dataset.propertiesGroupName = propertiesGroupName
|
204
|
-
|
205
|
-
this.view.sectionsContainer.appendChild(sectionElement);
|
206
|
-
|
207
|
-
this.view.populateJsonFields(sectionData, sectionElement, content, inputType);
|
157
|
+
async onSectionChanged(section, container) {
|
158
|
+
if (this.model.isRemote()) return
|
208
159
|
|
209
|
-
const addButton = document.createElement('button');
|
210
|
-
addButton.textContent = 'Add Field';
|
211
|
-
addButton.className = 'add-field-btn';
|
212
|
-
addButton.onclick = () => this.addNewField(sectionData, sectionElement, inputType);
|
213
|
-
sectionElement.appendChild(addButton);
|
214
|
-
}
|
215
|
-
|
216
|
-
async onSectionChanged(sectionItem, sectionElement) {
|
217
160
|
try {
|
218
|
-
|
219
|
-
await this.model.saveSection(sectionItem, configuration);
|
220
|
-
this.view.showApplyChangesButton();
|
161
|
+
await this.model.saveSection(container.dataset.key, section.content);
|
221
162
|
await this.checkBrandHealth();
|
163
|
+
if (this.model.isCurrentBrand) {
|
164
|
+
await this.switchToBrand(true)
|
165
|
+
}
|
222
166
|
} catch (error) {
|
223
167
|
console.error('Error saving section:', error);
|
224
168
|
alert(error.message);
|
225
169
|
}
|
226
170
|
}
|
227
171
|
|
228
|
-
|
229
|
-
const data = {};
|
230
|
-
const level = container.dataset.level
|
231
|
-
|
232
|
-
const jsonObjects = container.querySelectorAll(`.json-object-${level}`);
|
233
|
-
jsonObjects.forEach(jsonObject => {
|
234
|
-
const jsonObjectLabel = jsonObject.querySelector('label').textContent;
|
235
|
-
data[jsonObjectLabel] = this.collectJsonData(jsonObject);
|
236
|
-
|
237
|
-
});
|
238
|
-
|
239
|
-
const group = container.querySelectorAll(`.json-array-${level}`);
|
240
|
-
group.forEach(arrayItem => {
|
241
|
-
const label = arrayItem.querySelector('label').textContent;
|
242
|
-
const items = arrayItem.querySelectorAll(`.json-array-item-${level}`);
|
243
|
-
const indexed = arrayItem.querySelectorAll(`.json-array-item-indexed-${level}`).length !== 0;
|
244
|
-
|
245
|
-
data[label] = Array.from(items).map((item, index) => {
|
246
|
-
const values = this.collectJsonData(item)
|
247
|
-
if (indexed) {
|
248
|
-
return values[index]
|
249
|
-
}
|
250
|
-
return values;
|
251
|
-
});
|
252
|
-
});
|
253
|
-
|
254
|
-
const result = this.collectJsonFields(container, level);
|
255
|
-
|
256
|
-
Object.keys(data).forEach(key => {
|
257
|
-
result[key] = data[key];
|
258
|
-
});
|
259
|
-
return result
|
260
|
-
}
|
261
|
-
|
262
|
-
collectJsonFields(container, level) {
|
263
|
-
const result = {};
|
264
|
-
|
265
|
-
const inputFields = container.querySelectorAll(`.json-field-${level}`);
|
266
|
-
inputFields.forEach(inputField => {
|
267
|
-
const values = this.getInputFieldsData(inputField);
|
268
|
-
Object.keys(values).forEach(key => {
|
269
|
-
result[key] = values[key];
|
270
|
-
});
|
271
|
-
})
|
272
|
-
return result
|
273
|
-
}
|
274
|
-
|
275
|
-
getInputFieldsData(container) {
|
276
|
-
const data = {};
|
277
|
-
|
278
|
-
const inputs = container.querySelectorAll('input');
|
279
|
-
inputs.forEach(input => {
|
280
|
-
if (input.type === 'checkbox') {
|
281
|
-
data[input.id] = input.checked;
|
282
|
-
} else {
|
283
|
-
let value = input.value;
|
284
|
-
if (input.type === 'color') {
|
285
|
-
value = `#${value.substring(1).toUpperCase()}`;
|
286
|
-
} else if (!isNaN(value) && value.trim() !== '') {
|
287
|
-
// Convert to number if it's a valid number string
|
288
|
-
value = parseFloat(value);
|
289
|
-
}
|
290
|
-
data[input.id] = value;
|
291
|
-
}
|
292
|
-
});
|
293
|
-
|
294
|
-
return data;
|
295
|
-
}
|
296
|
-
|
297
|
-
addNewField(sectionItem, sectionElement, inputType) {
|
298
|
-
this.view.showAddFieldForm(sectionItem, sectionElement, inputType);
|
299
|
-
}
|
300
|
-
|
301
|
-
deleteField(sectionItem, fieldContainer) {
|
302
|
-
this.view.showConfirmationDialog(
|
303
|
-
'Are you sure you want to delete this item?',
|
304
|
-
() => {
|
305
|
-
const sectionElement = fieldContainer.closest('.section');
|
306
|
-
fieldContainer.remove();
|
307
|
-
this.onSectionChanged(sectionItem, sectionElement);
|
308
|
-
}
|
309
|
-
);
|
310
|
-
}
|
311
|
-
|
312
|
-
getArrayValue(container) {
|
313
|
-
const arrayItems = container.querySelectorAll('.array-item-input');
|
314
|
-
return Array.from(arrayItems).map(item => item.value);
|
315
|
-
}
|
316
|
-
|
317
|
-
async switchToBrand() {
|
172
|
+
async switchToBrand(silentError = false) {
|
318
173
|
try {
|
319
174
|
await this.model.switchToBrand();
|
320
|
-
const applyChangesButton = document.getElementById('applyChangesButton');
|
321
|
-
applyChangesButton.style.display = 'none';
|
322
|
-
location.reload();
|
323
175
|
} catch (error) {
|
324
176
|
console.error('Error switching to brand:', error);
|
325
|
-
alert(error.message);
|
177
|
+
if (!silentError) alert(error.message);
|
326
178
|
}
|
327
179
|
}
|
328
180
|
|
@@ -350,72 +202,13 @@ class BrandDetailController {
|
|
350
202
|
}
|
351
203
|
}
|
352
204
|
|
353
|
-
async getSectionData(sectionKey) {
|
354
|
-
try {
|
355
|
-
const container = this.view.sectionsContainer;
|
356
|
-
|
357
|
-
const sectionElements = Array.from(container.querySelectorAll('.section'))
|
358
|
-
.filter(element => element.dataset.key === sectionKey);
|
359
|
-
|
360
|
-
if (sectionElements.length === 1) {
|
361
|
-
return this.collectJsonData(sectionElements[0])
|
362
|
-
}
|
363
|
-
|
364
|
-
const configurations = sectionElements.map(sectionElement => {
|
365
|
-
const propertiesGroupName = sectionElement.dataset.propertiesGroupName;
|
366
|
-
const config = this.collectJsonData(sectionElement);
|
367
|
-
|
368
|
-
if (propertiesGroupName !== "null") {
|
369
|
-
return {[propertiesGroupName]: config};
|
370
|
-
} else {
|
371
|
-
return config;
|
372
|
-
}
|
373
|
-
});
|
374
|
-
|
375
|
-
return configurations.reduce((acc, config) => {
|
376
|
-
const key = Object.keys(config)[0]; // Get the key from the configuration item
|
377
|
-
acc[key] = config[key]; // Assign the value to the dynamic object
|
378
|
-
return acc;
|
379
|
-
}, {});
|
380
|
-
|
381
|
-
} catch (error) {
|
382
|
-
console.error('Error downloading brand:', error);
|
383
|
-
alert(error.message);
|
384
|
-
}
|
385
|
-
}
|
386
|
-
|
387
205
|
async exportBrand() {
|
388
206
|
try {
|
389
|
-
const
|
207
|
+
const result = this.view.sectionsFormManager.data();
|
390
208
|
|
391
209
|
const brandKey = this.view.sectionsContainer.dataset.brandKey;
|
392
210
|
const brandName = this.view.sectionsContainer.dataset.brandName;
|
393
211
|
|
394
|
-
const sectionElements = Array.from(sectionsContainer.querySelectorAll('.section'));
|
395
|
-
|
396
|
-
const uniqueSections = new Map();
|
397
|
-
|
398
|
-
// The theme section has been divided into multiple categories (e.g., colors, typography).
|
399
|
-
// In the getSectionData function, we merge these sections. To prevent duplication when
|
400
|
-
// processing the sections, we will ensure that each section key is processed only once.
|
401
|
-
await Promise.all(sectionElements.map(async sectionElement => {
|
402
|
-
const key = sectionElement.dataset.key;
|
403
|
-
// Check if the key already exists in the map
|
404
|
-
if (!uniqueSections.has(key)) {
|
405
|
-
const configurations = await this.getSectionData(key);
|
406
|
-
uniqueSections.set(
|
407
|
-
key, {
|
408
|
-
key: key,
|
409
|
-
name: sectionElement.dataset.name,
|
410
|
-
inputType: sectionElement.dataset.inputType,
|
411
|
-
content: configurations
|
412
|
-
});
|
413
|
-
}
|
414
|
-
}));
|
415
|
-
|
416
|
-
// Convert the map values to an array
|
417
|
-
const result = Array.from(uniqueSections.values());
|
418
|
-
|
419
212
|
this.model.exportBrand(brandKey, brandName, result);
|
420
213
|
|
421
214
|
} catch (error) {
|
@@ -10,6 +10,7 @@ class BrandDetailModel {
|
|
10
10
|
constructor() {
|
11
11
|
this.localSource = new BrandLocalSource();
|
12
12
|
this.remoteSource = new BrandRemoteSource();
|
13
|
+
this.isCurrentBrand = false;
|
13
14
|
|
14
15
|
this.brandKey = this.getQueryFromUrl('brand_key');
|
15
16
|
|
@@ -17,6 +18,14 @@ class BrandDetailModel {
|
|
17
18
|
this.source = sourceFromUrl === DataSource.LOCAL ? DataSource.LOCAL : DataSource.REMOTE;
|
18
19
|
}
|
19
20
|
|
21
|
+
isLocal() {
|
22
|
+
return this.source === DataSource.LOCAL
|
23
|
+
}
|
24
|
+
|
25
|
+
isRemote() {
|
26
|
+
return this.source === DataSource.REMOTE
|
27
|
+
}
|
28
|
+
|
20
29
|
getQueryFromUrl(name) {
|
21
30
|
return this.localSource.getQueryFromUrl(name);
|
22
31
|
}
|
@@ -29,10 +38,10 @@ class BrandDetailModel {
|
|
29
38
|
return await this.localSource.fetchCurrentBrand(this.brandKey);
|
30
39
|
}
|
31
40
|
|
32
|
-
async saveSection(
|
41
|
+
async saveSection(key, configuration) {
|
33
42
|
switch (this.source) {
|
34
43
|
case DataSource.LOCAL:
|
35
|
-
return await this.localSource.saveSection(
|
44
|
+
return await this.localSource.saveSection(key, configuration, this.brandKey);
|
36
45
|
case DataSource.REMOTE:
|
37
46
|
// Saving is not supported remotely. Instead, user can export the brand.
|
38
47
|
return
|
@@ -48,7 +57,7 @@ class BrandDetailModel {
|
|
48
57
|
async checkBrandHealth() {
|
49
58
|
switch (this.source) {
|
50
59
|
case DataSource.LOCAL:
|
51
|
-
|
60
|
+
return await this.localSource.checkBrandHealth(this.brandKey);
|
52
61
|
case DataSource.REMOTE:
|
53
62
|
// Checking health is not supported remotely yet.
|
54
63
|
return
|
@@ -57,8 +66,8 @@ class BrandDetailModel {
|
|
57
66
|
}
|
58
67
|
}
|
59
68
|
|
60
|
-
async
|
61
|
-
return await this.remoteSource.
|
69
|
+
async createNewBrandConfigurations() {
|
70
|
+
return await this.remoteSource.createNewBrandConfigurations();
|
62
71
|
}
|
63
72
|
|
64
73
|
async createBrandConfigurationsFromDirectory(dirHandle) {
|