diff --git a/addon/services/crud.js b/addon/services/crud.js index 9f08f91..f7e66f4 100644 --- a/addon/services/crud.js +++ b/addon/services/crud.js @@ -198,25 +198,29 @@ export default class CrudService extends Service { // set the model uri endpoint const modelEndpoint = dasherize(pluralize(modelName)); + const exportParams = options.params ?? {}; this.modalsManager.show('modals/export-form', { title: `Export ${pluralize(modelName)}`, acceptButtonText: 'Download', modalClass: 'modal-sm', + format: 'xlsx', formatOptions: ['csv', 'xlsx', 'xls', 'html', 'pdf'], setFormat: ({ target }) => { this.modalsManager.setOption('format', target.value || null); }, confirm: (modal, done) => { - const format = modal.getOption('format') || 'xlsx'; + const format = modal.getOption('format') ?? 'xlsx'; modal.startLoading(); return this.fetch .download( `${modelEndpoint}/export`, { format, + ...exportParams, }, { + method: 'POST', fileName: `${modelEndpoint}-${formatDate(new Date(), 'yyyy-MM-dd-HH:mm')}.${format}`, } ) diff --git a/addon/services/fetch.js b/addon/services/fetch.js index ae67c32..79fbcb6 100644 --- a/addon/services/fetch.js +++ b/addon/services/fetch.js @@ -15,6 +15,7 @@ import corslite from '../utils/corslite'; import getMimeType from '../utils/get-mime-type'; import download from '../utils/download'; import getUserOptions from '../utils/get-user-options'; +import isEmptyObject from '../utils/is-empty-object'; import fetch from 'fetch'; if (isBlank(config.API.host)) { @@ -339,7 +340,7 @@ export default class FetchService extends Service { return this.cachedGet(...arguments); } - const urlParams = !isBlank(query) ? new URLSearchParams(query).toString() : ''; + const urlParams = !isEmptyObject(query) ? new URLSearchParams(query).toString() : ''; return this.request(`${path}${urlParams ? '?' + urlParams : ''}`, 'GET', {}, options); } @@ -506,7 +507,7 @@ export default class FetchService extends Service { let version = options?.version ?? 'v1'; let host = options?.host ?? `https://${options?.subdomain ?? 'routing'}.fleetbase.io`; let route = coordinates.map((coords) => coords.join(',')).join(';'); - let params = !isBlank(query) ? new URLSearchParams(query).toString() : ''; + let params = !isEmptyObject(query) ? new URLSearchParams(query).toString() : ''; let path = `${host}/${service}/${version}/${profile}/${route}`; let url = `${path}${params ? '?' + params : ''}`; @@ -598,13 +599,25 @@ export default class FetchService extends Service { */ download(path, query = {}, options = {}) { const headers = Object.assign(this.getHeaders(), options.headers ?? {}); + const method = options.method ?? 'GET'; + const credentials = options.credentials ?? this.credentials; + const baseUrl = `${options.host || this.host}/${options.namespace || this.namespace}`; + const isReadOnlyRequest = ['GET', 'HEAD'].includes(method.toUpperCase()); + const params = isReadOnlyRequest && !isEmptyObject(query) ? `?${new URLSearchParams(query).toString()}` : ''; + const body = !isReadOnlyRequest ? JSON.stringify(query) : {}; + const fetchOptions = { + method, + credentials, + headers, + }; + + // Only supply body to fetch if not GET or HEAD request + if (!isReadOnlyRequest) { + fetchOptions.body = body; + } return new Promise((resolve, reject) => { - return fetch(`${options.host || this.host}/${options.namespace || this.namespace}/${path}?${!isBlank(query) ? new URLSearchParams(query).toString() : ''}`, { - method: 'GET', - credentials: options.credentials || this.credentials, - headers, - }) + return fetch(`${baseUrl}/${path}${params}`, fetchOptions) .then((response) => { options.fileName = this.getFilenameFromResponse(response, options.fileName); options.mimeType = this.getMimeTypeFromResponse(response, options.mimeType); diff --git a/addon/services/loader.js b/addon/services/loader.js index 557aa69..15e8288 100644 --- a/addon/services/loader.js +++ b/addon/services/loader.js @@ -75,9 +75,10 @@ export default class LoaderService extends Service { } const loadingMessage = typeof options.loadingMessage === 'string' ? options.loadingMessage : 'Loading...'; - const opacity = typeof options.opacity === 'number' ? options.opacity : 0.1; + const opacity = typeof options.opacity === 'number' ? options.opacity : 0; const isDarkMode = document.body.dataset.theme ? document.body.dataset.theme === 'dark' : true; const preserveTargetPosition = options.preserveTargetPosition === true; + const loaderContainerClass = options.loaderContainerClass ?? ''; if (!preserveTargetPosition) { target.style.position = 'relative'; @@ -86,7 +87,7 @@ export default class LoaderService extends Service { let loader = document.createElement('div'); loader.classList.add('overloader'); loader.style.backgroundColor = isDarkMode ? `rgba(128, 128, 128, ${opacity})` : `rgba(249, 250, 251, ${opacity})`; - loader.innerHTML = `
+ loader.innerHTML = `
diff --git a/addon/utils/is-empty-object.js b/addon/utils/is-empty-object.js new file mode 100644 index 0000000..28073d2 --- /dev/null +++ b/addon/utils/is-empty-object.js @@ -0,0 +1,3 @@ +export default function isEmptyObject(obj) { + return Object.keys(obj).length === 0 && obj.constructor === Object; +} diff --git a/app/utils/is-empty-object.js b/app/utils/is-empty-object.js new file mode 100644 index 0000000..63726f8 --- /dev/null +++ b/app/utils/is-empty-object.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/ember-core/utils/is-empty-object'; diff --git a/package.json b/package.json index 2b68191..58caa7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/ember-core", - "version": "0.2.9", + "version": "0.2.10", "description": "Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.", "keywords": [ "fleetbase-core", diff --git a/tests/unit/utils/is-empty-object-test.js b/tests/unit/utils/is-empty-object-test.js new file mode 100644 index 0000000..eb8285d --- /dev/null +++ b/tests/unit/utils/is-empty-object-test.js @@ -0,0 +1,10 @@ +import isEmptyObject from 'dummy/utils/is-empty-object'; +import { module, test } from 'qunit'; + +module('Unit | Utility | is-empty-object', function () { + // TODO: Replace this with your real tests. + test('it works', function (assert) { + let result = isEmptyObject(); + assert.ok(result); + }); +});