Compare commits

..

30 Commits

Author SHA1 Message Date
Ron
62c396b789 Merge pull request #218 from fleetbase/dev-v0.4.6
v0.4.6
2024-02-21 17:48:54 +07:00
Ronald A. Richardson
295da5f331 fix composer files 2024-02-21 18:44:27 +08:00
Ronald A. Richardson
e775ccc2c8 upgraded dependencies and prepared for release 2024-02-21 18:40:36 +08:00
Ronald A. Richardson
bd0759881f updated notification channel config for firebase and apn 2024-02-21 18:16:15 +08:00
Ronald A. Richardson
830ae69b1d hotfix: composer.lock with core-api upgrade 2024-02-17 20:04:35 +08:00
Ron
3b9a80866f Merge pull request #213 from fleetbase/dev-v0.4.5
v0.4.5
2024-02-17 18:26:25 +07:00
Ronald A. Richardson
76badbf949 patch admin organization browser; upgrade core-api; upgrade fleetops 2024-02-17 19:21:07 +08:00
Ronald A. Richardson
f968556585 hotfix organizations browsers in admin w/ more details, better querying and admin based view, upgrade of dependencies with major patches 2024-02-16 17:32:44 +07:00
Ronald A. Richardson
6d01eab305 use fixed fleetbase/laravel-mysql-spatial v1.0.2 2024-02-16 01:37:20 +07:00
Ronald A. Richardson
9406446db1 explicitly set production environment on gitlab cd workflow 2024-02-16 00:16:22 +07:00
Ronald A. Richardson
565db7bbce explicitly set production environment for cd workflows 2024-02-16 00:15:15 +07:00
Ron
003cb467e8 Merge pull request #212 from fleetbase/dev-v0.4.4
v0.4.4
2024-02-15 23:41:59 +07:00
Ronald A. Richardson
8e1b281e77 upgraded core-api v1.4.4 and fleetops v0.4.10 w/ critical patches for webhook and driver 2024-02-15 23:32:46 +07:00
Ronald A. Richardson
af4507cc87 added translations for create/join organization and migrated to use Dockerfile 2024-02-15 23:11:20 +07:00
Ronald A. Richardson
2a2c7d8426 use correct CONSOLE_HOST env variable in cors config 2024-02-14 17:05:59 +07:00
Ronald A. Richardson
077a4298b9 Update console Dockerfile to allow environment to be passed in build 2024-02-14 16:02:55 +07:00
Ron
af840d30d0 Merge pull request #211 from fleetbase/dev-v0.4.3
v0.4.3
2024-02-13 16:52:05 +08:00
Ronald A. Richardson
a253c0c6f7 Upgrade dependencies with critical patches 2024-02-13 16:50:21 +08:00
Ron
0fc1d6068d Merge pull request #210 from fleetbase/dev-v0.4.2
v0.4.2
2024-02-13 14:41:56 +08:00
Ronald A. Richardson
6993510d08 upgraded core-api for api credential key generation patch 2024-02-13 14:36:37 +08:00
Ron
4fcf09c1b8 Merge pull request #209 from fleetbase/dev-v0.4.1-version-patch
upgrade packages
2024-02-13 13:41:22 +08:00
Ronald A. Richardson
f89596a74b upgrade packages 2024-02-13 13:40:24 +08:00
Ron
d89e93f248 Merge pull request #208 from fleetbase/dev-v0.4.1-version-patch
v0.4.1 patch
2024-02-13 13:39:26 +08:00
Ronald A. Richardson
2f0c15bc93 bump version for v0.4.1 release 2024-02-13 13:37:34 +08:00
Ron
ae6c07006b Merge pull request #207 from fleetbase/dev-v0.4.1
v0.4.1
2024-02-13 13:26:53 +08:00
Ronald A. Richardson
d754641493 Upgraded to Fleet-Ops v0.4.8, Core API to v1.4.1 and added support for sendgrid officially. 2024-02-13 13:22:20 +08:00
Ron
bb0d706006 Merge pull request #205 from ekini/fix/crond 2024-02-10 15:02:38 +08:00
Eugene Dementyev
652bc363e1 Fix crond 2024-02-10 20:00:50 +13:00
Ron
363309af61 Merge pull request #204 from ekini/fix/helm_replicas 2024-02-10 15:00:30 +08:00
Eugene Dementyev
83791ea91c Scale deployment of the app to 2 by default 2024-02-10 19:57:37 +13:00
32 changed files with 1656 additions and 1299 deletions

View File

@@ -147,7 +147,7 @@ jobs:
run: |
set -eu
pnpm build
pnpm build --environment production
working-directory: ./console
- name: Deploy Console 🚀

View File

@@ -173,7 +173,7 @@ jobs:
run: |
set -eu
pnpm build
pnpm build --environment production
working-directory: ./console
- name: Deploy Console 🚀

View File

@@ -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.7",
"fleetbase/fleetops-api": "^0.4.13",
"fleetbase/storefront-api": "^0.3.1",
"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",

1152
api/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ 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' => [],

View File

@@ -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

View File

@@ -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>

View File

@@ -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,37 @@ 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 +77,31 @@ export default class ConfigureNotificationChannelsComponent extends Component {
}
}
@action uploadFirebaseCredentials(file) {
try {
this.fetch.uploadFile.perform(
file,
{
disk: 'local',
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 +129,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 +151,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,

View File

@@ -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;

View File

@@ -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,
});
}

View File

@@ -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;
}
/**

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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 });
}
}

View 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' },
};
}
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -1,6 +1,6 @@
{
"name": "@fleetbase/console",
"version": "0.4.0",
"version": "0.4.6",
"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-ui": "^0.2.11",
"@fleetbase/fleetops-engine": "^0.4.13",
"@fleetbase/fleetops-data": "^0.1.9",
"@fleetbase/storefront-engine": "^0.3.1",
"@fleetbase/dev-engine": "^0.2.1",
"@fleetbase/iam-engine": "^0.0.9",
"@fleetbase/leaflet-routing-machine": "^3.2.16",
@@ -142,8 +143,8 @@
"pnpm": {
"overrides": {
"@fleetbase/ember-core": "^0.2.4",
"@fleetbase/ember-ui": "^0.2.10",
"@fleetbase/fleetops-data": "^0.1.8"
"@fleetbase/ember-ui": "^0.2.11",
"@fleetbase/fleetops-data": "^0.1.9"
}
},
"prettier": {

1401
console/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View 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);
});
});

View File

@@ -13,6 +13,7 @@ terms:
search: Search
search-input: Search Input
common:
confirm: Confirm
edit: Edit
save: Save
cancel: Cancel
@@ -56,6 +57,8 @@ common:
status: Status
close-and-save: Close and Save
users: Users
changelog: Changelog
ok: OK
component:
file:
dropdown-label: File actions
@@ -68,7 +71,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 +129,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 +187,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 +234,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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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