@lipagas/ember-core
Version:
Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.
245 lines (220 loc) • 8.39 kB
JavaScript
import Service from '@ember/service';
import { inject as service } from '@ember/service';
import { action, get } from '@ember/object';
import { isArray } from '@ember/array';
import { dasherize } from '@ember/string';
import { later } from '@ember/runloop';
import { pluralize } from 'ember-inflector';
import { format as formatDate } from 'date-fns';
import getModelName from '../utils/get-model-name';
import getWithDefault from '../utils/get-with-default';
import humanize from '../utils/humanize';
import first from '../utils/first';
export default class CrudService extends Service {
/**
* Inject the `fetch` service
*
* @var {Service}
*/
fetch;
/**
* Inject the `modalsManager` service
*
* @var {Service}
*/
modalsManager;
/**
* Inject the `notifications` service
*
* @var {Service}
*/
notifications;
/**
* Inject the `store` service
*
* @var {Service}
*/
store;
/**
* Generic deletion modal with options
*
* @param {Model} model
* @param {Object} options
* @void
*/
delete(model, options = {}) {
const modelName = getModelName(model, get(options, 'modelName'), { humanize: true, capitalizeWords: true });
this.modalsManager.confirm({
title: `Are you sure to delete this ${modelName}?`,
args: ['model'],
model,
confirm: (modal) => {
if (typeof options.onConfirm === 'function') {
options.onConfirm(model);
}
modal.startLoading();
return model
.destroyRecord()
.then((model) => {
this.notifications.success(options.successNotification || `${model.name ? modelName + " '" + model.name + "'" : "'" + modelName + "'"} has been deleted.`);
if (typeof options.onSuccess === 'function') {
options.onSuccess(model);
}
})
.catch((error) => {
this.notifications.serverError(error);
if (typeof options.onError === 'function') {
options.onError(error, model);
}
})
.finally(() => {
if (typeof options.callback === 'function') {
options.callback(model);
}
});
},
...options,
});
}
/**
* Generic deletion modal with options
*
* @param {Array} selected an array of selected models for deletion
* @param {Object} options
* @void
*/
bulkDelete(selected, options = {}) {
if (!isArray(selected) || selected.length === 0) {
return;
}
const firstModel = first(selected);
const modelName = getModelName(firstModel, get(options, 'modelName'), { humanize: true, capitalizeWords: true });
// make sure all are the same type
selected = selected.filter((m) => getModelName(m) === getModelName(firstModel));
return this.bulkAction('delete', selected, {
acceptButtonScheme: 'danger',
acceptButtonIcon: 'trash',
actionPath: `${dasherize(pluralize(modelName))}/bulk-delete`,
actionMethod: `DELETE`,
modelName,
...options,
});
}
/**
* Generic bulk action on multiple models modal with options
*
* @param {Array} selected an array of selected models for deletion
* @param {Object} options
* @void
*/
bulkAction(verb, selected, options = {}) {
if (!isArray(selected) || selected.length === 0) {
return;
}
const firstModel = first(selected);
const modelName = getModelName(firstModel, get(options, 'modelName'), { humanize: true, capitalizeWords: true });
const count = selected.length;
const actionMethod = (typeof options.actionMethod === 'string' ? options.actionMethod : `POST`).toLowerCase();
const fetchParams = getWithDefault(options, 'fetchParams', {});
const fetchOptions = getWithDefault(options, 'fetchOptions', {});
this.modalsManager.show('modals/bulk-action-model', {
title: `Bulk ${verb} ${pluralize(modelName)}`,
acceptButtonText: humanize(verb),
args: ['selected'],
modelNamePath: 'name',
verb,
selected,
count,
modelName,
remove: (model) => {
selected.removeObject(model);
this.modalsManager.setOption('selected', selected);
},
confirm: (modal) => {
const selected = modal.getOption('selected');
if (typeof options.onConfirm === 'function') {
options.onConfirm(selected);
}
modal.startLoading();
return this.fetch[actionMethod](
options.actionPath,
{
ids: selected.map((model) => model.id),
...fetchParams,
},
fetchOptions
)
.then((response) => {
this.notifications.success(response.message ?? options.successNotification ?? `${count} ${pluralize(modelName, count)} were updated successfully.`);
if (typeof options.onSuccess === 'function') {
options.onSuccess(selected);
}
})
.catch((error) => {
this.notifications.serverError(error);
if (typeof options.onError === 'function') {
options.onError(error, selected);
}
})
.finally(() => {
if (typeof options.callback === 'function') {
options.callback(selected);
}
});
},
...options,
});
}
/**
* Toggles dialog to export resource data
*
* @void
*/
export(modelName, options = {}) {
// always lowercase modelname
modelName = modelName.toLowerCase();
// 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';
modal.startLoading();
return this.fetch
.download(
`${modelEndpoint}/export`,
{
format,
...exportParams,
},
{
method: 'POST',
fileName: `${modelEndpoint}-${formatDate(new Date(), 'yyyy-MM-dd-HH:mm')}.${format}`,
}
)
.then(() => {
later(
this,
() => {
return done();
},
600
);
})
.catch((error) => {
modal.stopLoading();
this.notifications.serverError(error, 'Unable to download API credentials export.');
});
},
...options,
});
}
}