Compare commits

...

15 Commits

Author SHA1 Message Date
Ron
908e0eb9ee Merge pull request #219 from fleetbase/dev-v0.4.7
v0.4.7
2024-02-22 17:11:51 +07:00
Ronald A. Richardson
6438138913 add upgraded core-api submodule 2024-02-22 17:57:22 +08:00
Ronald A. Richardson
aee06a2146 update composer lockfile 2024-02-22 17:54:01 +08:00
Ronald A. Richardson
ccacc6c597 adds fleetops migrations hotfix 2024-02-22 12:43:33 +08:00
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
24 changed files with 1918 additions and 1454 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

@@ -9,9 +9,9 @@
"license": "MIT",
"require": {
"php": "^8.0",
"fleetbase/core-api": "^1.4.4",
"fleetbase/fleetops-api": "^0.4.10",
"fleetbase/storefront-api": "^0.3.0",
"fleetbase/core-api": "^1.4.8",
"fleetbase/fleetops-api": "^0.4.14",
"fleetbase/storefront-api": "^0.3.1",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^10.0",
"laravel/octane": "^2.3",

1703
api/composer.lock generated

File diff suppressed because it is too large Load Diff

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

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

@@ -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.4",
"version": "0.4.7",
"private": true,
"description": "Fleetbase Console",
"repository": "https://github.com/fleetbase/fleetbase",
@@ -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.3.0",
"@fleetbase/fleetops-engine": "^0.4.10",
"@fleetbase/fleetops-data": "^0.1.8",
"@fleetbase/ember-ui": "^0.2.11",
"@fleetbase/fleetops-engine": "^0.4.14",
"@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

@@ -71,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
@@ -232,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

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