@fleetbase/registry-bridge-engine 0.0.1

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 (211) hide show
  1. package/.php-cs-fixer.php +29 -0
  2. package/LICENSE.md +651 -0
  3. package/README.md +122 -0
  4. package/addon/adapters/registry-bridge.js +5 -0
  5. package/addon/adapters/registry-extension-bundle.js +1 -0
  6. package/addon/adapters/registry-extension.js +1 -0
  7. package/addon/components/extension-card.hbs +12 -0
  8. package/addon/components/extension-card.js +235 -0
  9. package/addon/components/extension-form.hbs +237 -0
  10. package/addon/components/extension-form.js +123 -0
  11. package/addon/components/extension-modal-title.hbs +14 -0
  12. package/addon/components/extension-modal-title.js +20 -0
  13. package/addon/components/extension-monetize-form.hbs +56 -0
  14. package/addon/components/extension-monetize-form.js +7 -0
  15. package/addon/components/extension-pending-publish-viewer.hbs +52 -0
  16. package/addon/components/extension-pending-publish-viewer.js +37 -0
  17. package/addon/components/extension-reviewer-control.hbs +68 -0
  18. package/addon/components/extension-reviewer-control.js +68 -0
  19. package/addon/components/modals/confirm-extension-purchase.hbs +5 -0
  20. package/addon/components/modals/confirm-extension-purchase.js +3 -0
  21. package/addon/components/modals/extension-details.hbs +69 -0
  22. package/addon/components/modals/extension-details.js +33 -0
  23. package/addon/components/modals/extension-purchase-form.hbs +5 -0
  24. package/addon/components/modals/extension-purchase-form.js +3 -0
  25. package/addon/components/modals/extension-uninstall.hbs +25 -0
  26. package/addon/components/modals/extension-uninstall.js +11 -0
  27. package/addon/components/modals/select-extension-bundle.hbs +43 -0
  28. package/addon/components/modals/select-extension-bundle.js +31 -0
  29. package/addon/components/progress-bar.hbs +12 -0
  30. package/addon/components/progress-bar.js +8 -0
  31. package/addon/controllers/application.js +6 -0
  32. package/addon/controllers/developers/analytics.js +26 -0
  33. package/addon/controllers/developers/extensions/edit/bundles.js +70 -0
  34. package/addon/controllers/developers/extensions/edit/index.js +3 -0
  35. package/addon/controllers/developers/extensions/edit/monetize.js +7 -0
  36. package/addon/controllers/developers/extensions/edit.js +107 -0
  37. package/addon/controllers/developers/extensions/index.js +3 -0
  38. package/addon/controllers/developers/extensions/new.js +32 -0
  39. package/addon/controllers/developers/payments/index.js +39 -0
  40. package/addon/controllers/developers/payments/onboard.js +67 -0
  41. package/addon/controllers/explore/category.js +22 -0
  42. package/addon/controllers/explore/index.js +15 -0
  43. package/addon/controllers/installed.js +86 -0
  44. package/addon/controllers/purchased.js +18 -0
  45. package/addon/engine.js +44 -0
  46. package/addon/models/registry-extension-bundle.js +62 -0
  47. package/addon/models/registry-extension.js +215 -0
  48. package/addon/routes/application.js +12 -0
  49. package/addon/routes/developers/analytics.js +10 -0
  50. package/addon/routes/developers/credentials.js +3 -0
  51. package/addon/routes/developers/extensions/edit/bundles.js +21 -0
  52. package/addon/routes/developers/extensions/edit/details.js +3 -0
  53. package/addon/routes/developers/extensions/edit/index.js +3 -0
  54. package/addon/routes/developers/extensions/edit/monetize.js +3 -0
  55. package/addon/routes/developers/extensions/edit.js +18 -0
  56. package/addon/routes/developers/extensions/index.js +10 -0
  57. package/addon/routes/developers/extensions/new.js +3 -0
  58. package/addon/routes/developers/extensions.js +3 -0
  59. package/addon/routes/developers/payments/index.js +26 -0
  60. package/addon/routes/developers/payments/onboard.js +21 -0
  61. package/addon/routes/developers/payments.js +3 -0
  62. package/addon/routes/developers.js +3 -0
  63. package/addon/routes/explore/category.js +27 -0
  64. package/addon/routes/explore/index.js +17 -0
  65. package/addon/routes/explore.js +3 -0
  66. package/addon/routes/installed.js +10 -0
  67. package/addon/routes/purchased.js +10 -0
  68. package/addon/routes.js +28 -0
  69. package/addon/serializers/registry-extension-bundle.js +15 -0
  70. package/addon/serializers/registry-extension.js +21 -0
  71. package/addon/services/stripe.js +83 -0
  72. package/addon/styles/registry-bridge-engine.css +142 -0
  73. package/addon/templates/application.hbs +26 -0
  74. package/addon/templates/developers/analytics.hbs +83 -0
  75. package/addon/templates/developers/credentials.hbs +1 -0
  76. package/addon/templates/developers/extensions/edit/bundles.hbs +71 -0
  77. package/addon/templates/developers/extensions/edit/details.hbs +16 -0
  78. package/addon/templates/developers/extensions/edit/index.hbs +1 -0
  79. package/addon/templates/developers/extensions/edit/monetize.hbs +3 -0
  80. package/addon/templates/developers/extensions/edit.hbs +48 -0
  81. package/addon/templates/developers/extensions/index.hbs +27 -0
  82. package/addon/templates/developers/extensions/new.hbs +39 -0
  83. package/addon/templates/developers/extensions.hbs +1 -0
  84. package/addon/templates/developers/payments/index.hbs +33 -0
  85. package/addon/templates/developers/payments/onboard.hbs +48 -0
  86. package/addon/templates/developers/payments.hbs +1 -0
  87. package/addon/templates/developers.hbs +1 -0
  88. package/addon/templates/explore/category.hbs +12 -0
  89. package/addon/templates/explore/index.hbs +12 -0
  90. package/addon/templates/explore.hbs +1 -0
  91. package/addon/templates/installed.hbs +32 -0
  92. package/addon/templates/purchased.hbs +34 -0
  93. package/app/adapters/registry-bridge.js +1 -0
  94. package/app/adapters/registry-extension-bundle.js +1 -0
  95. package/app/adapters/registry-extension.js +1 -0
  96. package/app/components/extension-card.js +1 -0
  97. package/app/components/extension-form.js +1 -0
  98. package/app/components/extension-modal-title.js +1 -0
  99. package/app/components/extension-monetize-form.js +1 -0
  100. package/app/components/extension-pending-publish-viewer.js +1 -0
  101. package/app/components/extension-reviewer-control.js +1 -0
  102. package/app/components/modals/confirm-extension-purchase.js +1 -0
  103. package/app/components/modals/extension-details.js +1 -0
  104. package/app/components/modals/extension-purchase-form.js +1 -0
  105. package/app/components/modals/extension-uninstall.js +1 -0
  106. package/app/components/modals/select-extension-bundle.js +1 -0
  107. package/app/components/progress-bar.js +1 -0
  108. package/app/controllers/application.js +1 -0
  109. package/app/controllers/developers/analytics.js +1 -0
  110. package/app/controllers/developers/extensions/edit/bundles.js +1 -0
  111. package/app/controllers/developers/extensions/edit/index.js +1 -0
  112. package/app/controllers/developers/extensions/edit/monetize.js +1 -0
  113. package/app/controllers/developers/extensions/edit.js +1 -0
  114. package/app/controllers/developers/extensions/index.js +1 -0
  115. package/app/controllers/developers/extensions/new.js +1 -0
  116. package/app/controllers/developers/payments/index.js +1 -0
  117. package/app/controllers/developers/payments/onboard.js +1 -0
  118. package/app/controllers/explore/category.js +1 -0
  119. package/app/controllers/explore/index.js +1 -0
  120. package/app/controllers/installed.js +1 -0
  121. package/app/controllers/purchased.js +1 -0
  122. package/app/models/registry-extension-bundle.js +1 -0
  123. package/app/models/registry-extension.js +1 -0
  124. package/app/routes/developers/analytics.js +1 -0
  125. package/app/routes/developers/credentials.js +1 -0
  126. package/app/routes/developers/extensions/edit/bundles.js +1 -0
  127. package/app/routes/developers/extensions/edit/details.js +1 -0
  128. package/app/routes/developers/extensions/edit/index.js +1 -0
  129. package/app/routes/developers/extensions/edit/monetize.js +1 -0
  130. package/app/routes/developers/extensions/edit.js +1 -0
  131. package/app/routes/developers/extensions/index.js +1 -0
  132. package/app/routes/developers/extensions/new.js +1 -0
  133. package/app/routes/developers/extensions.js +1 -0
  134. package/app/routes/developers/payments/index.js +1 -0
  135. package/app/routes/developers/payments/onboard.js +1 -0
  136. package/app/routes/developers/payments.js +1 -0
  137. package/app/routes/developers.js +1 -0
  138. package/app/routes/explore/category.js +1 -0
  139. package/app/routes/explore/index.js +1 -0
  140. package/app/routes/explore.js +1 -0
  141. package/app/routes/installed.js +1 -0
  142. package/app/routes/purchased.js +1 -0
  143. package/app/serializers/registry-extension-bundle.js +1 -0
  144. package/app/serializers/registry-extension.js +1 -0
  145. package/app/services/stripe.js +1 -0
  146. package/app/templates/developers/analytics.js +1 -0
  147. package/app/templates/developers/credentials.js +1 -0
  148. package/app/templates/developers/extensions/edit/bundles.js +1 -0
  149. package/app/templates/developers/extensions/edit/details.js +1 -0
  150. package/app/templates/developers/extensions/edit/index.js +1 -0
  151. package/app/templates/developers/extensions/edit/monetize.js +1 -0
  152. package/app/templates/developers/extensions/edit.js +1 -0
  153. package/app/templates/developers/extensions/index.js +1 -0
  154. package/app/templates/developers/extensions/new.js +1 -0
  155. package/app/templates/developers/extensions.js +1 -0
  156. package/app/templates/developers/payments/index.js +1 -0
  157. package/app/templates/developers/payments/onboard.js +1 -0
  158. package/app/templates/developers/payments.js +1 -0
  159. package/app/templates/developers.js +1 -0
  160. package/app/templates/explore/category.js +1 -0
  161. package/app/templates/explore/index.js +1 -0
  162. package/app/templates/explore.js +1 -0
  163. package/app/templates/installed.js +1 -0
  164. package/app/templates/purchased.js +1 -0
  165. package/composer.json +95 -0
  166. package/config/environment.js +28 -0
  167. package/extension.json +10 -0
  168. package/index.js +26 -0
  169. package/package.json +129 -0
  170. package/phpstan.neon.dist +8 -0
  171. package/phpunit.xml.dist +16 -0
  172. package/server/.gitattributes +14 -0
  173. package/server/config/registry-bridge.php +32 -0
  174. package/server/migrations/2024_03_19_060627_create_registry_users_table.php +42 -0
  175. package/server/migrations/2024_03_21_051614_create_registry_extensions_table.php +76 -0
  176. package/server/migrations/2024_03_25_044537_create_registry_extension_bundles_table.php +54 -0
  177. package/server/migrations/2024_03_29_072101_registry_extension_installs.php +35 -0
  178. package/server/migrations/2024_07_16_155000_create_registry_extension_purchases.php +41 -0
  179. package/server/seeders/ExtensionsCategorySeeder.php +359 -0
  180. package/server/src/Console/Commands/Initialize.php +35 -0
  181. package/server/src/Console/Commands/PostInstallExtension.php +84 -0
  182. package/server/src/Exceptions/InstallFailedException.php +21 -0
  183. package/server/src/Expansions/CategoryExpansion.php +30 -0
  184. package/server/src/Http/Controllers/Internal/v1/ExtensionInstallerController.php +153 -0
  185. package/server/src/Http/Controllers/Internal/v1/RegistryAuthController.php +230 -0
  186. package/server/src/Http/Controllers/Internal/v1/RegistryController.php +54 -0
  187. package/server/src/Http/Controllers/Internal/v1/RegistryExtensionBundleController.php +112 -0
  188. package/server/src/Http/Controllers/Internal/v1/RegistryExtensionController.php +257 -0
  189. package/server/src/Http/Controllers/Internal/v1/RegistryPaymentsController.php +227 -0
  190. package/server/src/Http/Controllers/RegistryBridgeController.php +13 -0
  191. package/server/src/Http/Filter/RegistryExtensionFilter.php +80 -0
  192. package/server/src/Http/Requests/AddRegistryUserRequest.php +47 -0
  193. package/server/src/Http/Requests/AuthenticateRegistryUserRequest.php +47 -0
  194. package/server/src/Http/Requests/CreateRegistryExtensionBundleRequest.php +42 -0
  195. package/server/src/Http/Requests/CreateRegistryExtensionRequest.php +31 -0
  196. package/server/src/Http/Requests/InstallExtensionRequest.php +30 -0
  197. package/server/src/Http/Requests/RegistryAuthRequest.php +46 -0
  198. package/server/src/Http/Requests/RegistryExtensionActionRequest.php +30 -0
  199. package/server/src/Http/Resources/RegistryUser.php +40 -0
  200. package/server/src/Models/RegistryExtension.php +656 -0
  201. package/server/src/Models/RegistryExtensionBundle.php +1015 -0
  202. package/server/src/Models/RegistryExtensionInstall.php +76 -0
  203. package/server/src/Models/RegistryExtensionPurchase.php +87 -0
  204. package/server/src/Models/RegistryUser.php +140 -0
  205. package/server/src/Providers/RegistryBridgeServiceProvider.php +117 -0
  206. package/server/src/Support/Bridge.php +53 -0
  207. package/server/src/Support/Utils.php +19 -0
  208. package/server/src/routes.php +58 -0
  209. package/server/tests/Feature.php +5 -0
  210. package/translations/en-us.yaml +119 -0
  211. package/tsconfig.declarations.json +10 -0
@@ -0,0 +1,123 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class ExtensionFormComponent extends Component {
8
+ @service store;
9
+ @service fetch;
10
+ @service notifications;
11
+ @service intl;
12
+ @service modalsManager;
13
+ @tracked subscriptionModelOptions = ['flat_rate', 'tiered', 'usage'];
14
+ @tracked billingPeriodOptions = ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'];
15
+ @tracked uploadQueue = [];
16
+ acceptedImageTypes = ['image/jpeg', 'image/png', 'image/gif'];
17
+ acceptedBundleTypes = [
18
+ 'application/zip',
19
+ 'application/x-zip',
20
+ 'application/x-zip-compressed',
21
+ 'application/x-compressed',
22
+ 'multipart/x-zip',
23
+ 'application/x-tar',
24
+ 'application/gzip',
25
+ 'application/x-gzip',
26
+ 'application/x-tgz',
27
+ 'application/x-bzip2',
28
+ 'application/x-xz',
29
+ ];
30
+
31
+ @task *uploadIcon(file) {
32
+ const { extension, onIconUploaded } = this.args;
33
+
34
+ yield this.fetch.uploadFile.perform(
35
+ file,
36
+ {
37
+ path: `uploads/extensions/${extension.id}/icons`,
38
+ subject_uuid: extension.id,
39
+ subject_type: 'registry-bridge:registry-extension',
40
+ type: 'extension_icon',
41
+ },
42
+ (uploadedFile) => {
43
+ extension.setProperties({
44
+ icon: uploadedFile,
45
+ icon_uuid: uploadedFile.id,
46
+ icon_url: uploadedFile.url,
47
+ });
48
+
49
+ if (typeof onIconUploaded === 'function') {
50
+ onIconUploaded(uploadedFile);
51
+ }
52
+
53
+ return extension.save();
54
+ }
55
+ );
56
+ }
57
+
58
+ @action queueFile(file) {
59
+ // since we have dropzone and upload button within dropzone validate the file state first
60
+ // as this method can be called twice from both functions
61
+ if (['queued', 'failed', 'timed_out', 'aborted'].indexOf(file.state) === -1) {
62
+ return;
63
+ }
64
+
65
+ const { extension, onScreenshotUploaded } = this.args;
66
+
67
+ // Queue and upload immediatley
68
+ this.uploadQueue.pushObject(file);
69
+ this.fetch.uploadFile.perform(
70
+ file,
71
+ {
72
+ path: `uploads/extensions/${extension.id}/screenshots`,
73
+ subject_uuid: extension.id,
74
+ subject_type: 'registry-bridge:registry-extension',
75
+ type: 'extension_screenshot',
76
+ },
77
+ (uploadedFile) => {
78
+ extension.screenshots.pushObject(uploadedFile);
79
+ this.uploadQueue.removeObject(file);
80
+ if (typeof onScreenshotUploaded === 'function') {
81
+ onScreenshotUploaded(uploadedFile);
82
+ }
83
+ },
84
+ () => {
85
+ this.uploadQueue.removeObject(file);
86
+ // remove file from queue
87
+ if (file.queue && typeof file.queue.remove === 'function') {
88
+ file.queue.remove(file);
89
+ }
90
+ }
91
+ );
92
+ }
93
+
94
+ @action selectBundle() {
95
+ const { extension, onBundleSelected } = this.args;
96
+
97
+ this.modalsManager.show('modals/select-extension-bundle', {
98
+ title: 'Select extension bundle',
99
+ modalClass: 'modal-md',
100
+ acceptButtonText: 'Done',
101
+ hideDeclineButton: true,
102
+ extension,
103
+ onBundleSelected: (bundle) => {
104
+ extension.setProperties({
105
+ next_bundle_uuid: bundle.id,
106
+ next_bundle_id: bundle.bundle_id,
107
+ next_bundle_filename: bundle.bundle_filename,
108
+ next_bundle: bundle,
109
+ });
110
+
111
+ if (typeof onBundleSelected === 'function') {
112
+ onBundleSelected(bundle);
113
+ }
114
+
115
+ this.modalsManager.done();
116
+ },
117
+ });
118
+ }
119
+
120
+ @action removeFile(file) {
121
+ return file.destroyRecord();
122
+ }
123
+ }
@@ -0,0 +1,14 @@
1
+ <div class="flex flex-row" ...attributes {{did-update this.handleExtensionChanged @extension}}>
2
+ <div class="modal-title--extension-icon-wrapper shadow-sm dark:border-gray-700">
3
+ <div class="flex items-center justify-center rounded-lg w-full" {{background-url this.extension.icon_url overlay=true}}>
4
+ <Image src={{this.extension.icon_url}} class="w-full rounded-lg" alt={{this.extension.name}} @fallbackSrc={{config "defaultValues.extensionIcon"}} />
5
+ </div>
6
+ </div>
7
+ <div class="flex-1">
8
+ <div class={{this.detailsContainerClass}}>
9
+ <h1 class="dark:text-white text-black font-semibold">{{this.extension.name}}</h1>
10
+ <div class="dark:text-gray-300 text-black">{{this.extension.subtitle}}</div>
11
+ <div class="dark:text-gray-300 text-black text-sm">{{concat "Published by " this.extension.publisher_name}}</div>
12
+ </div>
13
+ </div>
14
+ </div>
@@ -0,0 +1,20 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { action } from '@ember/object';
4
+
5
+ export default class ExtensionModalTitleComponent extends Component {
6
+ @tracked extension;
7
+ @tracked detailsContainerClass = 'mb-4';
8
+
9
+ constructor(owner, { options, extension = null, detailsContainerClass = 'mb-4' }) {
10
+ super(...arguments);
11
+ this.extension = options ? options.extension : extension;
12
+ this.detailsContainerClass = detailsContainerClass;
13
+ }
14
+
15
+ @action handleExtensionChanged(el, [extension]) {
16
+ if (extension) {
17
+ this.extension = extension;
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,56 @@
1
+ <ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.extension-payment-details"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
2
+ <InputGroup @wrapperClass={{unless @extension.payment_required "mb-0i"}}>
3
+ <Toggle
4
+ @isToggled={{@extension.payment_required}}
5
+ @onToggle={{fn (mut @extension.payment_required)}}
6
+ @label={{t "registry-bridge.developers.extensions.extension-form.extension-payment-required"}}
7
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-payment-required-help-text"}}
8
+ />
9
+ </InputGroup>
10
+ {{#if @extension.payment_required}}
11
+ <InputGroup>
12
+ <Toggle
13
+ @isToggled={{@extension.subscription_required}}
14
+ @onToggle={{fn (mut @extension.subscription_required)}}
15
+ @label={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-required"}}
16
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-required-help-text"}}
17
+ />
18
+ </InputGroup>
19
+ {{#if @extension.subscription_required}}
20
+ <InputGroup
21
+ @name={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-billing-period"}}
22
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-billing-period-help-text"}}
23
+ >
24
+ <Select
25
+ @value={{@extension.subscription_billing_period}}
26
+ @options={{this.billingPeriodOptions}}
27
+ @onSelect={{fn (mut @extension.subscription_billing_period)}}
28
+ @placeholder={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-billing-period-placeholder"}}
29
+ class="w-full"
30
+ />
31
+ </InputGroup>
32
+ <InputGroup
33
+ @name={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-amount"}}
34
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-subscription-amount-help-text"}}
35
+ @wrapperClass="mb-0i"
36
+ >
37
+ <MoneyInput @value={{@extension.subscription_amount}} @currency="USD" />
38
+ </InputGroup>
39
+ {{else}}
40
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-2">
41
+ <InputGroup
42
+ @name={{t "registry-bridge.developers.extensions.extension-form.extension-price"}}
43
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-price-help-text"}}
44
+ >
45
+ <MoneyInput @value={{@extension.price}} @currency="USD" />
46
+ </InputGroup>
47
+ <InputGroup
48
+ @name={{t "registry-bridge.developers.extensions.extension-form.extension-sale-price"}}
49
+ @helpText={{t "registry-bridge.developers.extensions.extension-form.extension-sale-price-help-text"}}
50
+ >
51
+ <MoneyInput @value={{@extension.sale_price}} @currency="USD" />
52
+ </InputGroup>
53
+ </div>
54
+ {{/if}}
55
+ {{/if}}
56
+ </ContentPanel>
@@ -0,0 +1,7 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+
4
+ export default class ExtensionMonetizeFormComponent extends Component {
5
+ @tracked subscriptionModelOptions = ['flat_rate', 'tiered', 'usage'];
6
+ @tracked billingPeriodOptions = ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'];
7
+ }
@@ -0,0 +1,52 @@
1
+ <div class="space-y-4">
2
+ <ContentPanel @title={{t "registry-bridge.component.extension-pending-publish-viewer.content-panel-title"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
3
+ <div class="grid grid-cols-1 lg:grid-cols-4">
4
+ {{#each this.extensions as |extension|}}
5
+ <div class="flex flex-col">
6
+ <button type="button" class="rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm w-full hover:opacity-50" {{on "click" (fn this.focusExtension extension)}}>
7
+ <div class="flex items-center justify-center rounded-t-lg w-full h-36" {{background-url extension.icon_url overlay=true}}>
8
+ <Image src={{extension.icon_url}} class="w-full h-36 rounded-t-lg" alt={{extension.name}} @fallbackSrc={{config "defaultValues.extensionIcon"}} />
9
+ </div>
10
+ <div class="text-left px-3 py-2 rounded-b-lg bg-white border-t border-gray-200 dark:border-gray-700 dark:bg-gray-900">
11
+ <span class="font-semibold text-sm block">{{extension.name}}</span>
12
+ <p class="text-xs">{{n-a extension.description}}</p>
13
+ <div>
14
+ <Badge @status={{extension.status}} />
15
+ </div>
16
+ </div>
17
+ </button>
18
+ <div class="space-y-2 mt-3">
19
+ <Button
20
+ @size="sm"
21
+ @icon="clipboard-list"
22
+ @text={{t "registry-bridge.component.extension-pending-publish-viewer.view-details"}}
23
+ @onClick={{fn this.focusExtension extension}}
24
+ class="w-full"
25
+ />
26
+ <Button
27
+ @size="sm"
28
+ @icon="download"
29
+ @text={{t "registry-bridge.component.extension-pending-publish-viewer.download-bundle"}}
30
+ @onClick={{perform this.downloadBundle extension}}
31
+ class="w-full"
32
+ />
33
+ </div>
34
+ </div>
35
+ {{else}}
36
+ <div class="col-span-4">
37
+ <div class="text-base italic">{{t "registry-bridge.component.extension-pending-publish-viewer.no-extensions-awaiting-publish"}}</div>
38
+ </div>
39
+ {{/each}}
40
+ </div>
41
+ </ContentPanel>
42
+
43
+ {{#if this.focusedExtension}}
44
+ <ContentPanel @title={{t "registry-bridge.component.extension-pending-publish-viewer.focused-extension-title" extensionName=this.focusedExtension.name}} @open={{true}} @pad={{true}}>
45
+ <div class="flex items-center mb-4 px-1">
46
+ <Button @type="primary" @size="sm" @icon="check" @text={{t "common.done"}} @onClick={{this.unfocusExtension}} class="w-full" />
47
+ </div>
48
+ <ExtensionForm @extension={{this.focusedExtension}} @withMonetizeForm={{true}} />
49
+ </ContentPanel>
50
+ {{/if}}
51
+ </div>
52
+ <Spacer @height="400px" />
@@ -0,0 +1,37 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class ExtensionPendingPublishViewerComponent extends Component {
8
+ @service store;
9
+ @service notifications;
10
+ @tracked extensions = [];
11
+ @tracked focusedExtension;
12
+
13
+ constructor() {
14
+ super(...arguments);
15
+ this.getExtensionsPendingPublish.perform();
16
+ }
17
+
18
+ @task *getExtensionsPendingPublish() {
19
+ this.extensions = yield this.store.query('registry-extension', { status: 'approved' });
20
+ }
21
+
22
+ @task *downloadBundle(extension) {
23
+ try {
24
+ yield extension.downloadBundle();
25
+ } catch (error) {
26
+ this.notifications.error(error.message);
27
+ }
28
+ }
29
+
30
+ @action focusExtension(extension) {
31
+ this.focusedExtension = extension;
32
+ }
33
+
34
+ @action unfocusExtension() {
35
+ this.focusedExtension = undefined;
36
+ }
37
+ }
@@ -0,0 +1,68 @@
1
+ <div class="space-y-4">
2
+ <ContentPanel @title={{t "registry-bridge.component.extension-reviewer-control.content-panel-title"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
3
+ <div class="grid grid-cols-1 lg:grid-cols-4">
4
+ {{#each this.extensions as |extension|}}
5
+ <div class="flex flex-col">
6
+ <button type="button" class="rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm w-full hover:opacity-50" {{on "click" (fn this.focusExtension extension)}}>
7
+ <div class="flex items-center justify-center rounded-t-lg w-full h-36" {{background-url extension.icon_url overlay=true}}>
8
+ <Image src={{extension.icon_url}} class="w-full h-36 rounded-t-lg" alt={{extension.name}} @fallbackSrc={{config "defaultValues.extensionIcon"}} />
9
+ </div>
10
+ <div class="text-left px-3 py-2 rounded-b-lg bg-white border-t border-gray-200 dark:border-gray-700 dark:bg-gray-900">
11
+ <span class="font-semibold text-sm block">{{extension.name}}</span>
12
+ <p class="text-xs">{{n-a extension.description}}</p>
13
+ <div>
14
+ <Badge @status={{extension.status}} />
15
+ </div>
16
+ </div>
17
+ </button>
18
+ <div class="space-y-2 mt-3">
19
+ <Button
20
+ @size="sm"
21
+ @icon="clipboard-list"
22
+ @text={{t "registry-bridge.component.extension-reviewer-control.view-details"}}
23
+ @onClick={{fn this.focusExtension extension}}
24
+ class="w-full"
25
+ />
26
+ <Button
27
+ @size="sm"
28
+ @icon="download"
29
+ @text={{t "registry-bridge.component.extension-reviewer-control.download-bundle"}}
30
+ @onClick={{perform this.downloadBundle extension}}
31
+ class="w-full"
32
+ />
33
+ <Button
34
+ @type="success"
35
+ @size="sm"
36
+ @icon="check"
37
+ @text={{t "registry-bridge.component.extension-reviewer-control.approve"}}
38
+ @onClick={{fn this.approve extension}}
39
+ class="w-full"
40
+ />
41
+ <Button
42
+ @type="danger"
43
+ @size="sm"
44
+ @icon="ban"
45
+ @text={{t "registry-bridge.component.extension-reviewer-control.reject"}}
46
+ @onClick={{fn this.reject extension}}
47
+ class="w-full"
48
+ />
49
+ </div>
50
+ </div>
51
+ {{else}}
52
+ <div class="col-span-4">
53
+ <div class="text-base italic">{{t "registry-bridge.component.extension-reviewer-control.no-extensions-awaiting-review"}}</div>
54
+ </div>
55
+ {{/each}}
56
+ </div>
57
+ </ContentPanel>
58
+
59
+ {{#if this.focusedExtension}}
60
+ <ContentPanel @title={{t "registry-bridge.component.extension-reviewer-control.focused-extension-title" extensionName=this.focusedExtension.name}} @open={{true}} @pad={{true}}>
61
+ <div class="flex items-center mb-4 px-1">
62
+ <Button @type="primary" @size="sm" @icon="check" @text={{t "common.done"}} @onClick={{this.unfocusExtension}} class="w-full" />
63
+ </div>
64
+ <ExtensionForm @extension={{this.focusedExtension}} @withMonetizeForm={{true}} />
65
+ </ContentPanel>
66
+ {{/if}}
67
+ </div>
68
+ <Spacer @height="400px" />
@@ -0,0 +1,68 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class ExtensionReviewerControlComponent extends Component {
8
+ @service store;
9
+ @service modalsManager;
10
+ @service notifications;
11
+ @service intl;
12
+ @tracked extensions = [];
13
+ @tracked focusedExtension;
14
+
15
+ constructor() {
16
+ super(...arguments);
17
+ this.getExtensionsPendingReview.perform();
18
+ }
19
+
20
+ @task *getExtensionsPendingReview() {
21
+ this.extensions = yield this.store.query('registry-extension', { status: 'awaiting_review' });
22
+ }
23
+
24
+ @task *downloadBundle(extension) {
25
+ try {
26
+ yield extension.downloadBundle();
27
+ } catch (error) {
28
+ this.notifications.error(error.message);
29
+ }
30
+ }
31
+
32
+ @action focusExtension(extension) {
33
+ this.focusedExtension = extension;
34
+ }
35
+
36
+ @action unfocusExtension() {
37
+ this.focusedExtension = undefined;
38
+ }
39
+
40
+ @action approve(extension) {
41
+ return this.modalsManager.confirm({
42
+ title: this.intl.t('registry-bridge.component.extension-reviewer-control.approve-confirm-title', { extensionName: extension.name }),
43
+ body: this.intl.t('registry-bridge.component.extension-reviewer-control.approve-confirm-body'),
44
+ acceptButtonText: this.intl.t('registry-bridge.component.extension-reviewer-control.approve'),
45
+ confirm: () => {
46
+ this.unfocusExtension();
47
+ return extension.approve().finally(() => {
48
+ this.getExtensionsPendingReview.perform();
49
+ });
50
+ },
51
+ });
52
+ }
53
+
54
+ @action reject(extension) {
55
+ return this.modalsManager.confirm({
56
+ title: this.intl.t('registry-bridge.component.extension-reviewer-control.decline-confirm-title', { extensionName: extension.name }),
57
+ body: this.intl.t('registry-bridge.component.extension-reviewer-control.decline-confirm-body'),
58
+ acceptButtonText: this.intl.t('registry-bridge.component.extension-reviewer-control.reject'),
59
+ acceptButtonIcon: 'ban',
60
+ acceptButtonScheme: 'danger',
61
+ confirm: () => {
62
+ return extension.reject().finally(() => {
63
+ this.getExtensionsPendingReview.perform();
64
+ });
65
+ },
66
+ });
67
+ }
68
+ }
@@ -0,0 +1,5 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="pt-4 pb-6 px-4">
3
+ <Spinner @loadingMessage={{@options.loadingMessage}} @loadingMessageClass="ml-2 text-black dark:text-white" @wrapperClass="flex flex-row items-center" />
4
+ </div>
5
+ </Modal::Default>
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class ModalsConfirmExtensionPurchaseComponent extends Component {}
@@ -0,0 +1,69 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container space-y-3">
3
+ <div class="flex flex-row items-center space-x-2">
4
+ <div class="flb--extension-tag shadow-sm border dark:bg-gray-800 dark:border-gray-800 dark:text-gray-200">
5
+ Extension
6
+ </div>
7
+ <div class="flb--extension-tag shadow-sm border dark:bg-gray-800 dark:border-gray-800 dark:text-gray-200">
8
+ {{this.extension.category_name}}
9
+ </div>
10
+ </div>
11
+ {{#if @options.progress}}
12
+ <div class="my-4">
13
+ <ProgressBar @title={{@options.stepDescription}} @percent={{@options.progress}} />
14
+ </div>
15
+ {{/if}}
16
+ {{#if this.extension.screenshots}}
17
+ <div class="grid grid-cols-4 gap-2">
18
+ {{#each this.extension.screenshots as |screenshot|}}
19
+ <Image src={{screenshot.url}} class="flb--extension-screenshot" {{on "click" (fn this.lightboxScreenshot screenshot)}} />
20
+ {{/each}}
21
+ </div>
22
+ {{#if this.screenshotInLightbox}}
23
+ <EmberWormhole @to="console-wormhole">
24
+ <div class="flb--extension-screenshot-lightbox" {{did-insert this.setupScreenshotLightbox}}>
25
+ <Image src={{this.screenshotInLightbox.url}} class="flb--extension-screenshot" {{on "click" this.lightboxScreenshot}} />
26
+ </div>
27
+ </EmberWormhole>
28
+ {{/if}}
29
+ {{/if}}
30
+ <div>
31
+ <h3 class="dark:text-white font-semibold mb-1">Overview</h3>
32
+ <div class="space-y-1">
33
+ <p class="dark:text-gray-200 text-sm">{{this.extension.description}}</p>
34
+ <p class="dark:text-gray-200 text-sm">{{this.extension.promotional_text}}</p>
35
+ </div>
36
+ </div>
37
+ <div>
38
+ <h3 class="dark:text-white font-semibold mb-1">Details</h3>
39
+ <div class="space-y-2">
40
+ <div class="grid grid-cols-4 gap-2">
41
+ <div class="space-y-2">
42
+ <div>
43
+ <div class="text-sm font-semibold dark:text-gray-100">Version</div>
44
+ <div class="text-sm dark:text-gray-200">{{this.extension.version}}</div>
45
+ </div>
46
+ <div>
47
+ <div class="text-sm font-semibold dark:text-gray-100">Updated</div>
48
+ <div class="text-sm dark:text-gray-200">{{this.extension.updatedAt}}</div>
49
+ </div>
50
+ </div>
51
+ <div class="space-y-2">
52
+ <div>
53
+ <div class="text-sm font-semibold dark:text-gray-100">Author</div>
54
+ <div class="text-sm dark:text-gray-200">{{this.extension.publisher_name}}</div>
55
+ </div>
56
+ <div>
57
+ <div class="text-sm font-semibold dark:text-gray-100">Website</div>
58
+ <a
59
+ class="text-sm dark:text-gray-200"
60
+ href={{this.extension.website_url}}
61
+ target={{dasherize (concat this.extension.name "-website")}}
62
+ >{{this.extension.website_url}}</a>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </Modal::Default>
@@ -0,0 +1,33 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { action } from '@ember/object';
4
+ import { later } from '@ember/runloop';
5
+
6
+ export default class ModalsExtensionDetailsComponent extends Component {
7
+ @tracked extension;
8
+ @tracked screenshotInLightbox;
9
+
10
+ constructor(owner, { options }) {
11
+ super(...arguments);
12
+ this.extension = options.extension;
13
+ }
14
+
15
+ @action lightboxScreenshot(screenshot) {
16
+ this.screenshotInLightbox = screenshot;
17
+ }
18
+
19
+ @action setupScreenshotLightbox() {
20
+ const listener = () => {
21
+ this.screenshotInLightbox = undefined;
22
+ window.removeEventListener('click', listener);
23
+ };
24
+
25
+ later(
26
+ this,
27
+ () => {
28
+ window.addEventListener('click', listener);
29
+ },
30
+ 600
31
+ );
32
+ }
33
+ }
@@ -0,0 +1,5 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="p-1">
3
+ <div id="checkout" {{did-insert @options.checkoutElementInserted}}></div>
4
+ </div>
5
+ </Modal::Default>
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class ModalsExtensionPurchaseFormComponent extends Component {}
@@ -0,0 +1,25 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <div class="flex flex-row py-4">
4
+ <div class="w-60 flex justify-center">
5
+ <FaIcon @icon="triangle-exclamation" @size="3x" class="text-red-500" />
6
+ </div>
7
+ <div class="flex flex-col">
8
+ <h2 class="dark:text-white font-bold mb-2">Proceed with Uninstall?</h2>
9
+ <p class="dark:text-gray-200 text-sm">
10
+ Do you really want to uninstall the
11
+ {{this.extension.name}}? By proceeding, you will permanently delete all settings and data related to this extension. You can reinstall the extension anytime in the
12
+ future, but configuration and data will not be recovered.
13
+ </p>
14
+ <div class="mt-6 px-4 py-3 border border-gray-200 dark:border-gray-900 bg-gray-50 dark:bg-gray-700 rounded-lg">
15
+ <ExtensionModalTitle @options={{@options}} @detailsContainerClass="mb-0" />
16
+ </div>
17
+ </div>
18
+ </div>
19
+ {{#if @options.progress}}
20
+ <div class="my-4">
21
+ <ProgressBar @title={{@options.stepDescription}} @percent={{@options.progress}} />
22
+ </div>
23
+ {{/if}}
24
+ </div>
25
+ </Modal::Default>
@@ -0,0 +1,11 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+
4
+ export default class ModalsExtensionUninstallComponent extends Component {
5
+ @tracked extension;
6
+
7
+ constructor(owner, { options }) {
8
+ super(...arguments);
9
+ this.extension = options.extension;
10
+ }
11
+ }