@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.
- package/.php-cs-fixer.php +29 -0
- package/LICENSE.md +651 -0
- package/README.md +122 -0
- package/addon/adapters/registry-bridge.js +5 -0
- package/addon/adapters/registry-extension-bundle.js +1 -0
- package/addon/adapters/registry-extension.js +1 -0
- package/addon/components/extension-card.hbs +12 -0
- package/addon/components/extension-card.js +235 -0
- package/addon/components/extension-form.hbs +237 -0
- package/addon/components/extension-form.js +123 -0
- package/addon/components/extension-modal-title.hbs +14 -0
- package/addon/components/extension-modal-title.js +20 -0
- package/addon/components/extension-monetize-form.hbs +56 -0
- package/addon/components/extension-monetize-form.js +7 -0
- package/addon/components/extension-pending-publish-viewer.hbs +52 -0
- package/addon/components/extension-pending-publish-viewer.js +37 -0
- package/addon/components/extension-reviewer-control.hbs +68 -0
- package/addon/components/extension-reviewer-control.js +68 -0
- package/addon/components/modals/confirm-extension-purchase.hbs +5 -0
- package/addon/components/modals/confirm-extension-purchase.js +3 -0
- package/addon/components/modals/extension-details.hbs +69 -0
- package/addon/components/modals/extension-details.js +33 -0
- package/addon/components/modals/extension-purchase-form.hbs +5 -0
- package/addon/components/modals/extension-purchase-form.js +3 -0
- package/addon/components/modals/extension-uninstall.hbs +25 -0
- package/addon/components/modals/extension-uninstall.js +11 -0
- package/addon/components/modals/select-extension-bundle.hbs +43 -0
- package/addon/components/modals/select-extension-bundle.js +31 -0
- package/addon/components/progress-bar.hbs +12 -0
- package/addon/components/progress-bar.js +8 -0
- package/addon/controllers/application.js +6 -0
- package/addon/controllers/developers/analytics.js +26 -0
- package/addon/controllers/developers/extensions/edit/bundles.js +70 -0
- package/addon/controllers/developers/extensions/edit/index.js +3 -0
- package/addon/controllers/developers/extensions/edit/monetize.js +7 -0
- package/addon/controllers/developers/extensions/edit.js +107 -0
- package/addon/controllers/developers/extensions/index.js +3 -0
- package/addon/controllers/developers/extensions/new.js +32 -0
- package/addon/controllers/developers/payments/index.js +39 -0
- package/addon/controllers/developers/payments/onboard.js +67 -0
- package/addon/controllers/explore/category.js +22 -0
- package/addon/controllers/explore/index.js +15 -0
- package/addon/controllers/installed.js +86 -0
- package/addon/controllers/purchased.js +18 -0
- package/addon/engine.js +44 -0
- package/addon/models/registry-extension-bundle.js +62 -0
- package/addon/models/registry-extension.js +215 -0
- package/addon/routes/application.js +12 -0
- package/addon/routes/developers/analytics.js +10 -0
- package/addon/routes/developers/credentials.js +3 -0
- package/addon/routes/developers/extensions/edit/bundles.js +21 -0
- package/addon/routes/developers/extensions/edit/details.js +3 -0
- package/addon/routes/developers/extensions/edit/index.js +3 -0
- package/addon/routes/developers/extensions/edit/monetize.js +3 -0
- package/addon/routes/developers/extensions/edit.js +18 -0
- package/addon/routes/developers/extensions/index.js +10 -0
- package/addon/routes/developers/extensions/new.js +3 -0
- package/addon/routes/developers/extensions.js +3 -0
- package/addon/routes/developers/payments/index.js +26 -0
- package/addon/routes/developers/payments/onboard.js +21 -0
- package/addon/routes/developers/payments.js +3 -0
- package/addon/routes/developers.js +3 -0
- package/addon/routes/explore/category.js +27 -0
- package/addon/routes/explore/index.js +17 -0
- package/addon/routes/explore.js +3 -0
- package/addon/routes/installed.js +10 -0
- package/addon/routes/purchased.js +10 -0
- package/addon/routes.js +28 -0
- package/addon/serializers/registry-extension-bundle.js +15 -0
- package/addon/serializers/registry-extension.js +21 -0
- package/addon/services/stripe.js +83 -0
- package/addon/styles/registry-bridge-engine.css +142 -0
- package/addon/templates/application.hbs +26 -0
- package/addon/templates/developers/analytics.hbs +83 -0
- package/addon/templates/developers/credentials.hbs +1 -0
- package/addon/templates/developers/extensions/edit/bundles.hbs +71 -0
- package/addon/templates/developers/extensions/edit/details.hbs +16 -0
- package/addon/templates/developers/extensions/edit/index.hbs +1 -0
- package/addon/templates/developers/extensions/edit/monetize.hbs +3 -0
- package/addon/templates/developers/extensions/edit.hbs +48 -0
- package/addon/templates/developers/extensions/index.hbs +27 -0
- package/addon/templates/developers/extensions/new.hbs +39 -0
- package/addon/templates/developers/extensions.hbs +1 -0
- package/addon/templates/developers/payments/index.hbs +33 -0
- package/addon/templates/developers/payments/onboard.hbs +48 -0
- package/addon/templates/developers/payments.hbs +1 -0
- package/addon/templates/developers.hbs +1 -0
- package/addon/templates/explore/category.hbs +12 -0
- package/addon/templates/explore/index.hbs +12 -0
- package/addon/templates/explore.hbs +1 -0
- package/addon/templates/installed.hbs +32 -0
- package/addon/templates/purchased.hbs +34 -0
- package/app/adapters/registry-bridge.js +1 -0
- package/app/adapters/registry-extension-bundle.js +1 -0
- package/app/adapters/registry-extension.js +1 -0
- package/app/components/extension-card.js +1 -0
- package/app/components/extension-form.js +1 -0
- package/app/components/extension-modal-title.js +1 -0
- package/app/components/extension-monetize-form.js +1 -0
- package/app/components/extension-pending-publish-viewer.js +1 -0
- package/app/components/extension-reviewer-control.js +1 -0
- package/app/components/modals/confirm-extension-purchase.js +1 -0
- package/app/components/modals/extension-details.js +1 -0
- package/app/components/modals/extension-purchase-form.js +1 -0
- package/app/components/modals/extension-uninstall.js +1 -0
- package/app/components/modals/select-extension-bundle.js +1 -0
- package/app/components/progress-bar.js +1 -0
- package/app/controllers/application.js +1 -0
- package/app/controllers/developers/analytics.js +1 -0
- package/app/controllers/developers/extensions/edit/bundles.js +1 -0
- package/app/controllers/developers/extensions/edit/index.js +1 -0
- package/app/controllers/developers/extensions/edit/monetize.js +1 -0
- package/app/controllers/developers/extensions/edit.js +1 -0
- package/app/controllers/developers/extensions/index.js +1 -0
- package/app/controllers/developers/extensions/new.js +1 -0
- package/app/controllers/developers/payments/index.js +1 -0
- package/app/controllers/developers/payments/onboard.js +1 -0
- package/app/controllers/explore/category.js +1 -0
- package/app/controllers/explore/index.js +1 -0
- package/app/controllers/installed.js +1 -0
- package/app/controllers/purchased.js +1 -0
- package/app/models/registry-extension-bundle.js +1 -0
- package/app/models/registry-extension.js +1 -0
- package/app/routes/developers/analytics.js +1 -0
- package/app/routes/developers/credentials.js +1 -0
- package/app/routes/developers/extensions/edit/bundles.js +1 -0
- package/app/routes/developers/extensions/edit/details.js +1 -0
- package/app/routes/developers/extensions/edit/index.js +1 -0
- package/app/routes/developers/extensions/edit/monetize.js +1 -0
- package/app/routes/developers/extensions/edit.js +1 -0
- package/app/routes/developers/extensions/index.js +1 -0
- package/app/routes/developers/extensions/new.js +1 -0
- package/app/routes/developers/extensions.js +1 -0
- package/app/routes/developers/payments/index.js +1 -0
- package/app/routes/developers/payments/onboard.js +1 -0
- package/app/routes/developers/payments.js +1 -0
- package/app/routes/developers.js +1 -0
- package/app/routes/explore/category.js +1 -0
- package/app/routes/explore/index.js +1 -0
- package/app/routes/explore.js +1 -0
- package/app/routes/installed.js +1 -0
- package/app/routes/purchased.js +1 -0
- package/app/serializers/registry-extension-bundle.js +1 -0
- package/app/serializers/registry-extension.js +1 -0
- package/app/services/stripe.js +1 -0
- package/app/templates/developers/analytics.js +1 -0
- package/app/templates/developers/credentials.js +1 -0
- package/app/templates/developers/extensions/edit/bundles.js +1 -0
- package/app/templates/developers/extensions/edit/details.js +1 -0
- package/app/templates/developers/extensions/edit/index.js +1 -0
- package/app/templates/developers/extensions/edit/monetize.js +1 -0
- package/app/templates/developers/extensions/edit.js +1 -0
- package/app/templates/developers/extensions/index.js +1 -0
- package/app/templates/developers/extensions/new.js +1 -0
- package/app/templates/developers/extensions.js +1 -0
- package/app/templates/developers/payments/index.js +1 -0
- package/app/templates/developers/payments/onboard.js +1 -0
- package/app/templates/developers/payments.js +1 -0
- package/app/templates/developers.js +1 -0
- package/app/templates/explore/category.js +1 -0
- package/app/templates/explore/index.js +1 -0
- package/app/templates/explore.js +1 -0
- package/app/templates/installed.js +1 -0
- package/app/templates/purchased.js +1 -0
- package/composer.json +95 -0
- package/config/environment.js +28 -0
- package/extension.json +10 -0
- package/index.js +26 -0
- package/package.json +129 -0
- package/phpstan.neon.dist +8 -0
- package/phpunit.xml.dist +16 -0
- package/server/.gitattributes +14 -0
- package/server/config/registry-bridge.php +32 -0
- package/server/migrations/2024_03_19_060627_create_registry_users_table.php +42 -0
- package/server/migrations/2024_03_21_051614_create_registry_extensions_table.php +76 -0
- package/server/migrations/2024_03_25_044537_create_registry_extension_bundles_table.php +54 -0
- package/server/migrations/2024_03_29_072101_registry_extension_installs.php +35 -0
- package/server/migrations/2024_07_16_155000_create_registry_extension_purchases.php +41 -0
- package/server/seeders/ExtensionsCategorySeeder.php +359 -0
- package/server/src/Console/Commands/Initialize.php +35 -0
- package/server/src/Console/Commands/PostInstallExtension.php +84 -0
- package/server/src/Exceptions/InstallFailedException.php +21 -0
- package/server/src/Expansions/CategoryExpansion.php +30 -0
- package/server/src/Http/Controllers/Internal/v1/ExtensionInstallerController.php +153 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryAuthController.php +230 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryController.php +54 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryExtensionBundleController.php +112 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryExtensionController.php +257 -0
- package/server/src/Http/Controllers/Internal/v1/RegistryPaymentsController.php +227 -0
- package/server/src/Http/Controllers/RegistryBridgeController.php +13 -0
- package/server/src/Http/Filter/RegistryExtensionFilter.php +80 -0
- package/server/src/Http/Requests/AddRegistryUserRequest.php +47 -0
- package/server/src/Http/Requests/AuthenticateRegistryUserRequest.php +47 -0
- package/server/src/Http/Requests/CreateRegistryExtensionBundleRequest.php +42 -0
- package/server/src/Http/Requests/CreateRegistryExtensionRequest.php +31 -0
- package/server/src/Http/Requests/InstallExtensionRequest.php +30 -0
- package/server/src/Http/Requests/RegistryAuthRequest.php +46 -0
- package/server/src/Http/Requests/RegistryExtensionActionRequest.php +30 -0
- package/server/src/Http/Resources/RegistryUser.php +40 -0
- package/server/src/Models/RegistryExtension.php +656 -0
- package/server/src/Models/RegistryExtensionBundle.php +1015 -0
- package/server/src/Models/RegistryExtensionInstall.php +76 -0
- package/server/src/Models/RegistryExtensionPurchase.php +87 -0
- package/server/src/Models/RegistryUser.php +140 -0
- package/server/src/Providers/RegistryBridgeServiceProvider.php +117 -0
- package/server/src/Support/Bridge.php +53 -0
- package/server/src/Support/Utils.php +19 -0
- package/server/src/routes.php +58 -0
- package/server/tests/Feature.php +5 -0
- package/translations/en-us.yaml +119 -0
- package/tsconfig.declarations.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="https://user-images.githubusercontent.com/58805033/191936702-fed04b0f-7966-4041-96d0-95e27bf98248.png" width="280" height="80" style="height: 80px;" />
|
|
4
|
+
</p>
|
|
5
|
+
<p align="center">
|
|
6
|
+
Internal Bridge between Fleetbase API and Extensions Registry
|
|
7
|
+
</p>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
This monorepo contains both the frontend and backend components of the Internal Registry Bridge for Fleetbase. The frontend is built using Ember.js and the backend is implemented in PHP.
|
|
15
|
+
|
|
16
|
+
* PHP 7.3.0 or above
|
|
17
|
+
* Ember.js v4.8 or above
|
|
18
|
+
* Ember CLI v4.8 or above
|
|
19
|
+
* Node.js v18 or above
|
|
20
|
+
|
|
21
|
+
## Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
├── addon
|
|
25
|
+
├── app
|
|
26
|
+
├── assets
|
|
27
|
+
├── translations
|
|
28
|
+
├── config
|
|
29
|
+
├── node_modules
|
|
30
|
+
├── server
|
|
31
|
+
│ ├── config
|
|
32
|
+
│ ├── data
|
|
33
|
+
│ ├── migrations
|
|
34
|
+
│ ├── resources
|
|
35
|
+
│ ├── src
|
|
36
|
+
│ ├── tests
|
|
37
|
+
│ └── vendor
|
|
38
|
+
├── tests
|
|
39
|
+
├── testem.js
|
|
40
|
+
├── index.js
|
|
41
|
+
├── package.json
|
|
42
|
+
├── phpstan.neon.dist
|
|
43
|
+
├── phpunit.xml.dist
|
|
44
|
+
├── pnpm-lock.yaml
|
|
45
|
+
├── ember-cli-build.js
|
|
46
|
+
├── composer.json
|
|
47
|
+
├── CONTRIBUTING.md
|
|
48
|
+
├── LICENSE.md
|
|
49
|
+
├── README.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
### Backend
|
|
55
|
+
|
|
56
|
+
Install the PHP packages using Composer:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
composer require fleetbase/core-api
|
|
60
|
+
composer require fleetbase/registry-bridge
|
|
61
|
+
```
|
|
62
|
+
### Frontend
|
|
63
|
+
|
|
64
|
+
Install the Ember.js Engine/Addon:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pnpm install @fleetbase/registry-bridge-engine
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
### Backend
|
|
73
|
+
|
|
74
|
+
🧹 Keep a modern codebase with **PHP CS Fixer**:
|
|
75
|
+
```bash
|
|
76
|
+
composer lint
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
⚗️ Run static analysis using **PHPStan**:
|
|
80
|
+
```bash
|
|
81
|
+
composer test:types
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
✅ Run unit tests using **PEST**
|
|
85
|
+
```bash
|
|
86
|
+
composer test:unit
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
🚀 Run the entire test suite:
|
|
90
|
+
```bash
|
|
91
|
+
composer test
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Frontend
|
|
95
|
+
|
|
96
|
+
🧹 Keep a modern codebase with **ESLint**:
|
|
97
|
+
```bash
|
|
98
|
+
pnpm lint
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
✅ Run unit tests using **Ember/QUnit**
|
|
102
|
+
```bash
|
|
103
|
+
pnpm test
|
|
104
|
+
pnpm test:ember
|
|
105
|
+
pnpm test:ember-compatibility
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
🚀 Start the Ember Addon/Engine
|
|
109
|
+
```bash
|
|
110
|
+
pnpm start
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
🔨 Build the Ember Addon/Engine
|
|
114
|
+
```bash
|
|
115
|
+
pnpm build
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Contributing
|
|
119
|
+
See the Contributing Guide for details on how to contribute to this project.
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './registry-bridge';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './registry-bridge';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<button type="button" class="extension-card-container hover:opacity-50 {{@buttonClass}}" ...attributes {{on "click" this.onClick}} {{did-update this.onExtensionUpdated @extension}}>
|
|
2
|
+
<div class="extension-card-icon-container {{@iconWrapperClass}}">
|
|
3
|
+
<Image src={{@extension.icon_url}} class={{@iconClass}} alt={{@extension.name}} @fallbackSrc={{config "defaultValues.extensionIcon"}} />
|
|
4
|
+
</div>
|
|
5
|
+
<div class="extension-card-body-container {{@detailsWrapperClass}}">
|
|
6
|
+
<div class="flex flex-col">
|
|
7
|
+
<div class="font-semibold text-sm block {{@nameTextClass}}">{{@extension.name}}</div>
|
|
8
|
+
<div class="text-xs {{@descriptionTextClass}}">{{n-a @extension.description}}</div>
|
|
9
|
+
</div>
|
|
10
|
+
{{yield @extension}}
|
|
11
|
+
</div>
|
|
12
|
+
</button>
|
|
@@ -0,0 +1,235 @@
|
|
|
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 { later } from '@ember/runloop';
|
|
6
|
+
import formatCurrency from '@fleetbase/ember-ui/utils/format-currency';
|
|
7
|
+
import isModel from '@fleetbase/ember-core/utils/is-model';
|
|
8
|
+
|
|
9
|
+
function removeParamFromCurrentUrl(paramToRemove) {
|
|
10
|
+
const url = new URL(window.location.href);
|
|
11
|
+
url.searchParams.delete(paramToRemove);
|
|
12
|
+
window.history.pushState({ path: url.href }, '', url.href);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function addParamToCurrentUrl(paramName, paramValue) {
|
|
16
|
+
const url = new URL(window.location.href);
|
|
17
|
+
url.searchParams.set(paramName, paramValue);
|
|
18
|
+
window.history.pushState({ path: url.href }, '', url.href);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default class ExtensionCardComponent extends Component {
|
|
22
|
+
@service modalsManager;
|
|
23
|
+
@service notifications;
|
|
24
|
+
@service currentUser;
|
|
25
|
+
@service socket;
|
|
26
|
+
@service fetch;
|
|
27
|
+
@service stripe;
|
|
28
|
+
@service urlSearchParams;
|
|
29
|
+
@tracked extension;
|
|
30
|
+
|
|
31
|
+
constructor(owner, { extension }) {
|
|
32
|
+
super(...arguments);
|
|
33
|
+
this.extension = extension;
|
|
34
|
+
this.checkForCheckoutSession();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@action onExtenstionUpdated(el, [extension]) {
|
|
38
|
+
this.extension = extension;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@action onClick(options = {}) {
|
|
42
|
+
const installChannel = `install.${this.currentUser.companyId}.${this.extension.id}`;
|
|
43
|
+
const isAlreadyPurchased = this.extension.is_purchased === true;
|
|
44
|
+
const isAlreadyInstalled = this.extension.is_installed === true;
|
|
45
|
+
const isPaymentRequired = this.extension.payment_required === true && isAlreadyPurchased === false;
|
|
46
|
+
|
|
47
|
+
if (typeof this.args.onClick === 'function') {
|
|
48
|
+
this.args.onClick(this.extension);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addParamToCurrentUrl('extension_id', this.extension.id);
|
|
52
|
+
this.modalsManager.show('modals/extension-details', {
|
|
53
|
+
titleComponent: 'extension-modal-title',
|
|
54
|
+
modalClass: 'flb--extension-modal modal-lg',
|
|
55
|
+
modalHeaderClass: 'flb--extension-modal-header',
|
|
56
|
+
acceptButtonText: isPaymentRequired ? `Purchase for ${formatCurrency(this.extension.price, this.extension.currency)}` : isAlreadyInstalled ? 'Installed' : 'Install',
|
|
57
|
+
acceptButtonIcon: isPaymentRequired ? 'credit-card' : isAlreadyInstalled ? 'check' : 'download',
|
|
58
|
+
acceptButtonDisabled: isAlreadyInstalled,
|
|
59
|
+
acceptButtonScheme: isPaymentRequired ? 'success' : 'primary',
|
|
60
|
+
declineButtonText: 'Done',
|
|
61
|
+
process: null,
|
|
62
|
+
step: null,
|
|
63
|
+
stepDescription: 'Awaiting install to begin...',
|
|
64
|
+
progress: 0,
|
|
65
|
+
extension: this.extension,
|
|
66
|
+
confirm: async (modal) => {
|
|
67
|
+
modal.startLoading();
|
|
68
|
+
|
|
69
|
+
// Handle purchase flow
|
|
70
|
+
if (isPaymentRequired) {
|
|
71
|
+
return this.startCheckoutSession();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Listen for install progress
|
|
75
|
+
this.socket.listen(installChannel, ({ process, step, progress }) => {
|
|
76
|
+
let stepDescription;
|
|
77
|
+
switch (step) {
|
|
78
|
+
case 'server.install':
|
|
79
|
+
stepDescription = '(1/3) Installing extension...';
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
case 'engine.install':
|
|
83
|
+
stepDescription = '(2/3) Installing extension...';
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'console.build':
|
|
87
|
+
stepDescription = '(3/3) Completing install...';
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
default:
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
modal.setOptions({ process, step, progress, stepDescription });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Start install progress
|
|
97
|
+
modal.setOption('progress', 5);
|
|
98
|
+
|
|
99
|
+
// Run install
|
|
100
|
+
try {
|
|
101
|
+
await this.extension.install();
|
|
102
|
+
this.notifications.info(`${this.extension.name} is now Installed.`);
|
|
103
|
+
later(
|
|
104
|
+
this,
|
|
105
|
+
() => {
|
|
106
|
+
window.location.reload(true);
|
|
107
|
+
},
|
|
108
|
+
600
|
|
109
|
+
);
|
|
110
|
+
removeParamFromCurrentUrl('extension_id');
|
|
111
|
+
modal.done();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.notifications.serverError(error);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
decline: (modal) => {
|
|
117
|
+
modal.done();
|
|
118
|
+
removeParamFromCurrentUrl('extension_id');
|
|
119
|
+
},
|
|
120
|
+
...options,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async startCheckoutSession() {
|
|
125
|
+
const checkout = await this.stripe.initEmbeddedCheckout({
|
|
126
|
+
fetchClientSecret: this.fetchClientSecret.bind(this),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await this.modalsManager.done();
|
|
130
|
+
later(
|
|
131
|
+
this,
|
|
132
|
+
() => {
|
|
133
|
+
this.modalsManager.show('modals/extension-purchase-form', {
|
|
134
|
+
title: `Purchase the '${this.extension.name}' Extension`,
|
|
135
|
+
modalClass: 'stripe-extension-purchase',
|
|
136
|
+
modalFooterClass: 'hidden-i',
|
|
137
|
+
extension: this.extension,
|
|
138
|
+
checkoutElementInserted: (el) => {
|
|
139
|
+
checkout.mount(el);
|
|
140
|
+
},
|
|
141
|
+
decline: async (modal) => {
|
|
142
|
+
checkout.destroy();
|
|
143
|
+
await modal.done();
|
|
144
|
+
later(
|
|
145
|
+
this,
|
|
146
|
+
() => {
|
|
147
|
+
this.onClick();
|
|
148
|
+
},
|
|
149
|
+
100
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
100
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async fetchClientSecret() {
|
|
159
|
+
try {
|
|
160
|
+
const { clientSecret } = await this.fetch.post(
|
|
161
|
+
'payments/create-checkout-session',
|
|
162
|
+
{ extension: this.extension.id, uri: window.location.pathname },
|
|
163
|
+
{ namespace: '~registry/v1' }
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return clientSecret;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
this.notifications.serverError(error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async checkForCheckoutSession() {
|
|
173
|
+
later(
|
|
174
|
+
this,
|
|
175
|
+
async () => {
|
|
176
|
+
const checkoutSessionId = this.urlSearchParams.get('checkout_session_id');
|
|
177
|
+
const extensionId = this.urlSearchParams.get('extension_id');
|
|
178
|
+
|
|
179
|
+
if (!checkoutSessionId && this.extension.id === extensionId) {
|
|
180
|
+
return this.onClick();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (checkoutSessionId && this.extension.id === extensionId) {
|
|
184
|
+
this.modalsManager.show('modals/confirm-extension-purchase', {
|
|
185
|
+
title: 'Finalizing Purchase',
|
|
186
|
+
modalClass: 'finalize-extension-purchase',
|
|
187
|
+
loadingMessage: 'Completing purchase do not refresh or exit window...',
|
|
188
|
+
modalFooterClass: 'hidden-i',
|
|
189
|
+
backdropClose: false,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const { status, extension } = await this.fetch.post(
|
|
194
|
+
'payments/get-checkout-session',
|
|
195
|
+
{ checkout_session_id: checkoutSessionId, extension: this.extension.id },
|
|
196
|
+
{ namespace: '~registry/v1' }
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Update this extension
|
|
200
|
+
const extensionModel = this.fetch.jsonToModel(extension, 'registry-extension');
|
|
201
|
+
if (isModel(extensionModel)) {
|
|
202
|
+
this.extension = extensionModel;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Fire a callback
|
|
206
|
+
if (typeof this.args.onCheckoutCompleted === 'function') {
|
|
207
|
+
this.args.onCheckoutCompleted(this.extension, status);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (status === 'complete' || status === 'purchase_complete') {
|
|
211
|
+
// remove checkout session id
|
|
212
|
+
removeParamFromCurrentUrl('checkout_session_id');
|
|
213
|
+
|
|
214
|
+
// close confirmation dialog and notify payment completed
|
|
215
|
+
await this.modalsManager.done();
|
|
216
|
+
if (status === 'complete') {
|
|
217
|
+
this.notifications.success('Payment Completed.');
|
|
218
|
+
}
|
|
219
|
+
later(
|
|
220
|
+
this,
|
|
221
|
+
() => {
|
|
222
|
+
this.onClick();
|
|
223
|
+
},
|
|
224
|
+
100
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
this.notifications.serverError(error);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
300
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<div class="flex flex-row items-start">
|
|
2
|
+
<div class="w-32 flex flex-col justify-center">
|
|
3
|
+
<div class="mb-2">
|
|
4
|
+
<Image src={{@extension.icon_url}} @fallbackSrc={{config "defaultValues.extensionIcon"}} alt={{@extension.name}} class="w-32 h-32 rounded-lg border border-black shadow-sm" />
|
|
5
|
+
</div>
|
|
6
|
+
<FileUpload
|
|
7
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.upload-extension-icon"}}
|
|
8
|
+
@accept={{join "," this.acceptedImageTypes}}
|
|
9
|
+
@onFileAdded={{perform this.uploadIcon}}
|
|
10
|
+
@labelClass="flex flex-row items-center justify-center"
|
|
11
|
+
as |queue|
|
|
12
|
+
>
|
|
13
|
+
<a tabindex={{0}} class="flex items-center px-0 mt-2 text-xs no-underline truncate btn btn-sm btn-default" disabled={{not queue.files.length}}>
|
|
14
|
+
{{#if queue.files.length}}
|
|
15
|
+
<div class="mr-1.5">
|
|
16
|
+
<Spinner />
|
|
17
|
+
</div>
|
|
18
|
+
<span>
|
|
19
|
+
{{t "common.uploading"}}
|
|
20
|
+
</span>
|
|
21
|
+
{{else}}
|
|
22
|
+
<FaIcon @icon="icons" class="mr-1.5" />
|
|
23
|
+
<span>
|
|
24
|
+
{{t "registry-bridge.developers.extensions.extension-form.upload-extension-icon"}}
|
|
25
|
+
</span>
|
|
26
|
+
{{/if}}
|
|
27
|
+
</a>
|
|
28
|
+
</FileUpload>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="flex-1 px-6 space-y-6">
|
|
31
|
+
<ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.details-content-block"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
|
32
|
+
<InputGroup
|
|
33
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-name"}}
|
|
34
|
+
@value={{@extension.name}}
|
|
35
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-name-help-text"}}
|
|
36
|
+
/>
|
|
37
|
+
<InputGroup
|
|
38
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-description"}}
|
|
39
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-description-help-text"}}
|
|
40
|
+
>
|
|
41
|
+
<Textarea
|
|
42
|
+
@value={{@extension.description}}
|
|
43
|
+
placeholder={{t "registry-bridge.developers.extensions.extension-form.extension-description"}}
|
|
44
|
+
class="form-input w-full"
|
|
45
|
+
rows="3"
|
|
46
|
+
/>
|
|
47
|
+
</InputGroup>
|
|
48
|
+
<InputGroup
|
|
49
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-category"}}
|
|
50
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-category-help-text"}}
|
|
51
|
+
>
|
|
52
|
+
<ModelSelect
|
|
53
|
+
@modelName="category"
|
|
54
|
+
@query={{hash for="extension_category" core_category=1}}
|
|
55
|
+
@selectedModel={{@extension.category}}
|
|
56
|
+
@placeholder={{t "registry-bridge.developers.extensions.extension-form.extension-select-category"}}
|
|
57
|
+
@triggerClass="form-select form-input"
|
|
58
|
+
@infiniteScroll={{false}}
|
|
59
|
+
@renderInPlace={{true}}
|
|
60
|
+
@onChange={{fn (mut @extension.category)}}
|
|
61
|
+
as |category|
|
|
62
|
+
>
|
|
63
|
+
<div class="flex items-center">
|
|
64
|
+
<FaIcon @icon={{category.icon}} @size="sm" class="mr-2" />
|
|
65
|
+
<span>{{category.name}}</span>
|
|
66
|
+
</div>
|
|
67
|
+
</ModelSelect>
|
|
68
|
+
</InputGroup>
|
|
69
|
+
|
|
70
|
+
<InputGroup
|
|
71
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-tags"}}
|
|
72
|
+
@wrapperClass="mb-0i"
|
|
73
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-tags-help-text"}}
|
|
74
|
+
>
|
|
75
|
+
<TagInput
|
|
76
|
+
class="form-input"
|
|
77
|
+
@placeholder={{t "registry-bridge.developers.extensions.extension-form.extension-add-tags"}}
|
|
78
|
+
@allowSpacesInTags={{true}}
|
|
79
|
+
@tags={{@extension.tags}}
|
|
80
|
+
@addTag={{@extension.addTag}}
|
|
81
|
+
@removeTagAtIndex={{@extension.removeTag}}
|
|
82
|
+
@disabled={{this.core_extension}}
|
|
83
|
+
as |tag|
|
|
84
|
+
>
|
|
85
|
+
{{tag}}
|
|
86
|
+
</TagInput>
|
|
87
|
+
</InputGroup>
|
|
88
|
+
</ContentPanel>
|
|
89
|
+
<ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.extension-bundle"}} @open={{true}} @pad={{false}} @panelBodyClass="bg-white dark:bg-gray-800">
|
|
90
|
+
<div class="px-4 pb-4 pt-3 flex flex-col flex-grow-0">
|
|
91
|
+
<Button @type="magic" @icon="box-archive" @text="Select Bundle" @onClick={{this.selectBundle}} />
|
|
92
|
+
{{#if @extension.next_bundle_filename}}
|
|
93
|
+
<div class="mt-4 bg-blue-200 border border-blue-400 text-blue-900 rounded-lg shadow-sm px-4 py-2 flex flex-row items-center text-xs">
|
|
94
|
+
<FaIcon @icon="box-archive" @size="xs" />
|
|
95
|
+
<div class="ml-2 flex flex-row items-center space-x-2">
|
|
96
|
+
<span>{{@extension.next_bundle_filename}}</span>
|
|
97
|
+
<span>-</span>
|
|
98
|
+
<span>{{@extension.next_bundle_id}}</span>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
{{/if}}
|
|
102
|
+
</div>
|
|
103
|
+
</ContentPanel>
|
|
104
|
+
<ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.extension-listing-details"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
|
105
|
+
<InputGroup
|
|
106
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-promotional-text"}}
|
|
107
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-promotional-text-help-text"}}
|
|
108
|
+
>
|
|
109
|
+
<Textarea
|
|
110
|
+
@value={{@extension.promotional_text}}
|
|
111
|
+
placeholder={{t "registry-bridge.developers.extensions.extension-form.extension-promotional-text"}}
|
|
112
|
+
class="form-input w-full"
|
|
113
|
+
rows="3"
|
|
114
|
+
/>
|
|
115
|
+
</InputGroup>
|
|
116
|
+
<InputGroup
|
|
117
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-subtitle"}}
|
|
118
|
+
@value={{@extension.subtitle}}
|
|
119
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-subtitle-help-text"}}
|
|
120
|
+
/>
|
|
121
|
+
<InputGroup
|
|
122
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-copyright"}}
|
|
123
|
+
@value={{@extension.copyright}}
|
|
124
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-copyright-help-text"}}
|
|
125
|
+
/>
|
|
126
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
127
|
+
<InputGroup
|
|
128
|
+
@type="url"
|
|
129
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-website-url"}}
|
|
130
|
+
@value={{@extension.website_url}}
|
|
131
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-website-url-help-text"}}
|
|
132
|
+
@wrapperClass="mb-0i"
|
|
133
|
+
/>
|
|
134
|
+
<InputGroup
|
|
135
|
+
@type="url"
|
|
136
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-repo-url"}}
|
|
137
|
+
@value={{@extension.repo_url}}
|
|
138
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-repo-url-help-text"}}
|
|
139
|
+
@wrapperClass="mb-0i"
|
|
140
|
+
/>
|
|
141
|
+
<InputGroup
|
|
142
|
+
@type="url"
|
|
143
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-support-url"}}
|
|
144
|
+
@value={{@extension.support_url}}
|
|
145
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-support-url-help-text"}}
|
|
146
|
+
@wrapperClass="mb-0i"
|
|
147
|
+
/>
|
|
148
|
+
<InputGroup
|
|
149
|
+
@type="url"
|
|
150
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-privacy-policy-url"}}
|
|
151
|
+
@value={{@extension.privacy_policy_url}}
|
|
152
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-privacy-policy-url-help-text"}}
|
|
153
|
+
@wrapperClass="mb-0i"
|
|
154
|
+
/>
|
|
155
|
+
<InputGroup
|
|
156
|
+
@type="url"
|
|
157
|
+
@name={{t "registry-bridge.developers.extensions.extension-form.extension-tos-url"}}
|
|
158
|
+
@value={{@extension.tos_url}}
|
|
159
|
+
@helpText={{t "registry-bridge.developers.extensions.extension-form.extension-tos-url-help-text"}}
|
|
160
|
+
@wrapperClass="mb-0i"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
</ContentPanel>
|
|
164
|
+
<ContentPanel @title={{t "registry-bridge.developers.extensions.extension-form.extension-screenshots"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
|
165
|
+
<div class="space-y-4">
|
|
166
|
+
{{#if this.isUploading}}
|
|
167
|
+
<div
|
|
168
|
+
class="dropzone w-full rounded-lg px-4 py-8 bg-gray-50 dark:bg-gray-900 bg-opacity-25 text-gray-900 dark:text-white text-center flex flex-col items-center justify-center border-2 border-dashed border-gray-200 dark:border-indigo-500"
|
|
169
|
+
>
|
|
170
|
+
<div class="flex items-center justify-center py-5">
|
|
171
|
+
<Spinner class="text-sm dar:text-gray-100" @loadingMessage={{t "fleet-ops.common.uploading"}} />
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
{{else}}
|
|
175
|
+
{{#let (file-queue name="files" onFileAdded=this.queueFile accept=(join "," this.acceptedImageTypes)) as |queue|}}
|
|
176
|
+
<FileDropzone @queue={{queue}} class="dropzone file-dropzone" as |dropzone|>
|
|
177
|
+
{{#if dropzone.active}}
|
|
178
|
+
{{#if dropzone.valid}}
|
|
179
|
+
{{t "component.dropzone.drop-to-upload"}}
|
|
180
|
+
{{else}}
|
|
181
|
+
{{t "component.dropzone.invalid"}}
|
|
182
|
+
{{/if}}
|
|
183
|
+
{{else if queue.files.length}}
|
|
184
|
+
<div class="my-2">
|
|
185
|
+
<FaIcon @icon="folder-open" class="text-indigo-500 mr-2" />
|
|
186
|
+
{{t "component.dropzone.files-ready-for-upload" numOfFiles=(pluralize queue.files.length (t "component.dropzone.file"))}}
|
|
187
|
+
</div>
|
|
188
|
+
<div class="my-2">({{queue.progress}}%)</div>
|
|
189
|
+
{{else}}
|
|
190
|
+
<h4 class="font-semibold">
|
|
191
|
+
<FaIcon @icon="folder-open" @size="lg" class="text-indigo-500 mr-2" />
|
|
192
|
+
{{t "registry-bridge.developers.extensions.extension-form.upload-screenshots"}}
|
|
193
|
+
</h4>
|
|
194
|
+
<div>
|
|
195
|
+
{{#if dropzone.supported}}
|
|
196
|
+
<p class="text-sm my-5">{{t "component.dropzone.dropzone-supported-files"}}</p>
|
|
197
|
+
{{/if}}
|
|
198
|
+
<FileUpload @name="files" @for="files" @accept={{join "," this.acceptedFileTypes}} @multiple={{true}} @onFileAdded={{this.queueFile}}>
|
|
199
|
+
<a tabindex={{0}} class="btn btn-magic cursor-pointer ml-1">{{t "component.dropzone.or-select-button-text"}}</a>
|
|
200
|
+
</FileUpload>
|
|
201
|
+
</div>
|
|
202
|
+
{{/if}}
|
|
203
|
+
</FileDropzone>
|
|
204
|
+
{{/let}}
|
|
205
|
+
{{#if this.uploadQueue}}
|
|
206
|
+
<div class="mx-4">
|
|
207
|
+
<div class="flex items-center justify-between mb-4">
|
|
208
|
+
<span class="leading-6 dark:text-gray-100">{{t "component.dropzone.upload-queue"}}</span>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="space-y-2 mb-4">
|
|
211
|
+
{{#each this.uploadQueue as |file|}}
|
|
212
|
+
<div class="flex items-center justify-between bg-blue-100 border border-blue-800 dark:border-blue-800 py-1.5 shadow-sm rounded-lg px-4">
|
|
213
|
+
<div class="text-xs text-blue-900 truncate">{{truncate-filename file.name 50}}</div>
|
|
214
|
+
<div class="flex items-center text-sm">
|
|
215
|
+
<Spinner class="text-blue-900 mr-2" />
|
|
216
|
+
<span class="font-bold text-blue-900">{{round file.progress}}%</span>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
{{/each}}
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
{{/if}}
|
|
223
|
+
<div>
|
|
224
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-4">
|
|
225
|
+
{{#each @extension.screenshots as |file|}}
|
|
226
|
+
<File @file={{file}} @onDelete={{this.removeFile}} />
|
|
227
|
+
{{/each}}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
{{/if}}
|
|
231
|
+
</div>
|
|
232
|
+
</ContentPanel>
|
|
233
|
+
{{#if @withMonetizeForm}}
|
|
234
|
+
<ExtensionMonetizeForm @extension={{@extension}} />
|
|
235
|
+
{{/if}}
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|