salix-front/src/composables/useArrayData.js

215 lines
5.9 KiB
JavaScript

import { onMounted, ref, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import axios from 'axios';
import { useArrayDataStore } from 'stores/useArrayDataStore';
import { buildFilter } from 'filters/filterPanel';
const arrayDataStore = useArrayDataStore();
export function useArrayData(key, userOptions) {
if (!key) throw new Error('ArrayData: A key is required to use this composable');
if (!arrayDataStore.get(key)) {
arrayDataStore.set(key);
}
const store = arrayDataStore.get(key);
const hasMoreData = ref(false);
const router = useRouter();
const route = useRoute();
let canceller = null;
const page = ref(1);
onMounted(() => {
setOptions();
const query = route.query;
if (query.params) {
store.userParams = JSON.parse(query.params);
}
});
if (key && userOptions) {
setOptions();
}
function setOptions() {
const allowedOptions = [
'url',
'filter',
'where',
'order',
'limit',
'skip',
'userParams',
'userFilter',
'exprBuilder',
];
if (typeof userOptions === 'object') {
for (const option in userOptions) {
const isEmpty = userOptions[option] == null || userOptions[option] === '';
if (isEmpty || !allowedOptions.includes(option)) continue;
if (Object.prototype.hasOwnProperty.call(store, option)) {
store[option] = userOptions[option];
}
}
}
}
async function fetch({ append = false, updateRouter = true }) {
if (!store.url) return;
cancelRequest();
canceller = new AbortController();
const filter = {
order: store.order,
limit: store.limit,
skip: store.skip,
};
let exprFilter;
let userParams = { ...store.userParams };
if (store?.exprBuilder) {
const where = buildFilter(userParams, (param, value) => {
const res = store.exprBuilder(param, value);
if (res) delete userParams[param];
return res;
});
exprFilter = where ? { where } : null;
}
Object.assign(filter, store.userFilter, exprFilter);
Object.assign(store.filter, filter);
const params = {
filter: JSON.stringify(store.filter),
};
Object.assign(params, userParams);
store.isLoading = true;
store.currentFilter = params;
const response = await axios.get(store.url, {
signal: canceller.signal,
params,
});
const { limit } = filter;
hasMoreData.value = response.data.length === limit;
if (append) {
if (!store.data) store.data = [];
for (const row of response.data) store.data.push(row);
} else {
store.data = response.data;
if (!document.querySelectorAll('[role="dialog"]'))
updateRouter && updateStateParams();
}
store.isLoading = false;
canceller = null;
return response;
}
function destroy() {
if (arrayDataStore.get(key)) {
arrayDataStore.clear(key);
}
}
function cancelRequest() {
if (canceller) {
canceller.abort();
canceller = null;
}
}
async function applyFilter({ filter, params }) {
if (filter) store.userFilter = filter;
store.filter = {};
if (params) store.userParams = Object.assign({}, params);
await fetch({ append: false });
}
async function addFilter({ filter, params }) {
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
let userParams = Object.assign({}, store.userParams, params);
userParams = sanitizerParams(userParams, store?.exprBuilder);
store.userParams = userParams;
store.skip = 0;
store.filter.skip = 0;
await fetch({ append: false });
return { filter, params };
}
function sanitizerParams(params, exprBuilder) {
for (const param in params) {
if (params[param] === '' || params[param] === null) {
delete store.userParams[param];
delete params[param];
if (store.filter?.where) {
delete store.filter.where[
Object.keys(exprBuilder ? exprBuilder(param) : param)[0]
];
if (Object.keys(store.filter.where).length === 0) {
delete store.filter.where;
}
}
}
}
return params;
}
async function loadMore() {
if (!hasMoreData.value) return;
store.skip = store.limit * page.value;
page.value += 1;
await fetch({ append: true });
}
async function refresh() {
if (Object.values(store.userParams).length) await fetch({ append: false });
}
function updateStateParams() {
const query = {};
if (store.order) query.order = store.order;
if (store.limit) query.limit = store.limit;
if (store.skip) query.skip = store.skip;
if (store.userParams && Object.keys(store.userParams).length !== 0)
query.params = JSON.stringify(store.userParams);
if (router)
router.replace({
path: route.path,
query: query,
});
}
const totalRows = computed(() => (store.data && store.data.length) || 0);
const isLoading = computed(() => store.isLoading || false);
return {
fetch,
applyFilter,
addFilter,
refresh,
destroy,
loadMore,
store,
hasMoreData,
totalRows,
updateStateParams,
isLoading,
};
}