/**
* @desc A global mixin object containing common API calls
* @module GlobalApiMixin
*/
import { OfflineApiMixin } from '@/specific/mixins/offline-api';
export const GlobalApiMixin = {
mixins: [OfflineApiMixin],
methods: {
/**
* Injects the properties defined in tableapi_props.
* @param {string} tableAPI - The name of the table API.
* @returns {Promise<void>} - A promise that resolves when the properties are injected.
*/
async injectTableAPIProps(tableAPI) {
let p, props;
if (!this.$store.tableAPIProps[tableAPI]) {
p = await this.get("CommonAnon/GetTableAPIProps/" + tableAPI);
if (p == null || p == "") {
props = {};
} else {
props = p.props;
this.deepMerge(p.colAtts, p.props);
}
this.$store.tableAPIProps[tableAPI] = props;
}
this.deepMerge(this.$store.tableAPIProps[tableAPI], this);
},
/**
* Makes an API request using Axios.
* @param {AxiosInstance} axios - The Axios instance to use for the request.
* @param {string} url - The URL to make the request to.
* @param {object} options - The options to pass to the Axios request.
* @param {boolean} cache - Indicates whether to cache the response.
* @returns {Promise<any>} - A promise that resolves to the response data.
*/
async api(axios, url, options, cache, params = true) {
this.$store.working = true;
if (axios == this.axios.API.get && options && params) {
options = { params: options };
}
let data = await this.apiCache(axios, url, options, cache);
this.$store.working = false;
return data;
},
/**
* Makes an API request using Axios.
* @param {AxiosInstance} axios - The Axios instance to use for the request.
* @param {string} url - The URL to make the request to.
* @param {object} options - The options to pass to the Axios request.
* @param {boolean} cache - Indicates whether to cache the response.
* @returns {Promise<any>} - A promise that resolves to the response data.
*/
async apiCache(axios, url, options, cache){
const path = url.split('/');
const isOfflineMode = !this.$store.isOnline && cache && this.$store.pwa;
// Handle offline mode
if (isOfflineMode) {
return await this.handleOfflineRequest(path, options);
}
// Online mode: make the API call
// const response = await this.makeApiCall(axios, url, options);
let response = await axios(url, options);
response = response ? response.data : null;
// Cache data if needed
if (this.$store.pwa && cache) {
await this.cacheOnlineResponse(url, path, options, response, axios);
}
return response;
},
/**
* Fetches data from an API and stores it in the store if it is not already present.
* @param {string} name - The name of the store property to store the data.
* @param {object} axios - The Axios instance used for making API requests.
* @param {string} url - The URL of the API endpoint.
* @param {object} options - The options object to be passed to the Axios request.
* @param {boolean} cache - Indicates whether the response should be cached.
* @returns {Promise<void>} - A Promise that resolves when the API request is complete.
*/
async apiArray(name, axios, url, options, cache) {
if (this.$store[name].length == 0) {
let r = await this.api(axios, url, options, cache);
this.$store[name].push(...r); // to keep references to orig array
}
},
/**
* Fetches data from an API and stores it in the store if it doesn't already exist.
* @param {string} name - The name of the store property to store the fetched data.
* @param {object} axios - The Axios instance used for making API requests.
* @param {string} url - The URL of the API endpoint.
* @param {string} key - The key to use for mapping the fetched data to an object.
* @param {string} value - The value to use for mapping the fetched data to an object.
* @param {object} options - Additional options for the API request.
* @param {boolean} cache - Indicates whether to cache the fetched data.
* @returns {Promise<void>} - A Promise that resolves when the API request is complete.
*/
async apiDict(name, axios, url, key, value, options, cache) {
if (Object.keys(this.$store[name]).length == 0) {
let r = await this.api(axios, url, options, cache);
let o = this.arrayToObject(r, key, value);
this.copyObject(o, this.$store[name]);
}
},
/**
* Fetches data from an API and populates multiple dictionaries in the store.
* @param {Array} names - The names of the dictionaries to populate.
* @param {Object} axios - The Axios instance for making API requests.
* @param {string} url - The URL of the API endpoint.
* @param {string} key - The key to use for mapping objects in the response to the dictionaries.
* @param {string} value - The value to use for mapping objects in the response to the dictionaries.
* @param {Object} options - Additional options for the API request.
* @param {boolean} cache - Indicates whether to cache the API response.
* @returns {Promise<void>} - A promise that resolves when the dictionaries are populated.
*/
async apiDictMulti(names, axios, url, key, value, options, cache) {
if (Object.keys(this.$store[names[0]]).length > 0) return;
let r = await this.api(axios, url, options, cache);
if (r) {
for (let name of names) {
let o = this.arrayToObject(r[name], key, value);
this.copyObject(o, this.$store[name]);
}
}
},
/**
* Calls the API using the provided axios instance, URL, options, and cache.
* Updates the specified store properties with the response data.
* @param {string[]} names - The names of the store properties to update.
* @param {object} axios - The axios instance to use for the API call.
* @param {string} url - The URL to make the API call to.
* @param {object} options - The options to pass to the API call.
* @param {boolean} cache - Indicates whether to cache the API response.
* @returns {Promise<void>} - A promise that resolves when the API call is complete.
*/
async apiMultiForce(names, axios, url, options, cache) {
let r = await this.api(axios, url, options, cache);
if (r) {
for (let name of names) {
if (r[name]) {
if (Array.isArray(this.$store[name])) {
this.$store[name].length = 0;
this.$store[name].push(...r[name]);
} else {
if (r[name].length > 0) {
this.copyObject(r[name][0], this.$store[name]);
}
}
}
}
}
},
/**
* Fetches data from an API and updates an array in the store.
* @param {string} name - The name of the array in the store.
* @param {object} axios - The Axios instance used for making API requests.
* @param {string} url - The URL of the API endpoint.
* @param {object} options - The options object for the API request.
* @param {boolean} cache - Indicates whether the response should be cached.
* @returns {Promise<void>} - A promise that resolves when the API request is complete.
*/
async apiArrayForce(name, axios, url, options, cache) {
let r = await this.api(axios, url, options, cache);
if (r) {
this.$store[name].length = 0;
this.$store[name].push(...r);
}
},
/**
* Fetches the API object from the store if it is not already available.
* @param {string} name - The name of the API object.
* @param {object} axios - The Axios instance for making HTTP requests.
* @param {string} url - The URL for fetching the API object.
* @param {object} options - The options for the API request.
* @param {boolean} cache - Indicates whether to cache the API response.
* @returns {Promise<void>} - A Promise that resolves when the API object is fetched.
*/
async apiObject(name, axios, url, options, cache) {
if (Object.keys(this.$store[name]).length == 0) await this.apiObjectForce(name, axios, url, options, cache);
},
/**
* Updates the specified object in the store with the response from an API call.
* @param {string} name - The name of the object in the store.
* @param {object} axios - The axios instance used for the API call.
* @param {string} url - The URL for the API call.
* @param {object} options - The options for the API call.
* @param {boolean} cache - Indicates whether to cache the API response.
* @returns {Promise<void>} - A promise that resolves when the API call is complete.
*/
async apiObjectForce(name, axios, url, options, cache) {
let r = await this.api(axios, url, options, cache);
if (r) {
for (let key of Object.keys(r)) {
this.$set(this.$store[name], key, r[key]);
}
}
},
/**
* Executes an API request using the provided axios instance.
* @param {Object} axios - The axios instance to use for the API request.
* @param {string} url - The URL of the API endpoint.
* @param {Object} options - The options for the API request.
* @param {boolean} cache - Indicates whether to cache the API response.
* @returns {Promise} - A promise that resolves to the API response.
*/
async apiExec(axios, url, options, cache) {
return await this.api(axios, url, options, cache);
},
/**
* Makes a GET request to the specified URL.
* @param {string} url - The URL to make the GET request to.
* @param {object} options - The options to be passed as query parameters.
* @param {boolean} cache - Indicates whether the response should be cached.
* @returns {Promise} - A promise that resolves to the response data.
*/
async get(url, options, cache) {
// if (options) {
// return await this.api(this.axios.API.get, url, { params: options }, cache);
// } else {
return await this.api(this.axios.API.get, url, options, cache);
// }
},
/**
* Sends a POST request to the specified URL.
* @param {string} url - The URL to send the request to.
* @param {object} options - The options for the request.
* @param {boolean} cache - Indicates whether to cache the response.
* @returns {Promise} - A promise that resolves with the response data.
*/
async post(url, options, cache) {
return await this.api(this.axios.API.post, url, options, cache);
},
/**
* Sends a PUT request to the specified URL.
* @param {string} url - The URL to send the request to.
* @param {object} options - The options for the request.
* @returns {Promise} - A promise that resolves with the response data.
*/
async put(url, options, cache) {
return await this.api(this.axios.API.put, url, options);
},
/**
* Sends a DELETE request to the specified URL.
* @param {string} url - The URL to send the request to.
* @param {object} options - The options for the request.
* @returns {Promise} - A promise that resolves with the response data.
*/
async delete(url, options, cache) {
return await this.api(this.axios.API.delete, url, options, cache);
},
/**
* Retrieves an image from the specified URL.
* @param {string} url - The URL of the image to retrieve.
* @returns {Promise<string|null>} A Promise that resolves to the URL of the retrieved image, or null if the retrieval fails.
*/
async getImage(url, cache = false) {
let blob = await this.api(this.axios.API.get, url, { responseType: 'blob' }, cache, false);
// let response = await this.axios.API.get(url, { responseType: 'blob' });
// if (response) {
// const blob = new Blob([response.data], { type: response.headers['content-type'] });
if (blob) {
let o = URL.createObjectURL(blob);
this.$store.working = false;
return o;
}
// }
return null;
},
/**
* Checks if the response is OK.
* @param {Object} response - The response object.
* @returns {boolean} - Returns true if the response is OK, otherwise false.
*/
responseOk(response) {
return response?.error == undefined;
},
/**
* Gets routes available to the user
* @returns {Promise<void>}
*/
async getRoutes() {
this.$store.routes = await this.get("Auth/GetRoutes", null, true);
},
/**
* Gets icons
* @returns {Promise<void>}
*/
async getIcons() {
if (this.$store.icons.length == 0) this.$store.icons = await this.get("CommonUser/GetIcons");
},
// async uploadFiles(fileList, url, params) {
// let formData = new FormData();
// for (let i = 0; i < fileList.length; i++) {
// let file = fileList[i];
// formData.append("files", file, file.name);
// }
// formData.append("params", JSON.stringify(params));
// return await this.post(url, formData);
// }
/**
* Loads a lookup table from the API or local storage.
* @param {*} lookup
*/
async loadLookup(lookup, cache, search) {
if (lookup.API) {
lookup.options = await this.get(lookup.API, search, cache);
} else if (lookup.refTable) {
lookup.options = await this.get("Table/GetLookup/" + lookup.refTable, search, cache);
}
if (lookup.options.length > 0) {
lookup.optionValue = Object.keys(lookup.options[0])[0];
lookup.optionLabel = Object.keys(lookup.options[0])[1];
} else {
lookup.optionValue = "id";
lookup.optionLabel = "name";
}
if (!search) lookup.loaded = true;
},
/**
* Creates an empty row object with default values based on the given columns.
*
* @param {Array} columns - An array of column objects.
* @returns {Object} - An empty row object with default values.
*/
async createEmptyRow(columns) {
let obj = {};
for (let col of columns) {
if (col.type == 'json') {
obj[col.name] = "{}";
} else if (col.type == 'boolean') {
obj[col.name] = false;
} else {
obj[col.name] = null;
}
if (col.defaultPrevious || col.default) {
if (col.defaultPrevious) {
obj[col.name] = this.$q.localStorage.getItem("default_" + col.name) ?? obj[col.name];
} else {
obj[col.name] = col.default;
}
if (col.lookup) {
await this.loadLookup(col.lookup);
let displayValue = this.findLookupValue(obj[col.name], col.lookup);
obj[col.name + '_val'] = displayValue;
}
if (col.type == 'boolean' || col.type == 'integer') {
obj[col.name] = JSON.parse(obj[col.name]);
}
}
}
return obj;
},
/**
* Returns the type of the given value.
* @param {*} value - The value to determine the type of.
* @returns {string} The type of the value. Possible values are "boolean", "json", or "character varying".
*/
getValueType(value) {
if (typeof value === "boolean") {
return "boolean";
} else if (value == null) {
return "character varying";
} else if (typeof value === "object") {
return "json";
} else {
return "character varying";
}
},
/**
* Prepare row for saving : converts JSON strings to objects, blanks to nulls.
*
* @param {Object} row - The row object.
* @param {Object[]} columns - The array of column objects.
*/
prepareRow(row, columns) {
for (let col of columns) {
if (col.type == 'json') {
row[col.name] = JSON.parse(row[col.name]);
} else if (col.type == 'boolean') {
row[col.name] = row[col.name] ? true : false
} else if (row[col.name] == "") {
row[col.name] = null;
}
}
},
/**
* Finds display value for a lookup value in lookup table
* @param {*} value
* @param {*} tableName
* @returns display value for the lookup value in the lookup table
*/
findLookupValue(value, lookup) {
if (value == null) {
return "";
} else {
return lookup.options.find(v => v[lookup.optionValue] == value)[lookup.optionLabel];
}
},
/**
* Swaps the id and value columns for lookup fields.
* @param {*} columns
*/
swapIdAndValColumns(columns) {
let swapped = [];
for (let i = 0; i < columns.length; i++) {
//if (columns[i].name.endsWith('_id')) {
if (columns[i].lookup && columns[i].name.endsWith('_id')) {
let name = columns[i].name.slice(0, -3);
if (!swapped.includes(name)) {
for (let j = 0; j < columns.length; j++) {
if (columns[j].name == name + '_id_val') {
swapped.push(name);
let temp = columns[i];
columns[i] = columns[j];
columns[j] = temp;
break;
}
}
}
}
}
},
/**
* Initializes the properties for custom component in popup.
* @param {string} action - The action to be performed.
* @param {Object} rowToPass - The row object to be passed to the popup.
* @param {Array} rows - The array of rows in the table.
*/
async initPopup(action, rowToPass, rows, columns, editingRowIndex) {
//let popup = action.popup ?? 'default';
let popup = this.$store.popupLevel;
if (popup == this.$store.maxPopups-1) {
await this.showError(this.$t('Max popups reached'));
return;
}
popup = popup.toString();
this.$store.newPopups[popup].props = this.deepClone(action);
this.$store.newPopups[popup].props.rows = rows;
this.$store.newPopups[popup].props.parent = this;
this.$store.newPopups[popup].props.row = rowToPass;
this.$store.newPopups[popup].props.columns = columns;
this.$store.newPopups[popup].props.editingRow = rowToPass;
this.$store.newPopups[popup].props.editingRowIndex = editingRowIndex;
this.$store.newPopups[popup].component = action.component;
this.$store.newPopups[popup].show = true;
return this.$store.popupLevel++;
},
closePopup() {
console.log("closePopup", this.$store.popupLevel);
if (this.$store.popupLevel > 0) {
this.$store.popupLevel--;
this.$store.newPopups[this.$store.popupLevel].show = false;
}
},
/**
* Prepares and activates a route based on the provided action, row, and rows.
*
* @param {Object} action - The action object containing route information.
* @param {Object} row - The row object.
* @param {Array} rows - The array of rows.
* @returns {void}
*/
prepareRoute(action, rowToPass, rows, columns, editingRowIndex) {
// activate a route
let routerRoute = this.$store.routes.find((item) => item.path == action.route);
let route = {};
if (!routerRoute) { // this is a custom route, not in routes table
route = { name: action.route, component: action.component, path: action.route };
route.props = this.deepClone(action);
} else {
route = this.deepClone(routerRoute);
}
if (rowToPass) this.replaceVariables(route.props, rowToPass);
route.props.row = rowToPass;
route.props.rows = rows;
route.props.columns = columns;
route.props.editingRow = rowToPass;
route.props.editingRowIndex = editingRowIndex;
route.props.backButton = true;
this.activateRoute(route);
},
invokeComponent(action) {
if (action.route) {
this.prepareRoute(action, action.row, action.rows, action.columns, action.editingRowIndex);
} else {
this.initPopup(action, action.row, action.rows, action.columns, action.editingRowIndex);
}
},
async loadNews() {
this.$store.news = await this.get(`CommonAnon/GetNews/0/10`, null, true);
},
getProps(path) {
let ret = this.$store.routes.find((r) => r.path === path);
if (ret) ret = ret.props;
else ret = {};
return ret;
}
}
}