solara 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/bin/solara +18 -0
  3. data/solara/lib/.DS_Store +0 -0
  4. data/solara/lib/core/.DS_Store +0 -0
  5. data/solara/lib/core/aliases/alias_generator.rb +128 -0
  6. data/solara/lib/core/aliases/alias_generator_manager.rb +28 -0
  7. data/solara/lib/core/aliases/solara_terminal_setup.rb +103 -0
  8. data/solara/lib/core/brands/brand_onboarder.rb +46 -0
  9. data/solara/lib/core/brands/brand_switcher.rb +204 -0
  10. data/solara/lib/core/brands/brands_manager.rb +154 -0
  11. data/solara/lib/core/dashboard/.DS_Store +0 -0
  12. data/solara/lib/core/dashboard/brand/.DS_Store +0 -0
  13. data/solara/lib/core/dashboard/brand/BrandDetail.js +11 -0
  14. data/solara/lib/core/dashboard/brand/BrandDetailController.js +361 -0
  15. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +155 -0
  16. data/solara/lib/core/dashboard/brand/BrandDetailView.js +245 -0
  17. data/solara/lib/core/dashboard/brand/brand.html +477 -0
  18. data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +123 -0
  19. data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +260 -0
  20. data/solara/lib/core/dashboard/brands/Brands.js +10 -0
  21. data/solara/lib/core/dashboard/brands/BrandsController.js +155 -0
  22. data/solara/lib/core/dashboard/brands/BrandsModel.js +136 -0
  23. data/solara/lib/core/dashboard/brands/BrandsView.js +136 -0
  24. data/solara/lib/core/dashboard/brands/brands.html +345 -0
  25. data/solara/lib/core/dashboard/component/AddFieldSheet.js +212 -0
  26. data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +128 -0
  27. data/solara/lib/core/dashboard/component/BrandOptionsBottomSheet.js +130 -0
  28. data/solara/lib/core/dashboard/component/ConfirmationDialog.js +103 -0
  29. data/solara/lib/core/dashboard/component/MessageBottomSheet.js +80 -0
  30. data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +214 -0
  31. data/solara/lib/core/dashboard/dashboard_manager.rb +19 -0
  32. data/solara/lib/core/dashboard/dashboard_server.rb +132 -0
  33. data/solara/lib/core/dashboard/handler/base_handler.rb +25 -0
  34. data/solara/lib/core/dashboard/handler/brand_alisases_handler.rb +33 -0
  35. data/solara/lib/core/dashboard/handler/brand_configurations_handler.rb +18 -0
  36. data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +73 -0
  37. data/solara/lib/core/dashboard/handler/brand_icon_handler.rb +20 -0
  38. data/solara/lib/core/dashboard/handler/brand_section_handler.rb +20 -0
  39. data/solara/lib/core/dashboard/handler/brands_handler.rb +14 -0
  40. data/solara/lib/core/dashboard/handler/current_brand_handler.rb +18 -0
  41. data/solara/lib/core/dashboard/handler/doctor_handler.rb +39 -0
  42. data/solara/lib/core/dashboard/handler/edit_section_handler.rb +55 -0
  43. data/solara/lib/core/dashboard/handler/offboard_brand_handler.rb +34 -0
  44. data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +53 -0
  45. data/solara/lib/core/dashboard/handler/redirect_handler.rb +12 -0
  46. data/solara/lib/core/dashboard/handler/switch_handler.rb +25 -0
  47. data/solara/lib/core/dashboard/index.html +36 -0
  48. data/solara/lib/core/dashboard/local.html +41 -0
  49. data/solara/lib/core/dashboard/res/favicon/android-chrome-192x192.png +0 -0
  50. data/solara/lib/core/dashboard/res/favicon/android-chrome-512x512.png +0 -0
  51. data/solara/lib/core/dashboard/res/favicon/apple-touch-icon.png +0 -0
  52. data/solara/lib/core/dashboard/res/favicon/favicon-16x16.png +0 -0
  53. data/solara/lib/core/dashboard/res/favicon/favicon-32x32.png +0 -0
  54. data/solara/lib/core/dashboard/res/favicon/favicon.ico +0 -0
  55. data/solara/lib/core/dashboard/res/favicon/site.webmanifest +1 -0
  56. data/solara/lib/core/dashboard/solara.png +0 -0
  57. data/solara/lib/core/doctor/brand_doctor.rb +94 -0
  58. data/solara/lib/core/doctor/doctor_manager.rb +35 -0
  59. data/solara/lib/core/doctor/project_doctor.rb +8 -0
  60. data/solara/lib/core/doctor/schema/brand_configurations.json +60 -0
  61. data/solara/lib/core/doctor/schema/platform/android/android_config.json +23 -0
  62. data/solara/lib/core/doctor/schema/platform/android/android_signing.json +23 -0
  63. data/solara/lib/core/doctor/schema/platform/ios/ios_config.json +27 -0
  64. data/solara/lib/core/doctor/schema/platform/ios/ios_signing.json +27 -0
  65. data/solara/lib/core/doctor/schema/platform/shared/theme.json +48 -0
  66. data/solara/lib/core/doctor/validator/brand_settings_validator.rb +55 -0
  67. data/solara/lib/core/doctor/validator/brand_settings_validator_manager.rb +82 -0
  68. data/solara/lib/core/doctor/validator/directory_structure_validator.rb +38 -0
  69. data/solara/lib/core/doctor/validator/file_structure_validator.rb +37 -0
  70. data/solara/lib/core/doctor/validator/json_file_validator.rb +21 -0
  71. data/solara/lib/core/doctor/validator/json_schema_validator.rb +32 -0
  72. data/solara/lib/core/doctor/validator/project_filesystem_validator.rb +70 -0
  73. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +51 -0
  74. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +53 -0
  75. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +51 -0
  76. data/solara/lib/core/doctor/validator/template/template_validator.rb +108 -0
  77. data/solara/lib/core/doctor/validator/validation_strategy.rb +7 -0
  78. data/solara/lib/core/scripts/brand_config_generator.rb +245 -0
  79. data/solara/lib/core/scripts/brand_config_manager.rb +90 -0
  80. data/solara/lib/core/scripts/brand_exporter.rb +38 -0
  81. data/solara/lib/core/scripts/brand_importer.rb +84 -0
  82. data/solara/lib/core/scripts/brand_offboarder.rb +19 -0
  83. data/solara/lib/core/scripts/brand_resources_manager.rb +77 -0
  84. data/solara/lib/core/scripts/directory_creator.rb +22 -0
  85. data/solara/lib/core/scripts/file_manager.rb +90 -0
  86. data/solara/lib/core/scripts/file_path.rb +327 -0
  87. data/solara/lib/core/scripts/folder_copier.rb +41 -0
  88. data/solara/lib/core/scripts/gitignore_manager.rb +54 -0
  89. data/solara/lib/core/scripts/interactive_file_system_validator.rb +110 -0
  90. data/solara/lib/core/scripts/platform/android/android_manifest_switcher.rb +24 -0
  91. data/solara/lib/core/scripts/platform/android/android_strings_switcher.rb +39 -0
  92. data/solara/lib/core/scripts/platform/android/gradle_switcher.rb +233 -0
  93. data/solara/lib/core/scripts/platform/android/properties_generator.rb +31 -0
  94. data/solara/lib/core/scripts/platform/ios/ios_file_path_manager.rb +109 -0
  95. data/solara/lib/core/scripts/platform/ios/ios_plist_manager.rb +42 -0
  96. data/solara/lib/core/scripts/platform/ios/xcconfig_generator.rb +44 -0
  97. data/solara/lib/core/scripts/platform/ios/xcode_asset_manager.rb +56 -0
  98. data/solara/lib/core/scripts/platform/ios/xcode_project_manager.rb +82 -0
  99. data/solara/lib/core/scripts/platform/ios/xcode_project_switcher.rb +130 -0
  100. data/solara/lib/core/scripts/project_settings_manager.rb +39 -0
  101. data/solara/lib/core/scripts/solara_logger.rb +103 -0
  102. data/solara/lib/core/scripts/solara_settings_manager.rb +73 -0
  103. data/solara/lib/core/scripts/solara_status_manager.rb +55 -0
  104. data/solara/lib/core/scripts/solara_version_manager.rb +42 -0
  105. data/solara/lib/core/scripts/strings_xml_manager.rb +22 -0
  106. data/solara/lib/core/scripts/terminal_input_manager.rb +22 -0
  107. data/solara/lib/core/scripts/theme_generator.rb +250 -0
  108. data/solara/lib/core/scripts/yaml_manager.rb +72 -0
  109. data/solara/lib/core/solara_configurator.rb +15 -0
  110. data/solara/lib/core/template/brands/android/android_config.json +8 -0
  111. data/solara/lib/core/template/brands/android/android_signing.json +6 -0
  112. data/solara/lib/core/template/brands/android/res/.DS_Store +0 -0
  113. data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher.png +0 -0
  114. data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  115. data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher.png +0 -0
  116. data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  117. data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher.png +0 -0
  118. data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  119. data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  120. data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  121. data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  122. data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  123. data/solara/lib/core/template/brands/brands.json +4 -0
  124. data/solara/lib/core/template/brands/ios/assets/.DS_Store +0 -0
  125. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/100.png +0 -0
  126. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/102.png +0 -0
  127. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/1024.png +0 -0
  128. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/114.png +0 -0
  129. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/120.png +0 -0
  130. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/128.png +0 -0
  131. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/144.png +0 -0
  132. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/152.png +0 -0
  133. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/16.png +0 -0
  134. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/167.png +0 -0
  135. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/172.png +0 -0
  136. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/180.png +0 -0
  137. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/196.png +0 -0
  138. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/20.png +0 -0
  139. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/216.png +0 -0
  140. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/256.png +0 -0
  141. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/29.png +0 -0
  142. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/32.png +0 -0
  143. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/40.png +0 -0
  144. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/48.png +0 -0
  145. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/50.png +0 -0
  146. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/512.png +0 -0
  147. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/55.png +0 -0
  148. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/57.png +0 -0
  149. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/58.png +0 -0
  150. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/60.png +0 -0
  151. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/64.png +0 -0
  152. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/66.png +0 -0
  153. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/72.png +0 -0
  154. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/76.png +0 -0
  155. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/80.png +0 -0
  156. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/87.png +0 -0
  157. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/88.png +0 -0
  158. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/92.png +0 -0
  159. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/Contents.json +1 -0
  160. data/solara/lib/core/template/brands/ios/ios_config.json +7 -0
  161. data/solara/lib/core/template/brands/ios/ios_signing.json +7 -0
  162. data/solara/lib/core/template/brands/shared/.DS_Store +0 -0
  163. data/solara/lib/core/template/brands/shared/brand_config.json +2 -0
  164. data/solara/lib/core/template/brands/shared/theme.json +46 -0
  165. data/solara/lib/core/template/config/android_template_config.json +57 -0
  166. data/solara/lib/core/template/config/flutter_template_config.json +62 -0
  167. data/solara/lib/core/template/config/ios_template_config.json +57 -0
  168. data/solara/lib/core/template/project_template_generator.rb +63 -0
  169. data/solara/lib/platform_detector.rb +84 -0
  170. data/solara/lib/solara/cli.rb +5 -0
  171. data/solara/lib/solara/version.rb +3 -0
  172. data/solara/lib/solara.rb +238 -0
  173. data/solara/lib/solara_initializer.rb +44 -0
  174. data/solara/lib/solara_manager.rb +73 -0
  175. metadata +346 -0
@@ -0,0 +1,130 @@
1
+ class BrandOptionsBottomSheet extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({mode: 'open'});
5
+
6
+ this.shadowRoot.innerHTML = `
7
+ <head>
8
+ <style>
9
+ .bottom-sheet {
10
+ display: none;
11
+ position: fixed;
12
+ bottom: 0;
13
+ left: 0;
14
+ right: 0;
15
+ background-color: white;
16
+ border-top-left-radius: 20px;
17
+ border-top-right-radius: 20px;
18
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
19
+ z-index: 1000;
20
+ padding: 20px;
21
+ transition: transform 0.3s ease-out;
22
+ transform: translateY(100%);
23
+ max-width: 700px;
24
+ margin: 0 auto;
25
+ width: 100%;
26
+ }
27
+ .bottom-sheet.show {
28
+ transform: translateY(0);
29
+ }
30
+ .bottom-sheet ul {
31
+ list-style-type: none;
32
+ padding: 0;
33
+ margin: 0;
34
+ }
35
+ .bottom-sheet li {
36
+ padding: 15px 20px;
37
+ cursor: pointer;
38
+ transition: background-color 0.3s ease;
39
+ display: flex;
40
+ align-items: center;
41
+ }
42
+ .bottom-sheet li:hover {
43
+ background-color: var(--background-color);
44
+ }
45
+ .bottom-sheet li i {
46
+ margin-right: 15px;
47
+ font-size: 20px;
48
+ width: 24px;
49
+ text-align: center;
50
+ }
51
+ .overlay {
52
+ display: none;
53
+ position: fixed;
54
+ top: 0;
55
+ left: 0;
56
+ right: 0;
57
+ bottom: 0;
58
+ background: rgba(0, 0, 0, 0.5);
59
+ z-index: 999;
60
+ }
61
+ </style>
62
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
63
+ </head>
64
+ <div class="overlay"></div>
65
+ <div class="bottom-sheet" id="bottomSheet">
66
+ <ul>
67
+ <li id="cloneOption" class="clone-option"><i class="fas fa-copy"></i>Clone</li>
68
+ <li id="doctorOption" class="doctor-option"><i class="fas fa-stethoscope"></i>Doctor</li>
69
+ <li id="offboardOption" class="offboard-option"><i class="fas fa-trash-alt"></i>Offboard</li>
70
+ <li id="aliasesOption" class="aliases-option"><i class="fas fa-terminal"></i>Terminal aliases</li>
71
+ <li id="settingsOption" class="settings-option"><i class="fas fa-cog"></i>Settings</li>
72
+ </ul>
73
+ </div>
74
+ `;
75
+
76
+ this.bottomSheet = this.shadowRoot.getElementById('bottomSheet');
77
+ this.overlay = this.shadowRoot.querySelector('.overlay');
78
+
79
+ this.shadowRoot.getElementById('cloneOption').onclick = this.handleCloneOption.bind(this);
80
+ this.shadowRoot.getElementById('offboardOption').onclick = this.handleOffboardOption.bind(this);
81
+ this.shadowRoot.getElementById('doctorOption').onclick = this.handleDoctorOption.bind(this);
82
+ this.shadowRoot.getElementById('aliasesOption').onclick = this.handleAliasesOption.bind(this);
83
+ this.shadowRoot.getElementById('settingsOption').onclick = this.handleSettingsOption.bind(this);
84
+
85
+ this.overlay.onclick = this.hideBrandOptionsBottomSheet.bind(this);
86
+ }
87
+
88
+ show() {
89
+ this.bottomSheet.style.display = 'block';
90
+ this.overlay.style.display = 'block';
91
+ setTimeout(() => this.bottomSheet.classList.add('show'), 10);
92
+ }
93
+
94
+ hideBrandOptionsBottomSheet() {
95
+ this.bottomSheet.classList.remove('show');
96
+ this.overlay.style.display = 'none';
97
+ }
98
+
99
+ handleCloneOption(event) {
100
+ event.stopPropagation();
101
+ this.hideBrandOptionsBottomSheet();
102
+ this.dispatchEvent(new CustomEvent('clone', {detail: this.bottomSheet.dataset}));
103
+ }
104
+
105
+ handleOffboardOption(event) {
106
+ event.stopPropagation();
107
+ this.hideBrandOptionsBottomSheet();
108
+ this.dispatchEvent(new CustomEvent('offboard', {detail: this.bottomSheet.dataset}));
109
+ }
110
+
111
+ handleDoctorOption(event) {
112
+ event.stopPropagation();
113
+ this.hideBrandOptionsBottomSheet();
114
+ this.dispatchEvent(new CustomEvent('doctor', {detail: this.bottomSheet.dataset}));
115
+ }
116
+
117
+ handleAliasesOption(event) {
118
+ event.stopPropagation();
119
+ this.hideBrandOptionsBottomSheet();
120
+ this.dispatchEvent(new CustomEvent('aliases', {detail: this.bottomSheet.dataset}));
121
+ }
122
+
123
+ handleSettingsOption(event) {
124
+ event.stopPropagation();
125
+ this.hideBrandOptionsBottomSheet();
126
+ this.dispatchEvent(new CustomEvent('settings', {detail: this.bottomSheet.dataset}));
127
+ }
128
+ }
129
+
130
+ customElements.define('brand-options-bottom-sheet', BrandOptionsBottomSheet);
@@ -0,0 +1,103 @@
1
+ class ConfirmationDialog extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({mode: 'open'});
5
+ this.render();
6
+ }
7
+
8
+ render() {
9
+ this.shadowRoot.innerHTML = `
10
+ <style>
11
+ .confirmation-dialog {
12
+ display: none;
13
+ position: fixed;
14
+ top: 50%;
15
+ left: 50%;
16
+ transform: translate(-50%, -50%);
17
+ background-color: white;
18
+ padding: 20px;
19
+ border-radius: 10px;
20
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
21
+ z-index: 1001;
22
+ text-align: center;
23
+ }
24
+ .confirmation-dialog h3 {
25
+ margin-top: 0;
26
+ color: var(--primary-color);
27
+ }
28
+ .confirmation-dialog .buttons {
29
+ margin-top: 20px;
30
+ }
31
+ .confirmation-dialog button {
32
+ margin: 0 10px;
33
+ padding: 10px 20px;
34
+ border: none;
35
+ border-radius: 5px;
36
+ cursor: pointer;
37
+ font-size: 16px;
38
+ transition: background-color 0.3s ease;
39
+ }
40
+ .confirmation-dialog .confirm {
41
+ background-color: #dc3545;
42
+ color: white;
43
+ }
44
+ .confirmation-dialog .cancel {
45
+ background-color: #ccc;
46
+ color: #333;
47
+ }
48
+ .confirmation-dialog .confirm:hover {
49
+ background-color: #c82333;
50
+ }
51
+ .confirmation-dialog .cancel:hover {
52
+ background-color: #bbb;
53
+ }
54
+ .overlay {
55
+ display: none;
56
+ position: fixed;
57
+ top: 0;
58
+ left: 0;
59
+ width: 100%;
60
+ height: 100%;
61
+ background: rgba(0, 0, 0, 0.5);
62
+ z-index: 999;
63
+ }
64
+ </style>
65
+ <div class="overlay" id="overlay"></div>
66
+ <div class="confirmation-dialog" id="confirmationDialog">
67
+ <h3>Confirm Action</h3>
68
+ <p id="confirmationMessage"></p>
69
+ <div class="buttons">
70
+ <button class="confirm" id="confirmButton">Confirm</button>
71
+ <button id="cancelButton">Cancel</button>
72
+ </div>
73
+ </div>
74
+ `;
75
+
76
+ this.confirmationDialog = this.shadowRoot.getElementById('confirmationDialog');
77
+ this.overlay = this.shadowRoot.getElementById('overlay');
78
+ this.confirmButton = this.shadowRoot.getElementById('confirmButton');
79
+ this.cancelButton = this.shadowRoot.getElementById('cancelButton');
80
+ this.confirmationMessageElement = this.shadowRoot.getElementById('confirmationMessage');
81
+
82
+ this.cancelButton.onclick = () => this.hide();
83
+ this.overlay.onclick = () => this.hide();
84
+ }
85
+
86
+ showDialog(message, onConfirm) {
87
+ this.confirmationMessageElement.textContent = message;
88
+ this.confirmationDialog.style.display = 'block';
89
+ this.overlay.style.display = 'block';
90
+
91
+ this.confirmButton.onclick = () => {
92
+ onConfirm();
93
+ this.hide();
94
+ };
95
+ }
96
+
97
+ hide() {
98
+ this.confirmationDialog.style.display = 'none';
99
+ this.overlay.style.display = 'none';
100
+ }
101
+ }
102
+
103
+ customElements.define('confirmation-dialog', ConfirmationDialog);
@@ -0,0 +1,80 @@
1
+ class MessageBottomSheet extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({mode: 'open'});
5
+ this.shadowRoot.innerHTML = `
6
+ <style>
7
+ .message-bottom-sheet {
8
+ display: none;
9
+ position: fixed;
10
+ bottom: 0;
11
+ left: 0;
12
+ right: 0;
13
+ background-color: white;
14
+ border-top-left-radius: 20px;
15
+ border-top-right-radius: 20px;
16
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
17
+ z-index: 1000;
18
+ padding: 20px;
19
+ transition: transform 0.3s ease-out;
20
+ transform: translateY(100%);
21
+ max-width: 1000px;
22
+ margin: 0 auto;
23
+ width: 100%;
24
+ }
25
+ .message-bottom-sheet.show {
26
+ transform: translateY(0);
27
+ }
28
+ .message-content {
29
+ max-height: 300px; /* Set a maximum height */
30
+ overflow-y: auto; /* Enable vertical scrolling */
31
+ margin-bottom: 20px;
32
+ }
33
+ .close-message {
34
+ width: 10%;
35
+ margin: 20px;
36
+ padding: 10px;
37
+ background-color: var(--primary-color);
38
+ color: white;
39
+ border: none;
40
+ border-radius: 5px;
41
+ cursor: pointer;
42
+ }
43
+
44
+ .close-message:hover {
45
+ background-color: #0056b3; /* Darker shade on hover */
46
+ }
47
+ </style>
48
+ <div class="message-bottom-sheet" id="messageBottomSheet">
49
+ <div class="message-content" id="messageContent"></div>
50
+ <button class="close-message" id="closeMessage">Close</button>
51
+ </div>
52
+ <div id="overlay" class="overlay" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); z-index: 999;"></div>
53
+ `;
54
+
55
+ this.messageBottomSheet = this.shadowRoot.getElementById('messageBottomSheet');
56
+ this.messageContent = this.shadowRoot.getElementById('messageContent');
57
+ this.closeMessageButton = this.shadowRoot.getElementById('closeMessage');
58
+ this.overlay = this.shadowRoot.getElementById('overlay');
59
+
60
+ this.closeMessageButton.onclick = () => this.hideMessage();
61
+ this.overlay.onclick = () => this.hideMessage();
62
+ }
63
+
64
+ showMessage(message) {
65
+ this.messageContent.innerHTML = message.replace(/\n/g, '<br>');
66
+ this.messageBottomSheet.style.display = 'block';
67
+ this.overlay.style.display = 'block';
68
+ setTimeout(() => this.messageBottomSheet.classList.add('show'), 10);
69
+ }
70
+
71
+ hideMessage() {
72
+ this.messageBottomSheet.classList.remove('show');
73
+ setTimeout(() => {
74
+ this.messageBottomSheet.style.display = 'none';
75
+ this.overlay.style.display = 'none';
76
+ }, 300);
77
+ }
78
+ }
79
+
80
+ customElements.define('message-bottom-sheet', MessageBottomSheet);
@@ -0,0 +1,214 @@
1
+ class OnboardBrandBottomSheet extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({mode: 'open'});
5
+ this.onSubmit = null;
6
+ }
7
+
8
+ connectedCallback() {
9
+ this.render();
10
+ this.setupEventListeners();
11
+ }
12
+
13
+ render() {
14
+ this.shadowRoot.innerHTML = `
15
+ <head>
16
+ <style>
17
+ .overlay {
18
+ display: none;
19
+ position: fixed;
20
+ top: 0;
21
+ left: 0;
22
+ right: 0;
23
+ bottom: 0;
24
+ background-color: rgba(0, 0, 0, 0.5);
25
+ z-index: 999;
26
+ }
27
+ .bottom-sheet {
28
+ display: none;
29
+ position: fixed;
30
+ bottom: 0;
31
+ left: 0;
32
+ right: 0;
33
+ background-color: white;
34
+ border-top-left-radius: 20px;
35
+ border-top-right-radius: 20px;
36
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
37
+ z-index: 1000;
38
+ padding: 20px;
39
+ transition: transform 0.3s ease-out;
40
+ transform: translateY(100%);
41
+ max-width: 700px;
42
+ margin: 0 auto;
43
+ width: 100%;
44
+ }
45
+ .bottom-sheet.show {
46
+ transform: translateY(0);
47
+ }
48
+ h3 {
49
+ color: var(--primary-color, #4A90E2);
50
+ margin-top: 0;
51
+ }
52
+ .onboard-brand-form {
53
+ display: flex;
54
+ flex-direction: column;
55
+ gap: 15px;
56
+ }
57
+ .form-group {
58
+ display: flex;
59
+ flex-direction: column;
60
+ }
61
+ .form-group label {
62
+ display: flex;
63
+ align-items: center;
64
+ margin-bottom: 5px;
65
+ font-weight: bold;
66
+ }
67
+ .form-group input {
68
+ padding: 10px;
69
+ border: 1px solid var(--border-color, #E1E4E8);
70
+ border-radius: 5px;
71
+ font-size: 16px;
72
+ }
73
+ .tooltip {
74
+ position: relative;
75
+ display: inline-block;
76
+ margin: 5px;
77
+ }
78
+ .tooltip .tooltiptext {
79
+ visibility: hidden;
80
+ width: 200px;
81
+ background-color: #555;
82
+ color: #fff;
83
+ text-align: center;
84
+ border-radius: 6px;
85
+ padding: 5px;
86
+ position: absolute;
87
+ z-index: 1;
88
+ bottom: 125%;
89
+ left: 50%;
90
+ margin-left: -100px;
91
+ opacity: 0;
92
+ transition: opacity 0.3s;
93
+ }
94
+ .tooltip:hover .tooltiptext {
95
+ visibility: visible;
96
+ opacity: 1;
97
+ }
98
+ .onboard-brand-button {
99
+ background-color: var(--primary-color, #4A90E2);
100
+ color: white;
101
+ border: none;
102
+ padding: 10px 20px;
103
+ border-radius: 5px;
104
+ cursor: pointer;
105
+ font-size: 16px;
106
+ transition: background-color 0.3s ease;
107
+ }
108
+ .onboard-brand-button:hover {
109
+ background-color: #3a7bc8;
110
+ }
111
+ </style>
112
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
113
+ </head>
114
+ <div>
115
+ <div class="overlay" id="overlay"></div>
116
+ <div class="bottom-sheet" id="onboardBrandSheet">
117
+ <h3 id="sheetTitle">Onboard New Brand</h3>
118
+ <form class="onboard-brand-form" id="onboardBrandForm">
119
+ <div class="form-group">
120
+ <label for="brandKey">
121
+ Brand Key
122
+ <div class="tooltip">
123
+ <i class="fas fa-question-circle question-icon"></i>
124
+ <span class="tooltiptext">Brand key will be added to the brands.json and is used to identify the brand with a unique name. It must match the brand folder in brands.</span>
125
+ </div>
126
+ </label>
127
+ <input type="text" id="brandKey" name="brandKey" required>
128
+ </div>
129
+ <div class="form-group">
130
+ <label for="brandName">
131
+ Brand Name
132
+ <div class="tooltip">
133
+ <i class="fas fa-question-circle question-icon"></i>
134
+ <span class="tooltiptext">The brand name will be added to the brands.json and is used to identify the brand in Solara. It is not used in the actual app.</span>
135
+ </div>
136
+ </label>
137
+ <input type="text" id="brandName" name="brandName" required>
138
+ </div>
139
+ <button id="submitBtn" type="submit" class="onboard-brand-button">Onboard Brand</button>
140
+ </form>
141
+ </div>
142
+ </div>
143
+ `;
144
+ }
145
+
146
+ setupEventListeners() {
147
+ const form = this.shadowRoot.getElementById('onboardBrandForm');
148
+ form.addEventListener('submit', (e) => {
149
+ e.preventDefault();
150
+ this.submit();
151
+ });
152
+ }
153
+
154
+ submit() {
155
+ const brandKey = this.shadowRoot.getElementById('brandKey').value;
156
+ const brandName = this.shadowRoot.getElementById('brandName').value;
157
+
158
+ const brandKeyRegex = /^[A-Za-z][A-Za-z0-9_-]*$/;
159
+
160
+ if (!brandKeyRegex.test(brandKey)) {
161
+ alert('Brand key must start with a letter and contain no spaces. Only letters, numbers, underscores, and hyphens are allowed.');
162
+ return;
163
+ }
164
+
165
+ this.dispatchEvent(new CustomEvent('onboard', {
166
+ detail: {brandKey, brandName},
167
+ bubbles: true,
168
+ composed: true
169
+ }));
170
+
171
+ // Call the onSubmit if it's defined
172
+ if (this.onSubmit) {
173
+ this.onSubmit(brandKey, brandName);
174
+ }
175
+
176
+ this.hide();
177
+ }
178
+
179
+ show(title, submitTitle, onSubmit) {
180
+ this.onSubmit = onSubmit; // Store the onSubmit function
181
+
182
+ // Set the title and submit title in the respective elements
183
+ if (title)
184
+ this.shadowRoot.getElementById('sheetTitle').textContent = title;
185
+ if (submitTitle)
186
+ this.shadowRoot.getElementById('submitBtn').textContent = submitTitle;
187
+
188
+ // Reference the sheet element and display it
189
+ const sheet = this.shadowRoot.getElementById('onboardBrandSheet');
190
+ sheet.style.display = 'block'; // Make the sheet visible
191
+
192
+ // Use a timeout to allow for smooth transitions
193
+ setTimeout(() => {
194
+ sheet.classList.add('show'); // Add 'show' class for CSS transitions
195
+ this.overlay = this.shadowRoot.getElementById('overlay');
196
+ this.overlay.style.display = 'block'; // Show the overlay
197
+
198
+ // Set up the overlay click event to hide the sheet
199
+ this.overlay.onclick = () => this.hide();
200
+ }, 10);
201
+ }
202
+
203
+ hide() {
204
+ const sheet = this.shadowRoot.getElementById('onboardBrandSheet');
205
+ sheet.classList.remove('show');
206
+ setTimeout(() => {
207
+ sheet.style.display = 'none';
208
+ this.overlay.style.display = 'none';
209
+ }, 300);
210
+ }
211
+
212
+ }
213
+
214
+ customElements.define('onboard-bottom-sheet', OnboardBrandBottomSheet);
@@ -0,0 +1,19 @@
1
+ class DashboardManager
2
+
3
+ def start(brand_key = nil, port = 8000)
4
+ Solara.logger.header("Solara Dashboard #{brand_key.nil? || brand_key.empty? ? "" : "for #{brand_key}"}")
5
+ if brand_key.nil? || brand_key.empty?
6
+ open("brands/brands.html?source=local", port)
7
+ Solara.logger.header("Solara Dashboard")
8
+ else
9
+ open("brand/brand.html?brand_key=#{brand_key}&source=local", port)
10
+ Solara.logger.header("Solara Dashboard for #{brand_key}")
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def open(page, port = 8000)
17
+ DashboardServer.new(page, port, FilePath.dashboard).start
18
+ end
19
+ end
@@ -0,0 +1,132 @@
1
+ Dir[File.expand_path('handler/*.rb', __dir__)].each { |file| require_relative file }
2
+ Dir[File.expand_path('../.solara/core/brands/*.rb')].each { |file| require_relative file }
3
+ Dir[File.expand_path('platform/android/*.rb', __dir__)].each { |file| require_relative file }
4
+ Dir[File.expand_path('scripts/*.rb', __dir__)].each { |file| require_relative file }
5
+
6
+ require 'webrick'
7
+ require 'json'
8
+ require 'cgi'
9
+ require 'cgi'
10
+
11
+ class DashboardServer
12
+ attr_reader :home, :root
13
+
14
+ def initialize(home, port, document_root)
15
+ @home = home
16
+ @port = port
17
+ @root = File.expand_path(document_root) || File.expand_path('.')
18
+ @server = nil
19
+ @router = nil
20
+ end
21
+
22
+ def start
23
+ create_server
24
+ setup_router
25
+ add_cors_headers
26
+ print_server_info
27
+ open_browser
28
+ start_server
29
+ end
30
+
31
+ def shutdown
32
+ Solara.logger.debug("Shutting down server...")
33
+ @server.shutdown if @server
34
+ end
35
+
36
+ def handle_error(res, error, message, status = 500)
37
+ Solara.logger.failure("#{message}: #{error.message}")
38
+ res.status = status
39
+ res.body = JSON.generate({ success: false, error: "#{message}: #{error.message}" })
40
+ res.content_type = 'application/json'
41
+ end
42
+
43
+ private
44
+
45
+ def create_server
46
+ logger = WEBrick::Log.new($stderr, Solara.verbose ? WEBrick::Log::DEBUG : WEBrick::Log::INFO)
47
+ @server = WEBrick::HTTPServer.new(
48
+ Port: @port,
49
+ DocumentRoot: @root,
50
+ DirectoryIndex: ['local.html'],
51
+ Logger: logger
52
+ )
53
+ rescue StandardError => e
54
+ Solara.logger.failure("Error creating server: #{e.message}")
55
+ exit(1)
56
+ end
57
+
58
+ def setup_router
59
+ @router = Router.new(@server, self)
60
+ register_handlers
61
+ @router.mount_routes
62
+ end
63
+
64
+ def register_handlers
65
+ return if @router.nil?
66
+
67
+ @router.register_handler(RedirectHandler)
68
+ @router.register_handler(BrandSectionHandler)
69
+ @router.register_handler(SwitchToBrandHandler)
70
+ @router.register_handler(CurrentBrandHandler)
71
+ @router.register_handler(BrandConfigurationsHandler)
72
+ @router.register_handler(BrandsHandler)
73
+ @router.register_handler(EditSectionHandler)
74
+ @router.register_handler(OnboardBrandHandler)
75
+ @router.register_handler(OffboardBrandHandler)
76
+ @router.register_handler(DoctorHandler)
77
+ @router.register_handler(BrandAliasesHandler)
78
+ @router.register_handler(BrandIconHandler)
79
+ end
80
+
81
+ def add_cors_headers
82
+
83
+ end
84
+
85
+ def print_server_info
86
+ Solara.logger.debug("Server starting on http://localhost:#{@port}")
87
+ Solara.logger.debug("Serving files from: #{@root}")
88
+ Solara.logger.debug("Press Ctrl+C to stop the server")
89
+ end
90
+
91
+ def start_server
92
+ trap 'INT' do
93
+ shutdown
94
+ end
95
+
96
+ @server.start
97
+ rescue StandardError => e
98
+ Solara.logger.failure("Error starting server: #{e.message}")
99
+ exit(1)
100
+ end
101
+
102
+ def open_browser
103
+ url = "http://localhost:#{@port}"
104
+ Solara.logger.debug("Opening browser at #{url}")
105
+ case RUBY_PLATFORM
106
+ when /darwin/
107
+ system "/usr/bin/open", url
108
+ when /linux/
109
+ system "/usr/bin/xdg-open", url
110
+ when /mswin|mingw|cygwin/
111
+ system "start", url
112
+ end
113
+ rescue StandardError => e
114
+ Solara.logger.failure("Error opening browser: #{e.message}")
115
+ end
116
+ end
117
+
118
+ class Router
119
+ def initialize(server, dashboard_server)
120
+ @server = server
121
+ @dashboard_server = dashboard_server
122
+ @handlers = []
123
+ end
124
+
125
+ def register_handler(handler_class)
126
+ @handlers << handler_class.new(@server, @dashboard_server)
127
+ end
128
+
129
+ def mount_routes
130
+ @handlers.each(&:mount)
131
+ end
132
+ end