solara 0.2.0 → 0.2.2

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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/solara/lib/core/.DS_Store +0 -0
  3. data/solara/lib/core/aliases/alias_generator.rb +1 -0
  4. data/solara/lib/core/aliases/alias_generator_manager.rb +2 -1
  5. data/solara/lib/core/aliases/solara_terminal_setup.rb +1 -1
  6. data/solara/lib/core/brands/brand_font_switcher.rb +154 -0
  7. data/solara/lib/core/brands/brand_onboarder.rb +8 -6
  8. data/solara/lib/core/brands/brand_switcher.rb +232 -166
  9. data/solara/lib/core/brands/brands_manager.rb +15 -39
  10. data/solara/lib/core/dashboard/brand/BrandDetail.js +19 -0
  11. data/solara/lib/core/dashboard/brand/BrandDetailController.js +50 -8
  12. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +9 -30
  13. data/solara/lib/core/dashboard/brand/BrandDetailView.js +49 -3
  14. data/solara/lib/core/dashboard/brand/InfoPlistStringCatalogManager.js +19 -0
  15. data/solara/lib/core/dashboard/brand/brand.html +209 -62
  16. data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +1 -1
  17. data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +38 -53
  18. data/solara/lib/core/dashboard/brands/BrandsController.js +6 -5
  19. data/solara/lib/core/dashboard/brands/BrandsModel.js +2 -2
  20. data/solara/lib/core/dashboard/brands/BrandsView.js +2 -2
  21. data/solara/lib/core/dashboard/brands/brands.html +3 -1
  22. data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +7 -5
  23. data/solara/lib/core/dashboard/dashboard_manager.rb +2 -0
  24. data/solara/lib/core/dashboard/dashboard_server.rb +13 -8
  25. data/solara/lib/core/dashboard/handler/brand_alisases_handler.rb +4 -11
  26. data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +11 -11
  27. data/solara/lib/core/dashboard/handler/{brand_configurations_handler.rb → brand_details_handler.rb} +4 -4
  28. data/solara/lib/core/dashboard/handler/brands_handler.rb +1 -1
  29. data/solara/lib/core/dashboard/handler/edit_section_handler.rb +8 -8
  30. data/solara/lib/core/doctor/brand_doctor.rb +31 -31
  31. data/solara/lib/core/doctor/doctor_manager.rb +0 -1
  32. data/solara/lib/core/doctor/project_doctor.rb +0 -1
  33. data/solara/lib/core/doctor/schema/platform/android/android_config.json +0 -4
  34. data/solara/lib/core/doctor/schema/platform/ios/InfoPlist.xcstrings +15 -0
  35. data/solara/lib/core/doctor/schema/platform/ios/ios_config.json +0 -5
  36. data/solara/lib/core/doctor/schema/platform/shared/brand_config.json +9 -0
  37. data/solara/lib/core/doctor/schema/platform/shared/theme.json +94 -9
  38. data/solara/lib/core/doctor/validator/brand_settings_validator.rb +6 -0
  39. data/solara/lib/core/doctor/validator/brand_settings_validator_manager.rb +9 -21
  40. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +22 -4
  41. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +22 -6
  42. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +22 -4
  43. data/solara/lib/core/scripts/brand_config_generator.rb +1 -0
  44. data/solara/lib/core/scripts/brand_config_manager.rb +2 -4
  45. data/solara/lib/core/scripts/brand_exporter.rb +1 -1
  46. data/solara/lib/core/scripts/brand_importer.rb +2 -3
  47. data/solara/lib/core/scripts/brand_offboarder.rb +5 -0
  48. data/solara/lib/core/scripts/brand_resources_manager.rb +127 -54
  49. data/solara/lib/core/scripts/directory_creator.rb +2 -2
  50. data/solara/lib/core/scripts/file_manager.rb +53 -19
  51. data/solara/lib/core/scripts/file_path.rb +175 -30
  52. data/solara/lib/core/scripts/folder_copier.rb +3 -7
  53. data/solara/lib/core/scripts/gitignore_manager.rb +21 -10
  54. data/solara/lib/core/scripts/interactive_file_system_validator.rb +8 -2
  55. data/solara/lib/core/scripts/platform/android/android_manifest_switcher.rb +3 -3
  56. data/solara/lib/core/scripts/platform/android/android_strings_switcher.rb +26 -24
  57. data/solara/lib/core/scripts/platform/android/gradle_switcher.rb +7 -6
  58. data/solara/lib/core/scripts/platform/ios/infoplist_string_catalog_manager.rb +123 -0
  59. data/solara/lib/core/scripts/platform/ios/infoplist_switcher.rb +59 -0
  60. data/solara/lib/core/scripts/platform/ios/ios_plist_manager.rb +11 -20
  61. data/solara/lib/core/scripts/platform/ios/plist_font_manager.rb +33 -0
  62. data/solara/lib/core/scripts/platform/ios/xcconfig_generator.rb +15 -3
  63. data/solara/lib/core/scripts/platform/ios/xcode_asset_manager.rb +2 -3
  64. data/solara/lib/core/scripts/platform/ios/xcode_project_manager.rb +80 -1
  65. data/solara/lib/core/scripts/platform/ios/xcode_project_switcher.rb +15 -34
  66. data/solara/lib/core/scripts/solara_logger.rb +10 -0
  67. data/solara/lib/core/scripts/solara_settings_manager.rb +26 -1
  68. data/solara/lib/core/scripts/strings_xml_manager.rb +17 -2
  69. data/solara/lib/core/scripts/theme_generator.rb +133 -130
  70. data/solara/lib/core/scripts/yaml_manager.rb +23 -0
  71. data/solara/lib/core/solara_configurator.rb +1 -1
  72. data/solara/lib/core/template/brands/android/android_config.json +0 -1
  73. data/solara/lib/core/template/brands/android/res/values/strings.xml +4 -0
  74. data/solara/lib/core/template/brands/ios/InfoPlist.xcstrings +30 -0
  75. data/solara/lib/core/template/brands/ios/ios_config.json +1 -2
  76. data/solara/lib/core/template/brands/shared/brand_config.json +1 -0
  77. data/solara/lib/core/template/brands/shared/theme.json +3 -3
  78. data/solara/lib/core/template/config/android_template_config.json +11 -1
  79. data/solara/lib/core/template/config/flutter_template_config.json +12 -2
  80. data/solara/lib/core/template/config/ios_template_config.json +11 -1
  81. data/solara/lib/core/template/project_template_generator.rb +8 -3
  82. data/solara/lib/solara/version.rb +1 -1
  83. data/solara/lib/solara.rb +8 -4
  84. data/solara/lib/solara_initializer.rb +5 -2
  85. data/solara/lib/solara_manager.rb +1 -1
  86. metadata +62 -43
  87. data/solara/lib/core/dashboard/handler/brand_section_handler.rb +0 -20
  88. data/solara/lib/core/doctor/validator/directory_structure_validator.rb +0 -38
  89. data/solara/lib/core/doctor/validator/file_structure_validator.rb +0 -37
  90. data/solara/lib/core/scripts/platform/ios/ios_file_path_manager.rb +0 -109
  91. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/.DS_Store +0 -0
  92. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/100.png +0 -0
  93. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/102.png +0 -0
  94. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/1024.png +0 -0
  95. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/114.png +0 -0
  96. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/120.png +0 -0
  97. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/128.png +0 -0
  98. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/144.png +0 -0
  99. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/152.png +0 -0
  100. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/16.png +0 -0
  101. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/167.png +0 -0
  102. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/172.png +0 -0
  103. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/180.png +0 -0
  104. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/196.png +0 -0
  105. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/20.png +0 -0
  106. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/216.png +0 -0
  107. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/256.png +0 -0
  108. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/29.png +0 -0
  109. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/32.png +0 -0
  110. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/40.png +0 -0
  111. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/48.png +0 -0
  112. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/50.png +0 -0
  113. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/512.png +0 -0
  114. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/55.png +0 -0
  115. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/57.png +0 -0
  116. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/58.png +0 -0
  117. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/60.png +0 -0
  118. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/64.png +0 -0
  119. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/66.png +0 -0
  120. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/72.png +0 -0
  121. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/76.png +0 -0
  122. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/80.png +0 -0
  123. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/87.png +0 -0
  124. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/88.png +0 -0
  125. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/92.png +0 -0
  126. /data/solara/lib/core/template/brands/ios/{assets → xcassets}/AppIcon.appiconset/Contents.json +0 -0
@@ -5,25 +5,22 @@ require 'rubygems'
5
5
  class BrandsManager
6
6
  include Singleton
7
7
 
8
+ # Class method to access the singleton instance
9
+ def self.instance
10
+ @instance ||= new
11
+ end
12
+
8
13
  def initialize
9
14
  brands_list_path = FilePath.brands_list
10
15
  @brands_list = JSON.parse(File.read(brands_list_path))
11
16
  end
12
17
 
13
- def first_brand
14
- brands_list.first
15
- end
16
-
17
18
  def first_brand_key
18
19
  brands_list.first["key"]
19
20
  rescue
20
21
  nil
21
22
  end
22
23
 
23
- def brand_name(brand_key)
24
- brand_data(brand_key)["name"]
25
- end
26
-
27
24
  def brand_with_configurations(brand_key)
28
25
  configurations = BrandConfigurationsManager.new(brand_key).create
29
26
  {
@@ -41,17 +38,8 @@ class BrandsManager
41
38
  @brands_list["brands"]
42
39
  end
43
40
 
44
- def find(brand_key)
45
- brands = brands_list
46
- brands.find { |b| b["key"] == brand_key }
47
- end
48
-
49
- def brands_json
50
- @brands_list
51
- end
52
-
53
41
  def exists(brand_key)
54
- !brand_data(brand_key).nil?
42
+ !brand_data(brand_key).nil? && File.exist?(FilePath.brand(brand_key))
55
43
  end
56
44
 
57
45
  def add_brand(brand_name, brand_key)
@@ -59,11 +47,11 @@ class BrandsManager
59
47
  existing_brand = brand_data(brand_key)
60
48
  if existing_brand
61
49
  update_brand(brands_list.index(existing_brand), brand)
62
- Solara.logger.debug("#{brand_name} Brand updated in the list.")
50
+ Solara.logger.debug("#{brand_name} Brand updated in the brands list.")
63
51
  else
64
52
  brands_list.push(brand)
65
53
  save_brands_list
66
- Solara.logger.debug("#{brand_name} added to the list.")
54
+ Solara.logger.debug("#{brand_name} added to the brands list.")
67
55
  end
68
56
  end
69
57
 
@@ -71,13 +59,6 @@ class BrandsManager
71
59
  JSON.parse(File.read(FilePath.current_brand))
72
60
  end
73
61
 
74
- def current_brand_content_changed(brand_key)
75
- unless is_current_brand(brand_key)
76
- return false
77
- end
78
- current_brand['content_changed'] == true
79
- end
80
-
81
62
  def set_current_brand_content_changed(brand_key, changed)
82
63
  unless is_current_brand(brand_key)
83
64
  return false
@@ -85,7 +66,7 @@ class BrandsManager
85
66
  Solara.logger.debug("")
86
67
  brand = current_brand
87
68
  brand['content_changed'] = changed
88
- save_current_brand_date(brand)
69
+ save_current_brand_data(brand)
89
70
  Solara.logger.debug("#{brand_key} changed saved to current_brand.json.")
90
71
  end
91
72
 
@@ -95,17 +76,17 @@ class BrandsManager
95
76
 
96
77
  def save_current_brand(brand_key)
97
78
  brand = brand_data(brand_key)
98
- save_current_brand_date(brand)
79
+ save_current_brand_data(brand)
99
80
  Solara.logger.debug("#{brand_key} saved as current brand.")
100
81
  end
101
82
 
102
- def save_current_brand_date(brand_data)
103
- current_brand = FilePath.current_brand
83
+ def save_current_brand_data(brand_data)
84
+ path = FilePath.current_brand
104
85
 
105
86
  # Create the file if it doesn't exist
106
- FileUtils.touch(current_brand) unless File.exist?(current_brand)
87
+ FileUtils.touch(path) unless File.exist?(path)
107
88
 
108
- File.open(current_brand, 'w') do |file|
89
+ File.open(path, 'w') do |file|
109
90
  file.write(JSON.pretty_generate(brand_data))
110
91
  end
111
92
 
@@ -128,7 +109,7 @@ class BrandsManager
128
109
  if index
129
110
  brand_dir = FilePath.brand(brand_key)
130
111
  remove_brand(index)
131
- FileUtils.rm_rf(brand_dir)
112
+ FileManager.delete_if_exists(brand_dir)
132
113
  save_brands_list
133
114
  Solara.logger.debug("Brand removed.")
134
115
  else
@@ -136,11 +117,6 @@ class BrandsManager
136
117
  end
137
118
  end
138
119
 
139
- # Class method to access the singleton instance
140
- def self.instance
141
- @instance ||= new
142
- end
143
-
144
120
  private
145
121
 
146
122
  def save_brands_list
@@ -3,6 +3,25 @@ import BrandDetailModel from './BrandDetailModel.js';
3
3
  import BrandDetailView from './BrandDetailView.js';
4
4
  import BrandDetailController from './BrandDetailController.js';
5
5
 
6
+ window.onload = function () {
7
+ document.getElementById('loadingOverlay').style.display = 'none';
8
+ };
9
+
10
+ let lastScrollTop = 0;
11
+ const header = document.getElementById('header');
12
+
13
+ window.addEventListener('scroll', function () {
14
+ let scrollTop = window.pageYOffset || document.documentElement.scrollTop;
15
+ if (scrollTop > lastScrollTop) {
16
+ // Scrolling down
17
+ header.classList.add('scroll-down');
18
+ } else {
19
+ // Scrolling up
20
+ header.classList.remove('scroll-down');
21
+ }
22
+ lastScrollTop = scrollTop;
23
+ });
24
+
6
25
  document.addEventListener('DOMContentLoaded', async () => {
7
26
  const model = new BrandDetailModel();
8
27
  const view = new BrandDetailView(model);
@@ -1,4 +1,5 @@
1
1
  import {DataSource} from './BrandDetailModel.js';
2
+ import InfoPlistStringCatalogManager from "./InfoPlistStringCatalogManager.js";
2
3
 
3
4
  class BrandDetailController {
4
5
  constructor(model, view) {
@@ -112,8 +113,11 @@ class BrandDetailController {
112
113
 
113
114
  async onLoadSections(configuraationsResult) {
114
115
  try {
116
+ this.view.addBrandOverlay.style.display = 'none'
117
+ this.view.header.style.display = 'flex';
115
118
  this.view.updateAppNameTitle(`${configuraationsResult.brand.key} (${configuraationsResult.brand.name})`);
116
119
  await this.showSections(configuraationsResult);
120
+ this.view.showIndex();
117
121
  } catch (error) {
118
122
  console.error('Error initializing app:', error);
119
123
  alert(error.message);
@@ -142,10 +146,17 @@ class BrandDetailController {
142
146
  const sectionInfo = sectionItems[i];
143
147
  console.log('Processing section:', i, sectionInfo);
144
148
 
145
- if (sectionInfo.key === 'theme') {
149
+ if (sectionInfo.key === 'theme.json') {
146
150
  this.createThemeSections(sectionInfo)
151
+ } else if (sectionInfo.key === 'InfoPlist.xcstrings') {
152
+ this.createSection(
153
+ sectionInfo.key,
154
+ sectionInfo,
155
+ new InfoPlistStringCatalogManager(sectionInfo.content).extractLocalizations(),
156
+ sectionInfo.name,
157
+ sectionInfo.inputType)
147
158
  } else {
148
- this.createSection(sectionInfo, sectionInfo.content, sectionInfo.name, sectionInfo.inputType)
159
+ this.createSection(sectionInfo.key, sectionInfo, sectionInfo.content, sectionInfo.name, sectionInfo.inputType)
149
160
  }
150
161
  }
151
162
  } catch (error) {
@@ -155,15 +166,42 @@ class BrandDetailController {
155
166
  }
156
167
 
157
168
  createThemeSections(sectionInfo) {
158
- this.createSection(sectionInfo, sectionInfo.content.colors, 'Theme Colors', 'color', 'colors')
159
- this.createSection(sectionInfo, sectionInfo.content.typography, 'Theme Typography', 'text', 'typography')
160
- this.createSection(sectionInfo, sectionInfo.content.spacing, 'Theme Spacing', 'text', 'spacing')
161
- this.createSection(sectionInfo, sectionInfo.content.borderRadius, 'Theme Border Radius', 'text', 'borderRadius')
162
- this.createSection(sectionInfo, sectionInfo.content.elevation, 'Theme Elevation', 'text', 'elevation')
169
+ this.createSection(`${sectionInfo.key}_colors`,
170
+ sectionInfo,
171
+ sectionInfo.content.colors,
172
+ 'Theme Colors',
173
+ 'color',
174
+ 'colors')
175
+ this.createSection(`${sectionInfo.key}_typography`,
176
+ sectionInfo,
177
+ sectionInfo.content.typography,
178
+ 'Theme Typography',
179
+ 'text',
180
+ 'typography')
181
+ this.createSection(`${sectionInfo.key}_spacing`,
182
+ sectionInfo,
183
+ sectionInfo.content.spacing,
184
+ 'Theme Spacing', 'text',
185
+ 'spacing')
186
+ this.createSection(
187
+ `${sectionInfo.key}_borderRadius`,
188
+ sectionInfo,
189
+ sectionInfo.content.borderRadius,
190
+ 'Theme Border Radius',
191
+ 'text',
192
+ 'borderRadius')
193
+ this.createSection(
194
+ `${sectionInfo.key}_elevation`,
195
+ sectionInfo,
196
+ sectionInfo.content.elevation,
197
+ 'Theme Elevation',
198
+ 'text',
199
+ 'elevation')
163
200
  }
164
201
 
165
- createSection(sectionInfo, content, sectionName, inputType, propertiesGroupName = null) {
202
+ createSection(id, sectionInfo, content, sectionName, inputType, propertiesGroupName = null) {
166
203
  const sectionElement = this.view.createSection(sectionInfo.key, sectionName, inputType);
204
+ sectionElement.id = id;
167
205
  sectionElement.dataset.propertiesGroupName = propertiesGroupName
168
206
 
169
207
  this.view.sectionsContainer.appendChild(sectionElement);
@@ -208,6 +246,8 @@ class BrandDetailController {
208
246
  if (input.classList.contains('array-input')) {
209
247
  const arrayItemsContainer = input.closest('.input-wrapper').querySelector('.array-items-container');
210
248
  current[lastKey] = this.getArrayValue(arrayItemsContainer);
249
+ } else if (input.type === 'checkbox') {
250
+ current[lastKey] = input.checked;
211
251
  } else {
212
252
  let value = input.value;
213
253
  if (input.type === 'color') {
@@ -358,4 +398,6 @@ class BrandDetailController {
358
398
 
359
399
  }
360
400
 
401
+
402
+
361
403
  export default BrandDetailController;
@@ -14,9 +14,7 @@ class BrandDetailModel {
14
14
  this.brandKey = this.getQueryFromUrl('brand_key');
15
15
 
16
16
  const sourceFromUrl = this.getQueryFromUrl('source');
17
- // TODO: uncomment
18
- this.source = sourceFromUrl === DataSource.LOCAL ? DataSource.LOCAL : DataSource.REMOTE;
19
- // this.source = DataSource.REMOTE;
17
+ this.source = sourceFromUrl === DataSource.LOCAL ? DataSource.LOCAL : DataSource.REMOTE;
20
18
  }
21
19
 
22
20
  getQueryFromUrl(name) {
@@ -24,25 +22,11 @@ class BrandDetailModel {
24
22
  }
25
23
 
26
24
  async fetchBrandDetails() {
27
- switch (this.source) {
28
- case DataSource.LOCAL:
29
- return await this.localSource.fetchBrandDetails(this.brandKey);
30
- case DataSource.REMOTE:
31
- return await this.remoteSource.fetchBrandDetails(this.brandKey);
32
- default:
33
- throw new Error('Unknown data source');
34
- }
25
+ return await this.localSource.fetchBrandDetails(this.brandKey);
35
26
  }
36
27
 
37
28
  async fetchCurrentBrand() {
38
- switch (this.source) {
39
- case DataSource.LOCAL:
40
- return await this.localSource.fetchCurrentBrand(this.brandKey);
41
- case DataSource.REMOTE:
42
- return await this.remoteSource.fetchCurrentBrand(this.brandKey);
43
- default:
44
- throw new Error('Unknown data source');
45
- }
29
+ return await this.localSource.fetchCurrentBrand(this.brandKey);
46
30
  }
47
31
 
48
32
  async saveSection(sectionItem, configuration) {
@@ -50,29 +34,24 @@ class BrandDetailModel {
50
34
  case DataSource.LOCAL:
51
35
  return await this.localSource.saveSection(sectionItem, configuration, this.brandKey);
52
36
  case DataSource.REMOTE:
53
- return await this.remoteSource.saveSection(sectionItem, configuration, this.brandKey);
37
+ // Saving is not supported remotely. Instead, user can export the brand.
38
+ return
54
39
  default:
55
40
  throw new Error('Unknown data source');
56
41
  }
57
42
  }
58
43
 
59
44
  async switchToBrand() {
60
- switch (this.source) {
61
- case DataSource.LOCAL:
62
- return await this.localSource.switchToBrand(this.brandKey);
63
- case DataSource.REMOTE:
64
- return await this.remoteSource.switchToBrand(this.brandKey);
65
- default:
66
- throw new Error('Unknown data source');
67
- }
45
+ return await this.localSource.switchToBrand(this.brandKey);
68
46
  }
69
47
 
70
48
  async checkBrandHealth() {
71
49
  switch (this.source) {
72
50
  case DataSource.LOCAL:
73
- return await this.localSource.checkBrandHealth(this.brandKey);
51
+ return await this.localSource.checkBrandHealth(this.brandKey);
74
52
  case DataSource.REMOTE:
75
- return await this.remoteSource.checkBrandHealth(this.brandKey);
53
+ // Checking health is not supported remotely yet.
54
+ return
76
55
  default:
77
56
  throw new Error('Unknown data source');
78
57
  }
@@ -7,7 +7,11 @@ import '../component/MessageBottomSheet.js';
7
7
  class BrandDetailView {
8
8
  constructor(model) {
9
9
  this.model = model;
10
+
11
+ this.header = document.getElementById('header');
12
+
10
13
  this.brandDetailsContainer = document.getElementById('brand-details-container');
14
+ this.addBrandOverlay = document.getElementById('add-brand-overlay');
11
15
  this.addBrandContainer = document.getElementById('add-brand-container');
12
16
 
13
17
  this.sectionsContainer = document.getElementById('sections');
@@ -38,11 +42,15 @@ class BrandDetailView {
38
42
  }
39
43
 
40
44
  async setupLocal() {
45
+ this.addBrandOverlay.style.display = 'none'
46
+ this.header.style.display = 'flex'
41
47
  this.allBrandsButton.style.display = 'block'
42
48
  this.toggleAddBrandContainer(false);
43
49
  }
44
50
 
45
51
  async setupRemote() {
52
+ this.addBrandOverlay.style.display = 'flex'
53
+ this.header.style.display = 'none'
46
54
  this.allBrandsButton.style.display = 'none'
47
55
  this.toggleAddBrandContainer(true);
48
56
  }
@@ -76,7 +84,7 @@ class BrandDetailView {
76
84
 
77
85
  const subtitleElement = document.createElement('p');
78
86
  subtitleElement.className = 'section-subtitle';
79
- subtitleElement.textContent = `${key}.json`;
87
+ subtitleElement.textContent = key;
80
88
  titleContainer.appendChild(subtitleElement);
81
89
 
82
90
  section.appendChild(titleContainer);
@@ -90,10 +98,12 @@ class BrandDetailView {
90
98
  sectionElement.appendChild(this.createInputField(sectionItem, key, value, 'array'));
91
99
  } else if (typeof value === 'object' && value !== null) {
92
100
  for (const [subKey, subValue] of Object.entries(value)) {
93
- sectionElement.appendChild(this.createInputField(sectionItem, `${key}.${subKey}`, subValue, inputType));
101
+ const subInputType = subValue === true || subValue === false ? 'boolean' : inputType;
102
+ sectionElement.appendChild(this.createInputField(sectionItem, `${key}.${subKey}`, subValue, subInputType));
94
103
  }
95
104
  } else {
96
- sectionElement.appendChild(this.createInputField(sectionItem, key, value, inputType));
105
+ const fieldInputType = value === true || value === false ? 'boolean' : inputType;
106
+ sectionElement.appendChild(this.createInputField(sectionItem, key, value, fieldInputType));
97
107
  }
98
108
  }
99
109
  }
@@ -142,11 +152,30 @@ class BrandDetailView {
142
152
  input.value = '';
143
153
  });
144
154
 
155
+ } else if (inputType === 'boolean') {
156
+ const checkbox = document.createElement('input');
157
+ checkbox.type = 'checkbox';
158
+ checkbox.id = key;
159
+ checkbox.checked = value;
160
+
161
+ const checkboxLabel = document.createElement('label');
162
+ checkboxLabel.className = 'checkbox-label';
163
+ checkboxLabel.htmlFor = key;
164
+ checkboxLabel.textContent = value ? 'True' : 'False';
165
+
166
+ checkbox.addEventListener('change', () => {
167
+ checkboxLabel.textContent = checkbox.checked ? 'True' : 'False';
168
+ this.onSectionChanged(sectionItem, container.closest('.section'));
169
+ });
170
+
171
+ inputWrapper.appendChild(checkbox);
172
+ inputWrapper.appendChild(checkboxLabel);
145
173
  } else {
146
174
  const input = document.createElement('input');
147
175
  input.type = inputType;
148
176
  input.id = key;
149
177
 
178
+ console.log(value)
150
179
  if (inputType === 'color') {
151
180
  input.value = value.startsWith('#') ? value : `#${value.substring(4)}`;
152
181
  } else {
@@ -236,6 +265,23 @@ class BrandDetailView {
236
265
  this.onboardSheet.show('Brand Details', 'Add Brand', onSubmit);
237
266
  }
238
267
 
268
+ showIndex() {
269
+ const sectionsContainer = this.sectionsContainer;
270
+ const sectionElements = Array.from(sectionsContainer.querySelectorAll('.section'));
271
+
272
+ const indexElement = document.getElementById('index');
273
+
274
+ // Clear existing items if needed
275
+ indexElement.innerHTML = '';
276
+
277
+ sectionElements.forEach(sectionElement => {
278
+ const newItem = document.createElement('li');
279
+ newItem.classList.add('index-item');
280
+ newItem.innerHTML = `<a href="#${sectionElement.id}">${sectionElement.dataset.name}</a>`;
281
+ indexElement.appendChild(newItem);
282
+ });
283
+ }
284
+
239
285
  async hideOnboardBrandForm() {
240
286
  this.onboardSheet.hide();
241
287
  }
@@ -0,0 +1,19 @@
1
+ class InfoPlistStringCatalogManager {
2
+ constructor(jsonData) {
3
+ this.data = jsonData;
4
+ this.localizations = {};
5
+ this.extractLocalizations();
6
+ }
7
+
8
+ extractLocalizations() {
9
+ for (const [key, details] of Object.entries(this.data.strings)) {
10
+ for (const [lang, localization] of Object.entries(details.localizations)) {
11
+ const formattedKey = `${key}.${lang}`;
12
+ this.localizations[formattedKey] = localization.stringUnit.value;
13
+ }
14
+ }
15
+ return this.localizations
16
+ }
17
+ }
18
+
19
+ export default InfoPlistStringCatalogManager;