jws-frontend/src/stores/utils/index.ts

573 lines
16 KiB
TypeScript

import {
Dialog,
QSelect,
Notify,
QNotifyCreateOptions,
useQuasar,
} from 'quasar';
import { Ref, ref } from 'vue';
import axios, { AxiosInstance, AxiosProgressEvent } from 'axios';
import { ComposerTranslation } from 'vue-i18n';
import arr from 'src/utils/arr';
import { getRole } from 'src/services/keycloak';
import GlobalDialog from 'components/GlobalDialog.vue';
import DialogDuplicateData from 'components/DialogDuplicateData.vue';
import useOptionStore from '../options';
import { CustomerBranch } from '../customer/types';
import { CustomerBranchRelation } from '../quotations';
export const baseUrl = import.meta.env.VITE_API_BASE_URL;
export function dialog(opts: {
title: string;
message: string;
color?: string;
icon?: string;
persistent?: boolean;
actionText?: string;
cancelText?: string;
enablei18n?: boolean;
action?: (...args: unknown[]) => unknown;
cancel?: (...args: unknown[]) => unknown;
}) {
Dialog.create({
component: GlobalDialog,
componentProps: opts,
});
}
export function dialogCheckData(opts: {
checkData: (...args: unknown[]) => {
oldData: { nameField: string; value: string }[];
newData: { nameField: string; value: string }[];
};
action?: (...args: unknown[]) => unknown;
cancel?: (...args: unknown[]) => unknown;
}) {
Dialog.create({
component: DialogDuplicateData,
componentProps: opts,
});
}
export function dialogWarningClose(
t: ComposerTranslation,
opts: {
message?: string;
actionText?: string;
action?: (...args: unknown[]) => unknown;
cancel?: (...args: unknown[]) => unknown;
},
) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('form.warning.title'),
actionText: opts.actionText || t('dialog.action.close'),
message: opts.message || t('dialog.message.warningClose'),
action: async () => {
if (opts.action) opts.action();
},
cancel: () => {
if (opts.cancel) opts.cancel();
},
});
}
export function notify(
type: 'create',
message?: string,
opts?: QNotifyCreateOptions | string,
) {
if (type === 'create') {
Notify.create({
icon: 'mdi-check-circle',
iconColor: 'white',
iconSize: '42px',
color: 'positive',
message: message,
position: 'bottom',
timeout: 1000,
group: false,
actions: [
{
icon: 'mdi-close',
color: 'white',
round: true,
handler: () => {},
},
],
});
}
if (opts) {
Notify.create(opts);
}
}
export function moveItemUp<T extends unknown[]>(items: T, index: number) {
arr.move(items, index, -1);
}
export function moveItemDown<T extends unknown[]>(items: T, index: number) {
arr.move(items, index, 1);
}
export function deleteItem(items: unknown[], index: number) {
if (!items) return;
if (index >= 0 && index < items.length) {
items.splice(index, 1);
}
}
export function formatNumberDecimal(num: number, point: number = 2): string {
return num.toLocaleString('eng', {
minimumFractionDigits: point,
maximumFractionDigits: point,
});
}
export function selectFilterOptionRefMod(
source: Ref<Record<string, unknown>[]>,
destination: Ref<Record<string, unknown>[]>,
filterField?: string,
) {
destination.value = source.value;
const filter = ((value, update) => {
if (value === '') update(() => (destination.value = source.value));
else
update(
() => {
destination.value = source.value.filter((v) => {
const label = v[filterField || 'label'];
if (!label) return;
if (typeof label !== 'string') {
throw new Error('Label must be of type string.');
}
return (
label.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1
);
});
},
(ref) => {
if (value !== '' && destination.value.length > 0) {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
},
);
}) satisfies QSelect['onFilter'];
return filter;
}
export function selectOptionFilter(
list: Record<string, unknown>[],
filterField?: string,
prepare?: (
...args: unknown[]
) => Record<string, unknown>[] | Promise<Record<string, unknown>[]>,
) {
const options = ref<typeof list>([]);
(async () => {
const data = await prepare?.();
if (data) options.value = list = data;
})();
const filter = ((value, update) => {
if (value === '') update(() => (options.value = list));
else
update(() => {
options.value = list.filter((v) => {
const label = v[filterField || 'label'];
if (typeof label !== 'string') {
throw new Error('Label must be of type string.');
}
return (
label.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1
);
});
});
}) satisfies QSelect['onFilter'];
return { options, filter };
}
export function checkTabBeforeAdd(data: unknown[], except?: string[]) {
if (!data) return;
const lastData = data[data.length - 1];
if (typeof lastData !== 'object') return false;
let canAdd = false;
for (const [key, val] of Object.entries(
lastData as Record<string, unknown>,
)) {
if (except?.includes(key)) continue;
canAdd = val !== '' && val !== null;
if (canAdd) break;
}
return canAdd;
}
export function isRoleInclude(role2check: string[]): boolean {
const roles = getRole() ?? [];
const isIncluded = role2check.some((r) => roles.includes(r));
return isIncluded;
}
export function resetScrollBar(elementId: string) {
const element = document.getElementById(elementId);
if (element) {
element.scrollTop = 0;
}
}
export function manageAttachment(
api: AxiosInstance,
base: string,
option?: { onUploadProgress?: (e: AxiosProgressEvent) => void },
) {
return {
listAttachment: async (opts: { parentId: string }) => {
const res = await api.get<string[]>(
`/${base}/${opts.parentId}/attachment`,
);
if (res.status >= 400) return false;
return res.data;
},
headAttachment: async (opts: { parentId: string; fileId: string }) => {
const url = `/${base}/${opts.parentId}/attachment/${opts.fileId}`;
const res = await api.head(url);
if (res.status < 400) return res.headers;
return false;
},
getAttachment: async (opts: {
parentId: string;
name: string;
download?: boolean;
}) => {
const url = `/${base}/${opts.parentId}/attachment/${opts.name}`;
const res = await api.get<string>(url);
if (opts.download) {
fetch(res.data)
.then(async (res) => await res.blob())
.then((blob) => {
const a = document.createElement('a');
a.download = opts.name;
a.href = window.URL.createObjectURL(blob);
a.click();
a.remove();
});
}
return res.data;
},
putAttachment: async (opts: {
parentId: string;
name: string;
file: File;
onUploadProgress?: (e: AxiosProgressEvent) => void;
abortController?: AbortController;
}) => {
const urlRes = await api.put(
`/${base}/${opts.parentId}/attachment/${opts.name}`,
);
if (urlRes.status >= 400) return false;
// NOTE: Must use axios instance or else CORS error.
const uploadRes = await axios.put(urlRes.data, opts.file, {
headers: { 'Content-Type': opts.file.type },
onUploadProgress: opts.onUploadProgress
? opts.onUploadProgress
: option?.onUploadProgress
? option.onUploadProgress
: (e) => console.log(e),
signal: opts.abortController?.signal,
});
if (uploadRes.status >= 400) return false;
return true;
},
delAttachment: async (opts: { parentId: string; name: string }) => {
const res = await api.delete(
`/${base}/${opts.parentId}/attachment/${opts.name}`,
);
if (res.status < 400) return true;
return false;
},
};
}
export function manageFile<T extends string>(
api: AxiosInstance,
base: string,
option?: { onUploadProgress?: (e: AxiosProgressEvent) => void },
) {
return {
listFile: async (opts: { group: T; parentId: string }) => {
const res = await api.get<string[]>(
`/${base}/${opts.parentId}/file-${opts.group}`,
);
if (res.status >= 400) return false;
return res.data;
},
headFile: async (opts: { group: T; parentId: string; fileId: string }) => {
const url = `/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`;
const res = await api.head(url);
if (res.status < 400) return res.headers;
return false;
},
getFile: async (opts: {
group: T;
parentId: string;
fileId: string;
download?: boolean;
}) => {
const url = `/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`;
const res = await api.get<string>(url);
if (opts.download) {
fetch(res.data)
.then(async (res) => await res.blob())
.then((blob) => {
const a = document.createElement('a');
a.download = opts.fileId;
a.href = window.URL.createObjectURL(blob);
a.click();
a.remove();
});
}
return res.data;
},
putFile: async (opts: {
group: T;
parentId: string;
fileId: string;
file: File;
onUploadProgress?: (e: AxiosProgressEvent) => void;
abortController?: AbortController;
}) => {
const res = await api.put(
`/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
opts.file,
{
headers: { 'Content-Type': opts.file.type },
onUploadProgress: opts.onUploadProgress
? opts.onUploadProgress
: option?.onUploadProgress
? option.onUploadProgress
: (e) => console.log(e),
signal: opts.abortController?.signal,
},
);
if (res.status < 400) return true;
return false;
},
delFile: async (opts: { group: T; parentId: string; fileId: string }) => {
const res = await api.delete(
`/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
);
if (res.status < 400) return true;
return false;
},
};
}
export function manageMeta<T extends string>(api: AxiosInstance, base: string) {
return {
postMeta: async (opts: {
group: T;
parentId: string;
meta: any;
file?: File;
}) => {
const url = `${base}/${opts.parentId}/${opts.group}`;
const res = await api.post<typeof opts.meta>(url, opts.meta);
if (res.status < 400) {
if (opts.file !== undefined) {
await manageFile(api, base).putFile({
parentId: opts.parentId,
group: opts.group,
fileId: res.data.id,
file: opts.file,
});
}
return res.data;
}
},
getMetaList: async (opts: { group: T; parentId: string }) => {
const url = `${base}/${opts.parentId}/${opts.group}`;
const res = await api.get(url);
return res.data;
},
getMeta: async (opts: { group: T; parentId: string; metaId: string }) => {
const url = `${base}/${opts.parentId}/${opts.group}/${opts.metaId}`;
const res = await api.get<string>(url);
return res.data;
},
putMeta: async (opts: {
group: T;
parentId: string;
metaId: string;
meta: any;
file?: File;
}) => {
const res = await api.put<typeof opts.meta>(
`${base}/${opts.parentId}/${opts.group}/${opts.metaId}`,
opts.meta,
);
if (res.status < 400) {
if (opts.file !== undefined)
await manageFile(api, base).putFile({
parentId: opts.parentId,
group: opts.group,
fileId: res.data.id,
file: opts.file,
});
return res.data;
}
return false;
},
delMeta: async (opts: { group: T; parentId: string; metaId: string }) => {
const res = await api.delete(
`${base}/${opts.parentId}/${opts.group}/${opts.metaId}`,
);
if (res.status < 400) return true;
return false;
},
};
}
export async function waitAll<T extends Promise<any>[]>(arr: T) {
return await Promise.all(arr);
}
export function commaInput(
text: string,
type: 'string' | 'number' = 'number',
): string {
if (typeof text !== 'string') return '0';
if (!text) return '0';
const num = Number(text.replace(/,/gi, ''));
const parts = num.toString().split('.');
const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const decimalPart = parts[1]?.slice(0, 2) || (type === 'number' && '00');
if (integerPart === 'NaN') return '0';
return integerPart + (decimalPart ? `.${decimalPart}` : '');
}
export function convertFileSize(size: number): string {
if (size === undefined) return 'Unknow size';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let i = 0;
let sizeNumber = typeof size === 'string' ? parseFloat(size) : size;
while (sizeNumber >= 1024 && i++ < units.length - 1) {
sizeNumber /= 1024;
}
return sizeNumber.toFixed(2) + ' ' + units[i];
}
export async function getAttachmentHead(api: AxiosInstance, url: string) {
const res = await api.head<string>(`${url}`);
if (res) return res.headers;
}
export function calculateDaysUntilExpire(expireDate: Date): number {
const today = new Date();
const expire = new Date(expireDate);
const diffInTime = expire.getTime() - today.getTime();
const diffInDays = Math.ceil(diffInTime / (1000 * 60 * 60 * 24));
return diffInDays;
}
export function formatNumberWithCommas(value: string) {
const [integer, decimal] = value.split('.');
const integerWithCommas = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimal ? `${integerWithCommas}.${decimal}` : integerWithCommas;
}
export function createDataRefBase<T>(
defaultPage = 1,
defaultPageMax = 1,
defaultPageSize = 30,
) {
return {
data: ref<T[]>(),
page: ref<number>(defaultPage),
pageMax: ref<number>(defaultPageMax),
pageSize: ref<number>(defaultPageSize),
};
}
export function changeMode(mode: string) {
const $q = useQuasar();
if (mode === 'light') {
localStorage.setItem('currentTheme', 'light');
$q.dark.set(false);
return;
}
if (mode === 'dark') {
localStorage.setItem('currentTheme', 'dark');
$q.dark.set(true);
return;
}
if (mode === 'baseOnDevice') {
localStorage.setItem('currentTheme', 'baseOnDevice');
if (
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
) {
$q.dark.set(true);
} else {
$q.dark.set(false);
}
return;
}
}
export function capitalizeFirstLetter(val: string) {
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
}
export function getCustomerName(
record: CustomerBranch | CustomerBranchRelation,
opts?: {
locale?: string;
noCode?: boolean;
},
) {
const customer = record;
return (
{
['CORP']: {
['eng']: customer.registerNameEN,
['tha']: customer.registerName,
}[opts?.locale || 'eng'],
['PERS']:
{
['eng']: `${useOptionStore().mapOption(customer?.namePrefix || '')} ${customer?.firstNameEN} ${customer?.lastNameEN}`,
['tha']: `${useOptionStore().mapOption(customer?.namePrefix || '')} ${customer?.firstName} ${customer?.lastName}`,
}[opts?.locale || 'eng'] || '-',
}[customer.customer.customerType] +
(opts?.noCode ? '' : ' ' + `(${customer.code})`)
);
}