solara 0.4.0 → 0.5.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_switcher.rb +58 -1
- data/solara/lib/core/dashboard/brand/BrandDetail.js +34 -2
- data/solara/lib/core/dashboard/brand/BrandDetailController.js +23 -233
- data/solara/lib/core/dashboard/brand/BrandDetailModel.js +13 -5
- data/solara/lib/core/dashboard/brand/BrandDetailView.js +16 -200
- data/solara/lib/core/dashboard/brand/SectionsFormManager.js +232 -0
- data/solara/lib/core/dashboard/brand/brand.html +187 -177
- 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 +5 -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/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 +57 -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_path.rb +21 -1
- data/solara/lib/core/scripts/gitignore_manager.rb +11 -3
- data/solara/lib/core/scripts/json_manifest_processor.rb +95 -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 +61 -0
- data/solara/lib/core/template/brands/json/json_manifest.json +18 -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
- 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: 714797cd507e354e49c4a1981a516f6ad3e659797ba40b6040c7e7a6f30f11aa
|
4
|
+
data.tar.gz: 3b3d4ed6c116abea9dd15c2db4b5df308320649be4381623e3c7a3c863ef0385
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4c46c3f893d95eddbde530e7d83fe340ce36698974880073b3517779bb8e39b31813863c13f7743e821936b814e6d7f5d42337c10b3ef37a4cd6767732f5a20
|
7
|
+
data.tar.gz: 750e7851fa9584bb85b00b5f01e48ea246892914f1bbfafeb7a2b18db8e138d0da5f8fd2a2253434bed1dd6b6d9b0c6911dcaec908714e96ff69c894653490b8
|
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).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,60 @@ 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)
|
55
|
+
@brand_key = brand_key
|
56
|
+
end
|
57
|
+
|
58
|
+
def switch
|
59
|
+
Solara.logger.start_step("Process resource manifest: #{FilePath.resources_manifest}")
|
60
|
+
brand_resource_copier = ResourceManifestProcessor.new(@brand_key)
|
61
|
+
brand_resource_copier.copy
|
62
|
+
Solara.logger.debug("#{@brand_key} resources copied successfully according to the manifest: #{FilePath.resources_manifest}.")
|
63
|
+
Solara.logger.end_step("Process resource manifest: #{FilePath.resources_manifest}")
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
class JsonManifestSwitcher
|
69
|
+
def initialize(brand_key)
|
70
|
+
@brand_key = brand_key
|
71
|
+
@manifest_path = FilePath.brand_json_dir(brand_key)
|
72
|
+
end
|
73
|
+
|
74
|
+
def switch
|
75
|
+
Solara.logger.start_step("Process JSON manifest: #{@manifest_path}")
|
76
|
+
|
77
|
+
|
78
|
+
case SolaraSettingsManager.instance.platform
|
79
|
+
when Platform::Flutter
|
80
|
+
process_maifest(Language::Dart, FilePath.flutter_lib_artifacts)
|
81
|
+
process_maifest(Language::Dart, FilePath.brand_global_json_dir)
|
82
|
+
when Platform::IOS
|
83
|
+
process_maifest(Language::Swift, FilePath.ios_project_root_artifacts)
|
84
|
+
process_maifest(Language::Swift, FilePath.brand_global_json_dir)
|
85
|
+
when Platform::Android
|
86
|
+
process_maifest(Language::Kotlin, FilePath.android_project_java_artifacts )
|
87
|
+
process_maifest(Language::Kotlin, FilePath.brand_global_json_dir)
|
88
|
+
else
|
89
|
+
raise ArgumentError, "Invalid platform: #{@platform}"
|
90
|
+
end
|
91
|
+
|
92
|
+
Solara.logger.end_step("Process JSON manifest: #{@manifest_path}")
|
93
|
+
end
|
94
|
+
|
95
|
+
def process_maifest(language, output_path)
|
96
|
+
processor = JsonManifestProcessor.new(
|
97
|
+
@manifest_path,
|
98
|
+
language,
|
99
|
+
output_path
|
100
|
+
)
|
101
|
+
processor.process
|
45
102
|
end
|
46
103
|
end
|
47
104
|
|
@@ -225,7 +282,7 @@ class ThemeSwitcher
|
|
225
282
|
def generate_theme(name, language, platform)
|
226
283
|
Solara.logger.start_step("Generate #{name} for #{platform}")
|
227
284
|
|
228
|
-
theme_manager =
|
285
|
+
theme_manager = ThemeGenerator.new(FilePath.brand_theme(@brand_key))
|
229
286
|
theme_manager.generate(language, FilePath.generated_config(name, platform))
|
230
287
|
|
231
288
|
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
|
}
|
@@ -97,10 +92,10 @@ class BrandDetailController {
|
|
97
92
|
await this.onLoadSections(response.result);
|
98
93
|
const {isCurrentBrand, contentChanged} = await this.model.fetchCurrentBrand();
|
99
94
|
|
100
|
-
if (
|
95
|
+
if (isCurrentBrand) {
|
96
|
+
this.view.setupSyncBrandButton(contentChanged ? '#ff4136' : '#4A90E2');
|
97
|
+
} else {
|
101
98
|
this.view.showSwitchButton();
|
102
|
-
} else if (contentChanged) {
|
103
|
-
this.view.showApplyChangesButton();
|
104
99
|
}
|
105
100
|
|
106
101
|
await this.checkBrandHealth();
|
@@ -128,7 +123,12 @@ class BrandDetailController {
|
|
128
123
|
window.location.href = `../brands/brands.html?source=${this.model.source}`;
|
129
124
|
});
|
130
125
|
|
131
|
-
document.getElementById('
|
126
|
+
document.getElementById('syncBrandButton').addEventListener(
|
127
|
+
'click',
|
128
|
+
async () => {
|
129
|
+
await this.switchToBrand()
|
130
|
+
await this.view.toast("Synced successfully")
|
131
|
+
});
|
132
132
|
document.getElementById('switchButton').addEventListener('click', () => this.switchToBrand());
|
133
133
|
|
134
134
|
this.view.exportBrandBtn.addEventListener('click', () => this.exportBrand());
|
@@ -141,188 +141,37 @@ class BrandDetailController {
|
|
141
141
|
|
142
142
|
const sectionItems = configuraationsResult.configurations
|
143
143
|
|
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
|
-
}
|
144
|
+
this.view.sectionsFormManager.display(
|
145
|
+
sectionItems,
|
146
|
+
(section, container) => {
|
147
|
+
this.onSectionChanged(section, container)
|
148
|
+
})
|
149
|
+
|
160
150
|
} catch (error) {
|
161
151
|
console.error('Error loading configurations:', error);
|
162
152
|
alert(error.message);
|
163
153
|
}
|
164
154
|
}
|
165
155
|
|
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);
|
156
|
+
async onSectionChanged(section, container) {
|
157
|
+
if (this.model.isRemote()) return
|
208
158
|
|
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
159
|
try {
|
218
|
-
|
219
|
-
await this.model.saveSection(sectionItem, configuration);
|
220
|
-
this.view.showApplyChangesButton();
|
160
|
+
await this.model.saveSection(container.dataset.key, section.content);
|
221
161
|
await this.checkBrandHealth();
|
162
|
+
await this.switchToBrand(true)
|
222
163
|
} catch (error) {
|
223
164
|
console.error('Error saving section:', error);
|
224
165
|
alert(error.message);
|
225
166
|
}
|
226
167
|
}
|
227
168
|
|
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() {
|
169
|
+
async switchToBrand(silentError = false) {
|
318
170
|
try {
|
319
171
|
await this.model.switchToBrand();
|
320
|
-
const applyChangesButton = document.getElementById('applyChangesButton');
|
321
|
-
applyChangesButton.style.display = 'none';
|
322
|
-
location.reload();
|
323
172
|
} catch (error) {
|
324
173
|
console.error('Error switching to brand:', error);
|
325
|
-
alert(error.message);
|
174
|
+
if (!silentError) alert(error.message);
|
326
175
|
}
|
327
176
|
}
|
328
177
|
|
@@ -350,72 +199,13 @@ class BrandDetailController {
|
|
350
199
|
}
|
351
200
|
}
|
352
201
|
|
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
202
|
async exportBrand() {
|
388
203
|
try {
|
389
|
-
const
|
204
|
+
const result = this.view.sectionsFormManager.data();
|
390
205
|
|
391
206
|
const brandKey = this.view.sectionsContainer.dataset.brandKey;
|
392
207
|
const brandName = this.view.sectionsContainer.dataset.brandName;
|
393
208
|
|
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
209
|
this.model.exportBrand(brandKey, brandName, result);
|
420
210
|
|
421
211
|
} catch (error) {
|
@@ -17,6 +17,14 @@ class BrandDetailModel {
|
|
17
17
|
this.source = sourceFromUrl === DataSource.LOCAL ? DataSource.LOCAL : DataSource.REMOTE;
|
18
18
|
}
|
19
19
|
|
20
|
+
isLocal() {
|
21
|
+
return this.source === DataSource.LOCAL
|
22
|
+
}
|
23
|
+
|
24
|
+
isRemote() {
|
25
|
+
return this.source === DataSource.REMOTE
|
26
|
+
}
|
27
|
+
|
20
28
|
getQueryFromUrl(name) {
|
21
29
|
return this.localSource.getQueryFromUrl(name);
|
22
30
|
}
|
@@ -29,10 +37,10 @@ class BrandDetailModel {
|
|
29
37
|
return await this.localSource.fetchCurrentBrand(this.brandKey);
|
30
38
|
}
|
31
39
|
|
32
|
-
async saveSection(
|
40
|
+
async saveSection(key, configuration) {
|
33
41
|
switch (this.source) {
|
34
42
|
case DataSource.LOCAL:
|
35
|
-
return await this.localSource.saveSection(
|
43
|
+
return await this.localSource.saveSection(key, configuration, this.brandKey);
|
36
44
|
case DataSource.REMOTE:
|
37
45
|
// Saving is not supported remotely. Instead, user can export the brand.
|
38
46
|
return
|
@@ -48,7 +56,7 @@ class BrandDetailModel {
|
|
48
56
|
async checkBrandHealth() {
|
49
57
|
switch (this.source) {
|
50
58
|
case DataSource.LOCAL:
|
51
|
-
|
59
|
+
return await this.localSource.checkBrandHealth(this.brandKey);
|
52
60
|
case DataSource.REMOTE:
|
53
61
|
// Checking health is not supported remotely yet.
|
54
62
|
return
|
@@ -57,8 +65,8 @@ class BrandDetailModel {
|
|
57
65
|
}
|
58
66
|
}
|
59
67
|
|
60
|
-
async
|
61
|
-
return await this.remoteSource.
|
68
|
+
async createNewBrandConfigurations() {
|
69
|
+
return await this.remoteSource.createNewBrandConfigurations();
|
62
70
|
}
|
63
71
|
|
64
72
|
async createBrandConfigurationsFromDirectory(dirHandle) {
|