mirror of
https://github.com/fleetbase/fleetbase.git
synced 2026-01-07 15:01:45 +00:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
116873a1ce | ||
|
|
20a1793447 | ||
|
|
72ab83dc7a | ||
|
|
171e365ba6 | ||
|
|
009f2d6e53 | ||
|
|
4a59318feb | ||
|
|
8920039b40 | ||
|
|
cd9be05714 | ||
|
|
fbe35545e9 | ||
|
|
56ab967d7a | ||
|
|
79604c7981 | ||
|
|
312eb1aa6f | ||
|
|
6572a59120 | ||
|
|
fa536c6183 | ||
|
|
d4626be332 | ||
|
|
ffc54ecdbb | ||
|
|
9bf097b88b | ||
|
|
52c8df8b66 | ||
|
|
908e0eb9ee | ||
|
|
6438138913 | ||
|
|
aee06a2146 | ||
|
|
ccacc6c597 | ||
|
|
62c396b789 | ||
|
|
295da5f331 | ||
|
|
e775ccc2c8 | ||
|
|
bd0759881f | ||
|
|
830ae69b1d | ||
|
|
3b9a80866f | ||
|
|
76badbf949 | ||
|
|
f968556585 | ||
|
|
6d01eab305 | ||
|
|
9406446db1 | ||
|
|
565db7bbce | ||
|
|
003cb467e8 | ||
|
|
8e1b281e77 | ||
|
|
af4507cc87 | ||
|
|
2a2c7d8426 | ||
|
|
077a4298b9 | ||
|
|
af840d30d0 | ||
|
|
a253c0c6f7 | ||
|
|
0fc1d6068d | ||
|
|
6993510d08 | ||
|
|
4fcf09c1b8 | ||
|
|
f89596a74b | ||
|
|
d89e93f248 | ||
|
|
2f0c15bc93 | ||
|
|
ae6c07006b | ||
|
|
d754641493 | ||
|
|
bb0d706006 | ||
|
|
652bc363e1 | ||
|
|
363309af61 | ||
|
|
83791ea91c |
2
.github/workflows/cd.yml
vendored
2
.github/workflows/cd.yml
vendored
@@ -147,7 +147,7 @@ jobs:
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
pnpm build
|
||||
pnpm build --environment production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Deploy Console 🚀
|
||||
|
||||
2
.github/workflows/gcp-cd.yml
vendored
2
.github/workflows/gcp-cd.yml
vendored
@@ -173,7 +173,7 @@ jobs:
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
pnpm build
|
||||
pnpm build --environment production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Deploy Console 🚀
|
||||
|
||||
11
COMMERCIAL_LICENSE.md
Normal file
11
COMMERCIAL_LICENSE.md
Normal file
@@ -0,0 +1,11 @@
|
||||
FLEETBASE DUAL LICENSE
|
||||
|
||||
COPYRIGHT (C) 2024 FLEETBASE PTE LTD.
|
||||
|
||||
PERMISSION IS HEREBY GRANTED, FREE OF CHARGE, TO ANY PERSON OBTAINING A COPY OF THIS SOFTWARE AND ASSOCIATED DOCUMENTATION FILES (THE "SOFTWARE"), TO USE THE SOFTWARE FOR NON-COMMERCIAL PURPOSES ONLY. NON-COMMERCIAL PURPOSES INCLUDE INTERNAL OPERATIONS, ACADEMIC RESEARCH, PERSONAL PROJECTS, OR ANY OTHER USE THAT IS NOT INTENDED FOR COMMERCIAL GAIN.
|
||||
|
||||
FOR VERSIONS 0.4.10 ONWARDS, YOU ARE PERMITTED TO USE THE SOFTWARE FOR NON-COMMERCIAL PURPOSES FREE OF CHARGE. HOWEVER, COMMERCIAL USE OF THIS SOFTWARE, INCLUDING BUT NOT LIMITED TO BUILDING SAAS PLATFORMS, OFFERING SERVICES TO THIRD PARTIES, OR INTEGRATING WITH COMMERCIAL PRODUCTS, REQUIRES THE PURCHASE OF A COMMERCIAL LICENSE FROM FLEETBASE PTE LTD.
|
||||
|
||||
FOR INQUIRIES REGARDING COMMERCIAL LICENSING OR ANY OTHER QUESTIONS RELATED TO FLEETBASE, PLEASE CONTACT HELLO@FLEETBASE.IO.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Fleetbase Pte Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT LICENSE
|
||||
|
||||
COPYRIGHT (C) 2023 FLEETBASE PTE LTD
|
||||
|
||||
PERMISSION IS HEREBY GRANTED, FREE OF CHARGE, TO ANY PERSON OBTAINING A COPY
|
||||
OF THIS SOFTWARE AND ASSOCIATED DOCUMENTATION FILES (THE "SOFTWARE"), TO DEAL
|
||||
IN THE SOFTWARE WITHOUT RESTRICTION, INCLUDING WITHOUT LIMITATION THE RIGHTS
|
||||
TO USE, COPY, MODIFY, MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL
|
||||
COPIES OF THE SOFTWARE, AND TO PERMIT PERSONS TO WHOM THE SOFTWARE IS
|
||||
FURNISHED TO DO SO, SUBJECT TO THE FOLLOWING CONDITIONS:
|
||||
|
||||
THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN ALL
|
||||
COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -8,10 +8,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"fleetbase/core-api": "^1.4.0",
|
||||
"fleetbase/fleetops-api": "^0.4.7",
|
||||
"fleetbase/storefront-api": "^0.2.11",
|
||||
"php": "^8.0",
|
||||
"fleetbase/core-api": "^1.4.11",
|
||||
"fleetbase/fleetops-api": "^0.4.17",
|
||||
"fleetbase/storefront-api": "^0.3.4",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/octane": "^2.3",
|
||||
@@ -20,7 +20,8 @@
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"phpoffice/phpspreadsheet": "^1.28",
|
||||
"predis/predis": "^2.1",
|
||||
"psr/http-factory-implementation": "*"
|
||||
"psr/http-factory-implementation": "*",
|
||||
"s-ichikawa/laravel-sendgrid-driver": "^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"spatie/laravel-ignition": "^2.0",
|
||||
|
||||
2401
api/composer.lock
generated
2401
api/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -21,13 +21,13 @@ return [
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => array_filter(['http://localhost:4200', env('CONSOLE_HOST'), Utils::addWwwToUrl(env('CONSOLE_URL'))]),
|
||||
'allowed_origins' => array_filter(['http://localhost:4200', env('CONSOLE_HOST'), Utils::addWwwToUrl(env('CONSOLE_HOST'))]),
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['*'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
'exposed_headers' => ['x-compressed-json', 'access-console-sandbox', 'access-console-sandbox-key'],
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ WORKDIR /app
|
||||
RUN mkdir -p ~/.pnpm
|
||||
ENV PNPM_HOME /root/.pnpm
|
||||
|
||||
# Set environment
|
||||
ARG ENVIRONMENT=production
|
||||
|
||||
# Add the pnpm global bin to the PATH
|
||||
ENV PATH /root/.pnpm/bin:$PATH
|
||||
|
||||
@@ -33,7 +36,7 @@ RUN pnpm install
|
||||
COPY console .
|
||||
|
||||
# Build the application
|
||||
RUN pnpm build
|
||||
RUN pnpm build --environment $ENVIRONMENT
|
||||
|
||||
# ---- Serve Stage ----
|
||||
FROM nginx:alpine
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Textarea class="form-input w-full" @value={{this.apn.private_key_content}} placeholder="APN Private Key" rows="10" disabled={{this.isLoading}} />
|
||||
</InputGroup> --}}
|
||||
<InputGroup @wrapperClass="flex flex-row items-center">
|
||||
<UploadButton @name="apn-key" @accept="text/plain,application/x-pem-file,application/x-pkcs12,application/x-x509-ca-cert,.p12,.pem,.p8" @onFileAdded={{this.uploadApnKey}} @buttonText="Upload P8 Key File" @uploadIcon="upload" class="w-auto m-0i mt-0i" />
|
||||
<UploadButton @name="apn-key" @accept="text/plain,application/x-pem-file,application/x-pkcs12,application/x-x509-ca-cert,.p12,.pem,.p8" @onFileAdded={{this.uploadApnKey}} @buttonText="Upload P8 Key File" @icon="upload" class="w-auto m-0i mt-0i" />
|
||||
{{#if this.apn.private_key_file}}
|
||||
<div class="ml-2.5 text-sm dark:text-white text-black flex flex-row items-center border border-blue-500 rounded-lg px-2 py-0.5 -mt-1">
|
||||
<FaIcon @icon="file-text" @size="sm" class="mr-2 dark:text-white text-black" />
|
||||
@@ -15,16 +15,32 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
</InputGroup>
|
||||
<InputGroup>
|
||||
<InputGroup @wrapperClass="mb-0i">
|
||||
<Checkbox @label="APN Production" @value={{this.apn.production}} @onToggle={{fn (mut this.apn.production)}} @disabled={{this.isLoading}} />
|
||||
</InputGroup>
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Firebase Configutation" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @wrapperClass="flex flex-row items-center mb-0i">
|
||||
<UploadButton @name="firebase-service-account" @accept="text/plain,text/javascript,application/json" @onFileAdded={{this.uploadFirebaseCredentials}} @buttonText="Upload Service Account JSON" @icon="upload" class="w-auto m-0i mt-0i" />
|
||||
{{#if this.firebase.credentials_file}}
|
||||
<div class="ml-2.5 text-sm dark:text-white text-black flex flex-row items-center border border-blue-500 rounded-lg px-2 py-0.5 -mt-1">
|
||||
<FaIcon @icon="file-text" @size="sm" class="mr-2 dark:text-white text-black" />
|
||||
<span>{{this.firebase.credentials_file.original_filename}}</span>
|
||||
<a href="javascript:;" class="text-red-500 ml-2" {{on "click" this.removeFirebaseCredentialsFile}}><FaIcon @icon="times" class="text-red-500" /></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</InputGroup>
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="Test Push Notification" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-900">
|
||||
{{#if this.testResponse}}
|
||||
<div class="animate-pulse flex flex-row items-center rounded-lg border {{if (eq this.testResponse.status 'error') 'border-red-900 bg-red-800 text-red-100' 'border-green-900 bg-green-800 text-green-100'}} shadow-sm my-2 px-4 py-2">
|
||||
<FaIcon @icon={{if (eq this.testResponse.status 'error') 'triangle-exclamation' 'circle-check'}} class="mr-1.5 {{if (eq this.testResponse.status 'error') 'text-red-200' 'text-green-200'}}" />
|
||||
<span class="text-xs">{{this.this.testResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="mt-3 rounded-lg bg-gray-900 shadow-inner p-3">
|
||||
<div class="">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="text-sm w-40">Title:</div>
|
||||
@@ -47,6 +63,8 @@
|
||||
</div>
|
||||
</ContentPanel>
|
||||
|
||||
<Spacer @height="300px" />
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</EmberWormhole>
|
||||
@@ -18,14 +18,13 @@ export default class ConfigureNotificationChannelsComponent extends Component {
|
||||
team_id: '',
|
||||
app_bundle_id: '',
|
||||
private_key_path: '',
|
||||
_private_key_path: '',
|
||||
private_key_file_id: '',
|
||||
private_key_file: null,
|
||||
production: true,
|
||||
};
|
||||
@tracked fcm = {
|
||||
firebase_credentials_json: '',
|
||||
firebase_database_url: '',
|
||||
firebase_project_name: '',
|
||||
@tracked firebase = {
|
||||
credentials: '',
|
||||
};
|
||||
|
||||
constructor() {
|
||||
@@ -38,26 +37,36 @@ export default class ConfigureNotificationChannelsComponent extends Component {
|
||||
apnConfig.private_key_file = null;
|
||||
apnConfig.private_key_file_id = '';
|
||||
apnConfig.private_key_path = '';
|
||||
apnConfig._private_key_path = '';
|
||||
|
||||
this.apn = apnConfig;
|
||||
}
|
||||
|
||||
@action removeFirebaseCredentialsFile() {
|
||||
const firebaseConfig = this.firebase;
|
||||
firebaseConfig.credentials_file = null;
|
||||
firebaseConfig.credentials_file_id = '';
|
||||
firebaseConfig.credentials = '';
|
||||
|
||||
this.firebase = firebaseConfig;
|
||||
}
|
||||
|
||||
@action uploadApnKey(file) {
|
||||
try {
|
||||
this.fetch.uploadFile.perform(
|
||||
file,
|
||||
{
|
||||
disk: 'local',
|
||||
path: `apn`,
|
||||
path: 'apn',
|
||||
subject_uuid: this.currentUser.companyId,
|
||||
subject_type: `company`,
|
||||
type: `apn_key`,
|
||||
subject_type: 'company',
|
||||
type: 'apn_key',
|
||||
},
|
||||
(uploadedFile) => {
|
||||
const apnConfig = this.apn;
|
||||
apnConfig.private_key_file = uploadedFile;
|
||||
apnConfig.private_key_file_id = uploadedFile.id;
|
||||
apnConfig.private_key_path = uploadedFile.path;
|
||||
apnConfig._private_key_path = uploadedFile.path;
|
||||
|
||||
this.apn = apnConfig;
|
||||
}
|
||||
@@ -67,6 +76,30 @@ export default class ConfigureNotificationChannelsComponent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
@action uploadFirebaseCredentials(file) {
|
||||
try {
|
||||
this.fetch.uploadFile.perform(
|
||||
file,
|
||||
{
|
||||
path: 'firebase',
|
||||
subject_uuid: this.currentUser.companyId,
|
||||
subject_type: 'company',
|
||||
type: 'firebase_credentials',
|
||||
},
|
||||
(uploadedFile) => {
|
||||
const firebaseConfig = this.firebase;
|
||||
firebaseConfig.credentials_file = uploadedFile;
|
||||
firebaseConfig.credentials_file_id = uploadedFile.id;
|
||||
firebaseConfig.credentials_file_path = uploadedFile.path;
|
||||
|
||||
this.firebase = firebaseConfig;
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
for (const key in config) {
|
||||
if (this[key] !== undefined) {
|
||||
@@ -94,9 +127,13 @@ export default class ConfigureNotificationChannelsComponent extends Component {
|
||||
const apnConfig = this.apn;
|
||||
delete apnConfig.private_key_file;
|
||||
|
||||
const firebaseConfig = this.firebase;
|
||||
delete firebaseConfig.credentials_file;
|
||||
|
||||
this.fetch
|
||||
.post('settings/notification-channels-config', {
|
||||
apn: apnConfig,
|
||||
firebase: firebaseConfig,
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success("Notification channel's configuration saved.");
|
||||
@@ -112,6 +149,7 @@ export default class ConfigureNotificationChannelsComponent extends Component {
|
||||
this.fetch
|
||||
.post('settings/test-notification-channels-config', {
|
||||
apn: this.apn,
|
||||
firebase: this.firebase,
|
||||
title: this.testTitle,
|
||||
message: this.testMessage,
|
||||
apnToken: this.apnToken,
|
||||
|
||||
@@ -2,6 +2,7 @@ import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* Controller responsible for handling two-factor authentication.
|
||||
* @class AuthTwoFaController
|
||||
@@ -53,6 +54,8 @@ export default class AuthTwoFaController extends Controller {
|
||||
|
||||
/**
|
||||
* The current 2FA identity in memory
|
||||
* @property {string} identity
|
||||
* @tracked
|
||||
*/
|
||||
@tracked identity;
|
||||
|
||||
|
||||
@@ -51,6 +51,13 @@ export default class ConsoleController extends Controller {
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Inject the `universe` service.
|
||||
*
|
||||
@@ -205,8 +212,8 @@ export default class ConsoleController extends Controller {
|
||||
const country = this.currentUser.country;
|
||||
|
||||
this.modalsManager.show('modals/create-or-join-org', {
|
||||
title: 'Create or join a organization',
|
||||
acceptButtonText: 'Confirm',
|
||||
title: this.intl.t('console.create-or-join-organization.modal-title'),
|
||||
acceptButtonText: this.intl.t('common.confirm'),
|
||||
acceptButtonIcon: 'check',
|
||||
acceptButtonIconPrefix: 'fas',
|
||||
action: 'join',
|
||||
@@ -226,13 +233,22 @@ export default class ConsoleController extends Controller {
|
||||
const { action, next, name, description, phone, currency, country, timezone } = modal.getOptions();
|
||||
|
||||
if (action === 'join') {
|
||||
return this.fetch.post('auth/join-organization', { next }).then(() => {
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success('You have joined a new organization!');
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 900);
|
||||
});
|
||||
return this.fetch
|
||||
.post('auth/join-organization', { next })
|
||||
.then(() => {
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success(this.intl.t('console.create-or-join-organization.join-success-notification'));
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
}
|
||||
|
||||
return this.fetch
|
||||
@@ -246,7 +262,7 @@ export default class ConsoleController extends Controller {
|
||||
})
|
||||
.then(() => {
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success('You have created a new organization!');
|
||||
this.notifications.success(this.intl.t('console.create-or-join-organization.create-success-notification'));
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
@@ -254,6 +270,9 @@ export default class ConsoleController extends Controller {
|
||||
},
|
||||
900
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -270,9 +289,9 @@ export default class ConsoleController extends Controller {
|
||||
}
|
||||
|
||||
this.modalsManager.confirm({
|
||||
title: `Are you sure you want to switch organization to ${organization.name}?`,
|
||||
body: `By confirming your account will remain logged in, but your primary organization will be switched.`,
|
||||
acceptButtonText: `Yes, I want to switch organization`,
|
||||
title: this.intl.t('console.switch-organization.modal-title', { organizationName: organization.name }),
|
||||
body: this.intl.t('console.switch-organization.modal-body'),
|
||||
acceptButtonText: this.intl.t('console.switch-organization.modal-accept-button-text'),
|
||||
acceptButtonScheme: 'primary',
|
||||
confirm: (modal) => {
|
||||
modal.startLoading();
|
||||
@@ -281,10 +300,14 @@ export default class ConsoleController extends Controller {
|
||||
.post('auth/switch-organization', { next: organization.uuid })
|
||||
.then(() => {
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success('You have switched organizations');
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 900);
|
||||
this.notifications.success(this.intl.t('console.switch-organization.success-notification'));
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
@@ -295,8 +318,8 @@ export default class ConsoleController extends Controller {
|
||||
|
||||
@action viewChangelog() {
|
||||
this.modalsManager.show('modals/changelog', {
|
||||
title: 'Changelog',
|
||||
acceptButtonText: 'OK',
|
||||
title: this.intl.t('common.changelog'),
|
||||
acceptButtonText: this.intl.t('common.ok'),
|
||||
hideDeclineButton: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { task } from 'ember-concurrency';
|
||||
|
||||
/**
|
||||
* Controller for managing organizations in the admin console.
|
||||
@@ -41,6 +40,13 @@ export default class ConsoleAdminOrganizationsController extends Controller {
|
||||
*/
|
||||
@service filters;
|
||||
|
||||
/**
|
||||
* The search query param value.
|
||||
*
|
||||
* @var {String|null}
|
||||
*/
|
||||
@tracked query;
|
||||
|
||||
/**
|
||||
* The current page of data being viewed
|
||||
*
|
||||
@@ -104,14 +110,36 @@ export default class ConsoleAdminOrganizationsController extends Controller {
|
||||
filterable: true,
|
||||
filterComponent: 'filter/string',
|
||||
},
|
||||
{
|
||||
label: this.intl.t('console.admin.organizations.index.owner-name-column'),
|
||||
valuePath: 'owner.name',
|
||||
width: '200px',
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: this.intl.t('console.admin.organizations.index.owner-email-column'),
|
||||
valuePath: 'owner.email',
|
||||
width: '200px',
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
{
|
||||
label: this.intl.t('console.admin.organizations.index.phone-column'),
|
||||
valuePath: 'phone',
|
||||
valuePath: 'owner.phone',
|
||||
width: '200px',
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
filterComponent: 'filter/string',
|
||||
},
|
||||
{
|
||||
label: this.intl.t('console.admin.organizations.index.users-count-column'),
|
||||
valuePath: 'users_count',
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: this.intl.t('common.created-at'),
|
||||
valuePath: 'createdAt',
|
||||
@@ -119,15 +147,14 @@ export default class ConsoleAdminOrganizationsController extends Controller {
|
||||
];
|
||||
|
||||
/**
|
||||
* `search` is a task that performs a search query on the 'company' model in the store.
|
||||
* Update search query param and reset page to 1
|
||||
*
|
||||
* @method search
|
||||
* @param {string} query - The search query.
|
||||
* @returns {Promise} A promise that resolves with the search results.
|
||||
* @public
|
||||
* @param {Event} event
|
||||
* @memberof ConsoleAdminOrganizationsController
|
||||
*/
|
||||
@task({ restartable: true }) *search(event) {
|
||||
this.companies = yield this.store.query('company', { query: event.target.value });
|
||||
@action search(event) {
|
||||
this.query = event.target.value ?? '';
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,6 @@ import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { isBlank } from '@ember/utils';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
import { timeout } from 'ember-concurrency';
|
||||
|
||||
export default class ConsoleAdminOrganizationsIndexUsersController extends Controller {
|
||||
/**
|
||||
@@ -95,6 +92,17 @@ export default class ConsoleAdminOrganizationsIndexUsersController extends Contr
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Update search query param and reset page to 1
|
||||
*
|
||||
* @param {Event} event
|
||||
* @memberof ConsoleAdminOrganizationsController
|
||||
*/
|
||||
@action search(event) {
|
||||
this.nestedQuery = event.target.value ?? '';
|
||||
this.nestedPage = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the overlay component context object.
|
||||
*
|
||||
@@ -118,22 +126,4 @@ export default class ConsoleAdminOrganizationsIndexUsersController extends Contr
|
||||
|
||||
return this.router.transitionTo('console.admin.organizations.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* `search` is a task that performs a search query on the 'company' model in the store.
|
||||
*
|
||||
* @method search
|
||||
* @param {string} query - The search query.
|
||||
* @returns {Promise} A promise that resolves with the search results.
|
||||
* @public
|
||||
*/
|
||||
@task({ restartable: true }) *search(event) {
|
||||
const searchQuery = event.target.value ?? '';
|
||||
if (isBlank(searchQuery)) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield timeout(600);
|
||||
this.nestedQuery = searchQuery;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
export function initialize() {
|
||||
const socketClusterClientScript = document.createElement('script');
|
||||
socketClusterClientScript.src = '/assets/socketcluster-client.min.js';
|
||||
document.body.appendChild(socketClusterClientScript);
|
||||
// Check if the script already exists
|
||||
// Only insert the script tag if it doesn't already exist
|
||||
if (!document.querySelector('script[data-socketcluster-client]')) {
|
||||
const socketClusterClientScript = document.createElement('script');
|
||||
socketClusterClientScript.setAttribute('data-socketcluster-client', '1');
|
||||
socketClusterClientScript.src = '/assets/socketcluster-client.min.js';
|
||||
document.body.appendChild(socketClusterClientScript);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -29,6 +29,7 @@ export default class CategoryModel extends Model {
|
||||
@attr('string') slug;
|
||||
@attr('string') order;
|
||||
@attr('raw') translations;
|
||||
@attr('raw') meta;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') deleted_at;
|
||||
|
||||
@@ -13,6 +13,7 @@ export default class Company extends Model {
|
||||
@attr('string') place_uuid;
|
||||
|
||||
/** @relationships */
|
||||
@belongsTo('user') owner;
|
||||
@belongsTo('file') logo;
|
||||
@belongsTo('file') backdrop;
|
||||
|
||||
@@ -23,6 +24,7 @@ export default class Company extends Model {
|
||||
@attr('string') backdrop_url;
|
||||
@attr('string') description;
|
||||
@attr('raw') options;
|
||||
@attr('number') users_count;
|
||||
@attr('string') type;
|
||||
@attr('string') currency;
|
||||
@attr('string') country;
|
||||
|
||||
54
console/app/models/custom-field-value.js
Normal file
54
console/app/models/custom-field-value.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { format, formatDistanceToNow } from 'date-fns';
|
||||
|
||||
function isValidFileObjectJson(str) {
|
||||
return typeof str === 'string' && str.startsWith('{') && str.endsWith('}');
|
||||
}
|
||||
|
||||
export default class CustomFieldValueModel extends Model {
|
||||
/** @ids */
|
||||
@attr('string') company_uuid;
|
||||
@attr('string') custom_field_uuid;
|
||||
@attr('string') subject_uuid;
|
||||
@attr('string') subject_type;
|
||||
|
||||
/** @attributes */
|
||||
@attr('string') value;
|
||||
@attr('string') value_type;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') created_at;
|
||||
@attr('date') updated_at;
|
||||
@attr('date') deleted_at;
|
||||
|
||||
/** @computed */
|
||||
@computed('value') get asFile() {
|
||||
const owner = getOwner(this);
|
||||
const fetch = owner.lookup(`service:fetch`);
|
||||
const value = this.value;
|
||||
if (!isValidFileObjectJson(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fileModel = fetch.jsonToModel(value, 'file');
|
||||
return fileModel;
|
||||
}
|
||||
|
||||
@computed('created_at') get createdAgo() {
|
||||
return formatDistanceToNow(this.created_at);
|
||||
}
|
||||
|
||||
@computed('created_at') get createdAt() {
|
||||
return format(this.created_at, 'PPP p');
|
||||
}
|
||||
|
||||
@computed('updated_at') get updatedAgo() {
|
||||
return formatDistanceToNow(this.updated_at);
|
||||
}
|
||||
|
||||
@computed('updated_at') get updatedAt() {
|
||||
return format(this.updated_at, 'PPP p');
|
||||
}
|
||||
}
|
||||
48
console/app/models/custom-field.js
Normal file
48
console/app/models/custom-field.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { format, formatDistanceToNow } from 'date-fns';
|
||||
|
||||
export default class CustomFieldModel extends Model {
|
||||
/** @ids */
|
||||
@attr('string') company_uuid;
|
||||
@attr('string') category_uuid;
|
||||
@attr('string') subject_uuid;
|
||||
@attr('string') subject_type;
|
||||
|
||||
/** @attributes */
|
||||
@attr('string') name;
|
||||
@attr('string') description;
|
||||
@attr('string') help_text;
|
||||
@attr('string') label;
|
||||
@attr('string') type;
|
||||
@attr('string') component;
|
||||
@attr('string') default_value;
|
||||
@attr('number') order;
|
||||
@attr('boolean') required;
|
||||
@attr('boolean', { defaultValue: true }) editable;
|
||||
@attr('raw') options;
|
||||
@attr('raw') validation_rules;
|
||||
@attr('raw') meta;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') created_at;
|
||||
@attr('date') updated_at;
|
||||
@attr('date') deleted_at;
|
||||
|
||||
/** @computed */
|
||||
@computed('created_at') get createdAgo() {
|
||||
return formatDistanceToNow(this.created_at);
|
||||
}
|
||||
|
||||
@computed('created_at') get createdAt() {
|
||||
return format(this.created_at, 'PPP p');
|
||||
}
|
||||
|
||||
@computed('updated_at') get updatedAgo() {
|
||||
return formatDistanceToNow(this.updated_at);
|
||||
}
|
||||
|
||||
@computed('updated_at') get updatedAt() {
|
||||
return format(this.updated_at, 'PPP p');
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
import { format, formatDistanceToNow } from 'date-fns';
|
||||
|
||||
export default class NotificationModel extends Model {
|
||||
|
||||
@@ -14,11 +14,6 @@ export default class ConsoleAdminOrganizationsRoute extends Route {
|
||||
};
|
||||
|
||||
model(params) {
|
||||
return this.store.query('company', params);
|
||||
}
|
||||
|
||||
setupController(controller, model) {
|
||||
super.setupController(controller, model);
|
||||
controller.companies = model;
|
||||
return this.store.query('company', { view: 'admin', ...params });
|
||||
}
|
||||
}
|
||||
|
||||
15
console/app/serializers/company.js
Normal file
15
console/app/serializers/company.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
|
||||
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
|
||||
|
||||
export default class CompanySerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
|
||||
/**
|
||||
* Embedded relationship attributes
|
||||
*
|
||||
* @var {Object}
|
||||
*/
|
||||
get attrs() {
|
||||
return {
|
||||
owner: { embedded: 'always' },
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -44,3 +44,7 @@ body[data-theme='dark'] .two-fa-enforcement-alert button#two-fa-setup-button.btn
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
body.console-admin-organizations-index-index .next-table-wrapper > table {
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<FaIcon @icon="check-circle" @size="lg" class="text-green-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-green-900 dark:text-green-900">
|
||||
<strong>Check your {{this.selectedMethod}}</strong><br />
|
||||
We've sent you a verification code. Enter the code below to complete the login process.
|
||||
<strong>{{t "auth.two-fa.verify-code.check-title"}}</strong><br />
|
||||
{{t "auth.two-fa.verify-code.check-subtitle"}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -26,22 +26,22 @@
|
||||
{{/if}}
|
||||
{{#if this.isCodeExpired}}
|
||||
<InfoBlock>
|
||||
<div>Your 2FA authentication code has expired. You can request another code if you need more time.</div>
|
||||
<Button @type="primary" @wrapperClass="mt-2" @text="Resend Code" @icon="arrow-rotate-right" @onClick={{this.resendCode}} />
|
||||
<div>{{t "auth.two-fa.verify-code.expired-help-text"}}</div>
|
||||
<Button @type="primary" @wrapperClass="mt-2" @text={{t "auth.two-fa.verify-code.resend-code"}} @icon="arrow-rotate-right" @onClick={{this.resendCode}} />
|
||||
</InfoBlock>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<Button @buttonType="submit" @type="primary" @text="Verify Code" @icon="check-circle" @wrapperClass="btn-block" @isLoading={{this.isLoading}} />
|
||||
<Button @buttonType="submit" @type="primary" @text={{t "auth.two-fa.verify-code.verify-code"}} @icon="check-circle" @wrapperClass="btn-block" @isLoading={{this.isLoading}} />
|
||||
</div>
|
||||
|
||||
<div class="text-center flex flex-row items-center justify-center space-x-4 mt-3.5">
|
||||
<a href="#" class="text-sm text-blue-500 hover:underline inline-block" {{on "click" this.resendCode}}>
|
||||
Resend Code
|
||||
{{t "auth.two-fa.verify-code.resend-code"}}
|
||||
</a>
|
||||
<a href="#" class="text-sm text-danger hover:underline inline-block" {{on "click" this.cancelTwoFactor}}>
|
||||
Cancel Two-Factor
|
||||
{{t "auth.two-fa.verify-code.cancel-two-factor"}}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,15 +1,16 @@
|
||||
{{page-title (t "console.admin.organizations.index.title")}}
|
||||
<Layout::Section::Header @title={{t "console.admin.organizations.index.title"}} @searchQuery={{this.query}} @onSearch={{perform this.search}} />
|
||||
{{!-- template-lint-disable no-unbound --}}
|
||||
<Layout::Section::Header @title={{t "console.admin.organizations.index.title"}} @searchQuery={{unbound this.query}} @onSearch={{this.search}} />
|
||||
|
||||
<Layout::Section::Body>
|
||||
<Table
|
||||
@rows={{this.companies}}
|
||||
@rows={{@model}}
|
||||
@columns={{this.columns}}
|
||||
@selectable={{true}}
|
||||
@canSelectAll={{true}}
|
||||
@selectable={{false}}
|
||||
@canSelectAll={{false}}
|
||||
@onSetup={{fn (mut this.table)}}
|
||||
@pagination={{true}}
|
||||
@paginationMeta={{this.companies}}
|
||||
@paginationMeta={{@model.meta}}
|
||||
@page={{this.page}}
|
||||
@onPageChange={{fn (mut this.page)}}
|
||||
@tfootVerticalOffset="53"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title (t "common.users")}}
|
||||
<Overlay @isOpen={{@isOpen}} @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width={{or this.width @width "600px"}}>
|
||||
<Overlay @isOpen={{@isOpen}} @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="600px" @isResizable={{true}}>
|
||||
<Overlay::Header @title={{concat this.company.name " - " (t "common.users")}} @hideStatusDot={{true}} @titleWrapperClass="leading-5">
|
||||
<div class="flex flex-1 justify-end">
|
||||
<Button @type="default" @icon="times" @helpText={{t "common.close-and-save"}} @onClick={{this.onPressClose}} />
|
||||
@@ -7,12 +7,13 @@
|
||||
</Overlay::Header>
|
||||
|
||||
<Overlay::Body class="without-padding">
|
||||
<Layout::Section::Header @title={{t "console.admin.organizations.users.title"}} @searchQuery={{this.nestedQuery}} @onSearch={{perform this.search}}>
|
||||
{{!-- template-lint-disable no-unbound --}}
|
||||
<Layout::Section::Header @title={{t "console.admin.organizations.users.title"}} @searchQuery={{unbound this.nestedQuery}} @onSearch={{this.search}}>
|
||||
<Pagination @meta={{@model.meta}} @page={{this.nestedPage}} @onPageChange={{fn (mut this.nestedPage)}} @metaInfoClass="hidden" @metaInfoWrapperClass="within-layout-section-header" />
|
||||
</Layout::Section::Header>
|
||||
|
||||
<Layout::Section::Body>
|
||||
<Table @rows={{@model}} @columns={{this.columns}} @selectable={{true}} @canSelectAll={{true}} @onSetup={{fn (mut this.table)}} />
|
||||
<Table @rows={{@model}} @columns={{this.columns}} @selectable={{false}} @canSelectAll={{false}} @onSetup={{fn (mut this.table)}} />
|
||||
</Layout::Section::Body>
|
||||
</Overlay::Body>
|
||||
</Overlay>
|
||||
@@ -46,9 +46,12 @@ module.exports = function (environment) {
|
||||
driverImage: getenv('DEFAULT_DRIVER_IMAGE', 'https://s3.ap-southeast-1.amazonaws.com/flb-assets/static/no-avatar.png'),
|
||||
userImage: getenv('DEFAULT_USER_IMAGE', 'https://s3.ap-southeast-1.amazonaws.com/flb-assets/static/no-avatar.png'),
|
||||
contactImage: getenv('DEFAULT_CONTACT_IMAGE', 'https://s3.ap-southeast-1.amazonaws.com/flb-assets/static/no-avatar.png'),
|
||||
entityImage: getenv('DEFAULT_ENTITY_IMAGE', 'https://flb-assets.s3-ap-southeast-1.amazonaws.com/static/parcels/medium.png'),
|
||||
vendorImage: getenv('DEFAULT_VENDOR_IMAGE', 'https://s3.ap-southeast-1.amazonaws.com/flb-assets/static/no-avatar.png'),
|
||||
vehicleImage: getenv('DEFAULT_VEHICLE_IMAGE', 'https://s3.ap-southeast-1.amazonaws.com/flb-assets/static/vehicle-placeholder.png'),
|
||||
vehicleAvatar: getenv('DEFAUL_VEHICLE_AVATAR', 'https://flb-assets.s3-ap-southeast-1.amazonaws.com/static/vehicle-icons/mini_bus.svg'),
|
||||
driverAvatar: getenv('DEFAUL_DRIVER_AVATAR', 'https://flb-assets.s3-ap-southeast-1.amazonaws.com/static/driver-icons/moto-driver.png'),
|
||||
placeAvatar: getenv('DEFAUL_PLACE_AVATAR', 'https://flb-assets.s3-ap-southeast-1.amazonaws.com/static/place-icons/basic-building.png'),
|
||||
},
|
||||
|
||||
'ember-simple-auth': {
|
||||
@@ -60,6 +63,11 @@ module.exports = function (environment) {
|
||||
keyDelimiter: '/',
|
||||
includeEmberDataSupport: true,
|
||||
},
|
||||
|
||||
'ember-cli-notifications': {
|
||||
autoClear: true,
|
||||
clearDuration: 1000 * 3.5,
|
||||
},
|
||||
};
|
||||
|
||||
if (environment === 'development') {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fleetbase/console",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.10",
|
||||
"private": true,
|
||||
"description": "Fleetbase Console",
|
||||
"repository": "https://github.com/fleetbase/fleetbase",
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "node prebuild.js",
|
||||
"build": "pnpm run prebuild && ember build --environment=production",
|
||||
"build": "pnpm run prebuild && ember build",
|
||||
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
|
||||
"lint:css": "stylelint \"**/*.css\"",
|
||||
"lint:css:fix": "concurrently \"npm:lint:css -- --fix\"",
|
||||
@@ -24,15 +24,16 @@
|
||||
"postinstall": "patch-package",
|
||||
"lint:intl": "fleetbase-intl-lint",
|
||||
"start": "pnpm run prebuild && ember serve",
|
||||
"start:dev": "pnpm run prebuild && ember serve --environment development",
|
||||
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
|
||||
"test:ember": "ember test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fleetbase/ember-core": "^0.2.4",
|
||||
"@fleetbase/ember-ui": "^0.2.10",
|
||||
"@fleetbase/storefront-engine": "^0.2.11",
|
||||
"@fleetbase/fleetops-engine": "^0.4.7",
|
||||
"@fleetbase/fleetops-data": "^0.1.8",
|
||||
"@fleetbase/ember-core": "^0.2.5",
|
||||
"@fleetbase/ember-ui": "^0.2.11",
|
||||
"@fleetbase/fleetops-engine": "^0.4.17",
|
||||
"@fleetbase/fleetops-data": "^0.1.12",
|
||||
"@fleetbase/storefront-engine": "^0.3.4",
|
||||
"@fleetbase/dev-engine": "^0.2.1",
|
||||
"@fleetbase/iam-engine": "^0.0.9",
|
||||
"@fleetbase/leaflet-routing-machine": "^3.2.16",
|
||||
@@ -141,9 +142,9 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@fleetbase/ember-core": "^0.2.4",
|
||||
"@fleetbase/ember-ui": "^0.2.10",
|
||||
"@fleetbase/fleetops-data": "^0.1.8"
|
||||
"@fleetbase/ember-core": "^0.2.5",
|
||||
"@fleetbase/ember-ui": "^0.2.11",
|
||||
"@fleetbase/fleetops-data": "^0.1.12"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
|
||||
1423
console/pnpm-lock.yaml
generated
1423
console/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
14
console/tests/unit/models/custom-field-test.js
Normal file
14
console/tests/unit/models/custom-field-test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Model | custom field', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('custom-field', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
14
console/tests/unit/models/custom-field-value-test.js
Normal file
14
console/tests/unit/models/custom-field-value-test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Model | custom field value', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('custom-field-value', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
24
console/tests/unit/serializers/company-test.js
Normal file
24
console/tests/unit/serializers/company-test.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Serializer | company', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let serializer = store.serializerFor('company');
|
||||
|
||||
assert.ok(serializer);
|
||||
});
|
||||
|
||||
test('it serializes records', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let record = store.createRecord('company', {});
|
||||
|
||||
let serializedRecord = record.serialize();
|
||||
|
||||
assert.ok(serializedRecord);
|
||||
});
|
||||
});
|
||||
@@ -13,8 +13,10 @@ terms:
|
||||
search: Search
|
||||
search-input: Search Input
|
||||
common:
|
||||
confirm: Confirm
|
||||
edit: Edit
|
||||
save: Save
|
||||
save-changes: Save Changes
|
||||
cancel: Cancel
|
||||
2fa-config: 2FA Config
|
||||
account: Account
|
||||
@@ -56,6 +58,9 @@ common:
|
||||
status: Status
|
||||
close-and-save: Close and Save
|
||||
users: Users
|
||||
changelog: Changelog
|
||||
ok: OK
|
||||
select-file: Select File
|
||||
component:
|
||||
file:
|
||||
dropdown-label: File actions
|
||||
@@ -68,7 +73,9 @@ component:
|
||||
upload-images-videos: Upload Images & Videos
|
||||
upload-documents: Upload Documents
|
||||
upload-documents-files: Upload Documents & Files
|
||||
upload-avatar-files: Upload Custom Avatars
|
||||
dropzone-supported-images-videos: Drag and drop image and video files onto this dropzone
|
||||
dropzone-supported-avatars: Drag and drop SVG or PNG files
|
||||
dropzone-supported-files: Drag and drop files onto this dropzone
|
||||
or-select-button-text: or select files to upload.
|
||||
upload-queue: Upload Queue
|
||||
@@ -124,6 +131,13 @@ auth:
|
||||
send-by-sms: Send by SMS
|
||||
two-fa:
|
||||
verify-code:
|
||||
verification-code: Verification Code
|
||||
check-title: Check your email or phone
|
||||
check-subtitle: We've sent you a verification code. Enter the code below to complete the login process.
|
||||
expired-help-text: Your 2FA authentication code has expired. You can request another code if you need more time.
|
||||
resend-code: Resend Code
|
||||
verify-code: Verify Code
|
||||
cancel-two-factor: Cancel Two-Factor
|
||||
invalid-session-error-notification: Invalid session. Please try again.
|
||||
verification-successful-notification: Verification successful!
|
||||
verification-code-expired-notification: Verification code has expired. Please request a new one.
|
||||
@@ -175,6 +189,15 @@ auth:
|
||||
submit-button: Reset Password
|
||||
back-button: Back
|
||||
console:
|
||||
create-or-join-organization:
|
||||
modal-title: Create or join a organization
|
||||
join-success-notification: You have joined a new organization!
|
||||
create-success-notification: You have created a new organization!
|
||||
switch-organization:
|
||||
modal-title: Are you sure you want to switch organization to {organizationName}?
|
||||
modal-body: By confirming your account will remain logged in, but your primary organization will be switched.
|
||||
modal-accept-button-text: Yes, I want to switch organization
|
||||
success-notification: You have switched organizations
|
||||
account:
|
||||
index:
|
||||
upload-new: Upload new
|
||||
@@ -213,6 +236,10 @@ console:
|
||||
organizations:
|
||||
index:
|
||||
title: Organizations
|
||||
owner-name-column: Owner
|
||||
owner-phone-column: Owner Phone
|
||||
owner-email-column: Owner Phone
|
||||
users-count-column: Users
|
||||
phone-column: Phone
|
||||
email-column: Email
|
||||
users:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# syntax = docker/dockerfile:1.2
|
||||
# Base stage
|
||||
FROM dunglas/frankenphp:sha-7454826-php8.2-alpine as base
|
||||
FROM dunglas/frankenphp:1.1.0-php8.2-bookworm as base
|
||||
|
||||
# Install packages
|
||||
RUN apk update && apk add git openssh bind-tools mycli nodejs npm \
|
||||
RUN apt-get update && apt-get install -y git bind9-utils mycli nodejs npm \
|
||||
&& mkdir -p /root/.ssh \
|
||||
&& ssh-keyscan github.com >> /root/.ssh/known_hosts
|
||||
|
||||
@@ -98,12 +98,12 @@ RUN chmod 0600 ./crontab
|
||||
# Scheduler dev stage
|
||||
FROM scheduler-base as scheduler-dev
|
||||
ENTRYPOINT []
|
||||
CMD ["go-crond", "--verbose", "--no-auto", "root:./crontab"]
|
||||
CMD ["go-crond", "--verbose", "root:./crontab"]
|
||||
|
||||
# Scheduler stage
|
||||
FROM scheduler-base as scheduler
|
||||
ENTRYPOINT ["/sbin/ssm-parent", "-c", ".ssm-parent.yaml", "run", "--"]
|
||||
CMD ["go-crond", "--verbose", "--no-auto", "root:./crontab"]
|
||||
CMD ["go-crond", "--verbose", "root:./crontab"]
|
||||
|
||||
# Events stage
|
||||
FROM base as events
|
||||
|
||||
@@ -12,7 +12,32 @@ server {
|
||||
send_timeout 600;
|
||||
index index.php;
|
||||
|
||||
# tweaks
|
||||
client_max_body_size 600M;
|
||||
client_body_buffer_size 1m;
|
||||
client_header_buffer_size 1k;
|
||||
large_client_header_buffers 4 16k;
|
||||
keepalive_timeout 2 2;
|
||||
|
||||
#gzip tweaks
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_min_length 1100;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain application/javascript application/json application/x-javascript text/xml text/css;
|
||||
|
||||
#open file cache tweaks
|
||||
open_file_cache max=2000 inactive=20s;
|
||||
open_file_cache_valid 60s;
|
||||
open_file_cache_min_uses 5;
|
||||
open_file_cache_errors off;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_pass http://${NGINX_APPLICATION_HOSTNAME}:8000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,13 @@ app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
|
||||
{{- define "helm.commonVariables" -}}
|
||||
- name: CACHE_URL
|
||||
value: $(REDIS_SERVICE_PORT)/1
|
||||
value: tcp://redis-service.{{ .Release.Namespace }}.svc.cluster.local/1
|
||||
- name: CACHE_DRIVER
|
||||
value: redis
|
||||
- name: SOCKETCLUSTER_PORT
|
||||
value: "80"
|
||||
- name: SOCKETCLUSTER_HOST
|
||||
value: $(SOCKETCLUSTER_SERVICE_HOST)
|
||||
value: socketcluster.{{ .Release.Namespace }}.svc.cluster.local
|
||||
{{- end }}
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
{{- include "helm.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
replicas: 1
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
@@ -66,7 +66,7 @@ metadata:
|
||||
{{- include "helm.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
replicas: 1
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
|
||||
@@ -10,7 +10,7 @@ metadata:
|
||||
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install,pre-upgrade
|
||||
"helm.sh/hook-weight": "0"
|
||||
"helm.sh/hook-weight": "20"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation
|
||||
spec:
|
||||
template:
|
||||
|
||||
@@ -30,4 +30,3 @@ spec:
|
||||
port: 6379
|
||||
targetPort: 6379
|
||||
type: ClusterIP
|
||||
|
||||
|
||||
@@ -33,4 +33,3 @@ spec:
|
||||
port: 80
|
||||
targetPort: 8000
|
||||
type: ClusterIP
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
replicaCount: 2
|
||||
|
||||
image:
|
||||
repository: OVERRIDE
|
||||
@@ -25,10 +25,12 @@ serviceAccount:
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
podSecurityContext:
|
||||
{}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
securityContext:
|
||||
{}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
@@ -54,7 +56,8 @@ ingress:
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
resources: {}
|
||||
resources:
|
||||
{}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
|
||||
Submodule packages/core-api updated: c98e08a257...c614dfd01c
Submodule packages/ember-core updated: e9f4dc4086...d0c575778c
Submodule packages/ember-ui updated: e532986a5b...813241f0c3
Submodule packages/fleetops updated: 9ce45d0402...bd8fc66dbe
Submodule packages/fleetops-data updated: 9d92927c9b...b5ba2a311c
Submodule packages/storefront updated: 9096a19131...da534cea36
@@ -62,7 +62,7 @@ deploy:
|
||||
|
||||
# build the builder
|
||||
- docker buildx build -t frontend-${STACK}-${VERSION} --load --target builder -f console/Dockerfile .
|
||||
- docker run -i --rm --env-file /tmp/dotenv.file -v "$(pwd)/console_dist:/app/dist" frontend-${STACK}-${VERSION} pnpm build
|
||||
- docker run -i --rm --env-file /tmp/dotenv.file -v "$(pwd)/console_dist:/app/dist" frontend-${STACK}-${VERSION} pnpm build --environment production
|
||||
- |-
|
||||
DEPLOY_BUCKET=${PROJECT}-${STACK}
|
||||
wget -O- https://github.com/bep/s3deploy/releases/download/v2.11.0/s3deploy_2.11.0_linux-amd64.tar.gz | tar xzv -f - s3deploy
|
||||
|
||||
Reference in New Issue
Block a user