solara 0.4.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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) {
|