feat: setup apps

main
Firman Ramdhani 2024-10-01 14:56:15 +07:00
parent cb8b4959ac
commit 94d5d60674
114 changed files with 7959 additions and 8 deletions

4
.gitignore vendored
View File

@ -24,4 +24,6 @@ dist-ssr
*.sw?
.env
yarn.lock
yarn.lock
tsconfig.app.tsbuildinfo
tsconfig.node.tsbuildinfo

View File

@ -22,6 +22,7 @@ export default tseslint.config(
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-duplicate-enum-values': 'off',
'@typescript-eslint/no-unused-expressions': 'warn',
},
},
);

147
package-lock.json generated
View File

@ -22,19 +22,23 @@
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-intl": "^6.7.0",
"react-router-dom": "^6.26.2",
"recoil": "^0.7.7"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/crypto-js": "^4.2.2",
"@types/lodash": "^4.17.9",
"@types/node": "^22.7.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"eslint-plugin-react-refresh": "^0.4.12",
"globals": "^15.9.0",
"less": "^4.2.0",
"postcss": "^8.4.47",
@ -917,6 +921,18 @@
"node": ">=14"
}
},
"node_modules/@pkgr/core": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
"integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@rc-component/async-validator": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz",
@ -1057,6 +1073,14 @@
"react-dom": ">=16.9.0"
}
},
"node_modules/@remix-run/router": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
"integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.23.0.tgz",
@ -1511,6 +1535,15 @@
"integrity": "sha512-w9iWudx1XWOHW5lQRS9iKpK/XuRhnN+0T7HvdCCd802FYkT1AMTnxndJHGrNJwRoRHkslGr4S29tjm1cT7x/7w==",
"dev": true
},
"node_modules/@types/node": {
"version": "22.7.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
"integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
"dev": true,
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.13",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
@ -2516,6 +2549,48 @@
}
}
},
"node_modules/eslint-config-prettier": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
"dev": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
"integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.9.1"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint-plugin-prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"eslint-config-prettier": "*",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-react-hooks": {
"version": "5.1.0-rc-fb9a90fa48-20240614",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz",
@ -2630,6 +2705,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true
},
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@ -3827,6 +3908,18 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@ -4517,6 +4610,36 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-router": {
"version": "6.26.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz",
"integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==",
"dependencies": {
"@remix-run/router": "1.19.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.26.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz",
"integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==",
"dependencies": {
"@remix-run/router": "1.19.2",
"react-router": "6.26.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -4927,6 +5050,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/synckit": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
"integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
"dev": true,
"dependencies": {
"@pkgr/core": "^0.1.0",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/tailwindcss": {
"version": "3.4.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz",
@ -5100,6 +5239,12 @@
}
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true
},
"node_modules/update-browserslist-db": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",

View File

@ -24,19 +24,23 @@
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-intl": "^6.7.0",
"react-router-dom": "^6.26.2",
"recoil": "^0.7.7"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/crypto-js": "^4.2.2",
"@types/lodash": "^4.17.9",
"@types/node": "^22.7.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"eslint-plugin-react-refresh": "^0.4.12",
"globals": "^15.9.0",
"less": "^4.2.0",
"postcss": "^8.4.47",

View File

@ -0,0 +1,19 @@
import { LocalStorageService } from './local-storage.services';
import {
LS_DEFAULT_URL,
LS_ACTIVE_ACCOUNT,
IDB_T_NAME__USER_DATA,
IDB_T_NAME__ACCESS_TOKEN,
IDB_T_NAME__PRIVILEGE,
} from './storage.key';
import { IndexedDbService } from './indexed-db.services';
import { appConfig } from './indexed-db.init';
// LOCAL STORAGE NO NEED PROMISE FOR CALL
export const StorageActiveAccount = new LocalStorageService(LS_ACTIVE_ACCOUNT);
export const StorageDefaultURL = new LocalStorageService(LS_DEFAULT_URL);
// LOCAL STORAGE NEED PROMISE FOR CALL
export const StorageUserData = new IndexedDbService(appConfig, IDB_T_NAME__USER_DATA);
export const StorageAccessToken = new IndexedDbService(appConfig, IDB_T_NAME__ACCESS_TOKEN);
export const StoragePrivilege = new IndexedDbService(appConfig, IDB_T_NAME__PRIVILEGE);

View File

@ -0,0 +1,27 @@
import { openDB } from 'idb';
import { IDB_NAME, IDB_T_NAME__ACCESS_TOKEN, IDB_T_NAME__USER_DATA, IDB_T_NAME__PRIVILEGE } from './storage.key';
export const appConfig = openDB(IDB_NAME, 3, {
upgrade(db) {
if (!db.objectStoreNames.contains(IDB_T_NAME__ACCESS_TOKEN)) {
db.createObjectStore(IDB_T_NAME__ACCESS_TOKEN, {
keyPath: 'id',
autoIncrement: false,
});
}
if (!db.objectStoreNames.contains(IDB_T_NAME__USER_DATA)) {
db.createObjectStore(IDB_T_NAME__USER_DATA, {
keyPath: 'id',
autoIncrement: false,
});
}
if (!db.objectStoreNames.contains(IDB_T_NAME__PRIVILEGE)) {
db.createObjectStore(IDB_T_NAME__PRIVILEGE, {
keyPath: 'id',
autoIncrement: false,
});
}
},
});

View File

@ -0,0 +1,92 @@
import { DBSchema, IDBPDatabase } from 'idb';
import { IIndexedDbService } from './storage.interface';
import { decryptData, encryptData } from '@pos/base';
import { StorageActiveAccount } from './index';
export type IndexedDbValueEntity = any;
export class IndexedDbService implements IIndexedDbService<IndexedDbValueEntity> {
dbPromise: IDBPDatabase<DBSchema> | any;
dbTableName: string | any;
constructor(dbPromise: IDBPDatabase<DBSchema> | any, dbTableName: string | any) {
this.dbPromise = dbPromise;
this.dbTableName = dbTableName;
}
async create(payload: IndexedDbValueEntity): Promise<void> {
const id = StorageActiveAccount.get();
const dataIsExist = await this.get();
if (!dataIsExist && id) {
const d = encryptData(JSON.stringify(payload));
const item = { id, d };
const db = await this.dbPromise;
const tx = db?.transaction(this.dbTableName, 'readwrite');
const store = tx?.objectStore(this.dbTableName);
await store?.add(item);
} else {
this.update(payload);
}
}
async get(): Promise<IndexedDbValueEntity> {
const id = StorageActiveAccount.get();
if (!id) return undefined;
const db = await this.dbPromise;
const tx = db.transaction(this.dbTableName, 'readwrite');
const store = tx.objectStore(this.dbTableName);
const result = await store.get(id);
if (!result) return undefined;
const d = JSON.parse(decryptData(result.d));
return d;
}
async getAll(): Promise<IndexedDbValueEntity> {
const db = await this.dbPromise;
const tx = db.transaction(this.dbTableName, 'readwrite');
const store = tx.objectStore(this.dbTableName);
const result = await store.getAll();
if (!result) return undefined;
const newResult = result.map((item: any) => {
return {
...item,
d: JSON.parse(decryptData(item.d)),
};
});
return newResult;
}
async update(payload: IndexedDbValueEntity): Promise<void> {
const id = StorageActiveAccount.get();
if (id) {
const d = encryptData(JSON.stringify(payload));
const item = { id, d };
const db = await this.dbPromise;
const tx = db.transaction(this.dbTableName, 'readwrite');
const store = tx.objectStore(this.dbTableName);
await store.put(item);
}
}
async delete(): Promise<void> {
const id = StorageActiveAccount.get();
if (id) {
const db = await this.dbPromise;
const tx = db.transaction(this.dbTableName, 'readwrite');
const store = tx.objectStore(this.dbTableName);
await store.delete(id);
}
}
}

View File

@ -0,0 +1,31 @@
import { decryptData, encryptData } from '@pos/base';
import { ILocalStorageStorage, LSSetItemEntity } from './storage.interface';
export class LocalStorageService implements ILocalStorageStorage {
storageKey?: string;
constructor(storageKey?: string) {
this.storageKey = storageKey;
}
create(payload: LSSetItemEntity): void {
const keyData = this.storageKey ?? payload.key;
if (keyData && payload.value) {
const value = encryptData(JSON.stringify(payload.value));
localStorage.setItem(keyData, value);
}
}
get(key?: string): string | null | undefined {
const keyData = this.storageKey ?? key;
if (keyData) {
const value = localStorage.getItem(keyData);
if (value) return JSON.parse(decryptData(value));
return undefined;
}
}
delete(key?: string): void {
const keyData = this.storageKey ?? key;
if (keyData) localStorage.removeItem(keyData);
}
}

View File

@ -0,0 +1,18 @@
export interface IIndexedDbService<E> {
create(payload: E): Promise<void>;
get(): Promise<E>;
getAll(): Promise<E[]>;
update(payload: E): Promise<void>;
delete(): Promise<void>;
}
export interface LSSetItemEntity {
key?: string;
value: string;
}
export interface ILocalStorageStorage {
create(payload: LSSetItemEntity): void;
get(key: string): string | null | undefined;
delete(key: string): void;
}

View File

@ -0,0 +1,7 @@
export const IDB_NAME = 'pos__gen__conf';
export const IDB_T_NAME__USER_DATA = 'pos__Wl9Yf85G';
export const IDB_T_NAME__ACCESS_TOKEN = 'pos__vRtgHurGCD';
export const IDB_T_NAME__PRIVILEGE = 'pos__AzyMJGe21c';
export const LS_ACTIVE_ACCOUNT = 'pos__acc__log';
export const LS_DEFAULT_URL = 'pros__deft_u';

View File

@ -0,0 +1,544 @@
import { IBaseDataServicesRepository } from '../../domain/repositories';
import {
DataServicesConstructorEntity,
ResponseEntity,
ApiURLEntity,
RequestMethodEntity,
ManagerParamsEntity,
BaseEntity,
} from '../../domain/entities';
import { DEFAULT_METHOD, REQUEST_ACTION } from '../../infrastructure/constants';
import axios, { AxiosResponse } from 'axios';
export abstract class BaseRemoteDataServices<E extends BaseEntity = BaseEntity> implements IBaseDataServicesRepository {
protected baseUrl?: string;
protected URLs: ApiURLEntity;
protected methods: RequestMethodEntity;
protected moduleKey: string | undefined;
constructor(params: DataServicesConstructorEntity) {
const moduleKeyEncrypt = params.moduleKey;
this.moduleKey = moduleKeyEncrypt;
this.baseUrl = params.baseUrl;
this.URLs = {
...(this.makeDefaultURL(params.apiUrl ?? '') ?? {}),
...(params.urls ?? {}),
};
this.methods = {
...(DEFAULT_METHOD ?? {}),
...(params.requestMethods ?? {}),
};
}
protected makeApiUrl(variable = {} as any, url = '' as string): string {
return url
.split('/')
.map((item: string) => {
if (item.includes(':')) return variable[item.slice(1)];
return item;
})
.join('/');
}
protected makeIds(payload: E[]): string[] {
const ids: string[] = [];
for (const entity of payload) {
if (entity.id) ids.push(entity.id.toString());
}
return ids;
}
protected makeId(payload: E): string {
if (payload.id) return payload.id;
return '';
}
protected makeHeader(action: string) {
return {
'ex-model-key': this.moduleKey,
'ex-model-action': action,
};
}
protected makeDefaultURL(apiUrl: string): ApiURLEntity {
return {
getManyUrl: `${apiUrl}`,
getOneUrl: `${apiUrl}/:id`,
createUrl: `${apiUrl}`,
editUrl: `${apiUrl}/:id`,
deleteUrl: `${apiUrl}/:id`,
batchDeleteUrl: `${apiUrl}/batch-delete`,
activateUrl: `${apiUrl}/:id/active`,
batchActivateUrl: `${apiUrl}/batch-active`,
deactivateUrl: `${apiUrl}/:id/inactive`,
batchDeactivateUrl: `${apiUrl}/batch-inactive`,
confirmProcessDataUrl: `${apiUrl}/:id/confirm`,
batchConfirmProcessDataUrl: `${apiUrl}/batch-confirm`,
cancelProcessDataUrl: `${apiUrl}/:id/cancel`,
batchCancelProcessDataUrl: `${apiUrl}/batch-cancel`,
confirmProcessTransactionUrl: `${apiUrl}/:id/confirm-data`,
batchConfirmProcessTransactionUrl: `${apiUrl}/batch-confirm-data`,
cancelProcessTransactionUrl: `${apiUrl}/:id/cancel`,
batchCancelProcessTransactionUrl: `${apiUrl}/batch-cancel`,
rollbackProcessTransactionUrl: `${apiUrl}/:id/confirm-rollback`,
batchRollbackProcessTransactionUrl: `${apiUrl}/batch-confirm-rollback`,
holdProcessTransactionUrl: `${apiUrl}/:id/confirm-hold`,
batchHoldProcessTransactionUrl: `${apiUrl}/batch-confirm-hold`,
};
}
//custom request
async handleCustomRequest(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
...(manager.config ?? {}),
baseURL: this.baseUrl,
params: {
...(manager?.config?.params ?? {}),
},
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleGetOne(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.getOneUrl),
method: this.methods.getOneMethod,
...(manager.config ?? {}),
headers: this.makeHeader(REQUEST_ACTION.VIEW),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleGetMany(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.getManyUrl),
method: this.methods.getManyMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: { ...this.makeHeader(REQUEST_ACTION.VIEW), ...(manager?.config?.headers ?? {}) },
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleCreate(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.createUrl),
method: this.methods.createMethod,
// headers: HeaderPost,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CREATE),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleEdit(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.editUrl),
method: this.methods.editMethod,
// headers: HeaderPost,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.EDIT),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleDelete(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.deleteUrl),
method: this.methods.deleteMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.DELETE),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchDelete(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchDeleteUrl),
method: this.methods.batchDeleteMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.DELETE),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleConfirmProcessData(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.confirmProcessDataUrl),
method: this.methods.confirmProcessDataMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchConfirmProcessData(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchConfirmProcessDataUrl),
method: this.methods.batchConfirmProcessDataMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleCancelProcessData(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.cancelProcessDataUrl),
method: this.methods.cancelProcessDataMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CANCEL_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchCancelProcessData(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchCancelProcessDataUrl),
method: this.methods.batchCancelProcessDataMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CANCEL_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleActivate(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.activateUrl),
method: this.methods.activateMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchActivate(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchActivateUrl),
method: this.methods.batchActivateMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleDeactivate(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.deactivateUrl),
method: this.methods.deactivateMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchDeactivate(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchDeactivateUrl),
method: this.methods.batchDeactivateMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleConfirmProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.confirmProcessTransactionUrl),
method: this.methods.confirmProcessTransactionMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchConfirmProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchConfirmProcessTransactionUrl),
method: this.methods.batchConfirmProcessTransactionMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleCancelProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.cancelProcessTransactionUrl),
method: this.methods.cancelProcessTransactionMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CANCEL_PROCESS_TRANSACTION),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchCancelProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchCancelProcessTransactionUrl),
method: this.methods.batchCancelProcessTransactionMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CANCEL_PROCESS_TRANSACTION),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleRollbackProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.rollbackProcessTransactionUrl),
method: this.methods.rollbackProcessTransactionMethod,
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchRollbackProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchRollbackProcessTransactionUrl),
method: this.methods.batchRollbackProcessTransactionMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
...(manager?.config?.params ?? {}),
},
headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleHoldProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.holdProcessTransactionUrl),
method: this.methods.holdProcessTransactionMethod,
...(manager.config ?? {}),
params: {
action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION,
...(manager?.config?.params ?? {}),
},
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
async handleBatchHoldProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
try {
const response: AxiosResponse = await axios.request({
baseURL: this.baseUrl,
url: this.makeApiUrl(manager.variableURL, this.URLs.batchHoldProcessTransactionUrl),
method: this.methods.batchHoldProcessTransactionMethod,
// data: { ids: this.makeIds(manager.config?.data) },
...(manager.config ?? {}),
params: {
action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION,
...(manager?.config?.params ?? {}),
},
});
if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
} catch (error) {
if (manager.onFailed) manager.onFailed(error as ResponseEntity);
}
}
}

View File

@ -0,0 +1,4 @@
import { BaseEntity } from '../../domain/entities';
import { BaseRemoteDataServices } from './base-remote.data-services';
export class CommonRemoteDataServices<E extends BaseEntity = BaseEntity> extends BaseRemoteDataServices<E> {}

View File

@ -0,0 +1,2 @@
export * from './base-remote.data-services';
export * from './common-remote.data-services';

View File

@ -0,0 +1,100 @@
import { AxiosRequestConfig, Method as AxiosMethod } from 'axios';
export interface ResponseErrorEntity {
message?: Error | string | string[];
status: number;
}
export interface ResponseSuccessEntity<T = any> {
data?: T;
status: number;
}
export interface VariableURLEntity {
[key: string]: string;
}
export interface ManagerParamsEntity {
config?: AxiosRequestConfig;
variableURL?: VariableURLEntity;
onFailed?(error: ResponseErrorEntity): void;
onSuccess?(response: ResponseSuccessEntity): void;
}
export type ResponseEntity = ResponseSuccessEntity | ResponseErrorEntity;
export interface ApiURLEntity {
getManyUrl?: string;
getOneUrl?: string;
createUrl?: string;
editUrl?: string;
deleteUrl?: string;
batchDeleteUrl?: string;
confirmProcessDataUrl?: string;
batchConfirmProcessDataUrl?: string;
cancelProcessDataUrl?: string;
batchCancelProcessDataUrl?: string;
activateUrl?: string;
batchActivateUrl?: string;
deactivateUrl?: string;
batchDeactivateUrl?: string;
confirmProcessTransactionUrl?: string;
batchConfirmProcessTransactionUrl?: string;
cancelProcessTransactionUrl?: string;
batchCancelProcessTransactionUrl?: string;
rollbackProcessTransactionUrl?: string;
batchRollbackProcessTransactionUrl?: string;
holdProcessTransactionUrl?: string;
batchHoldProcessTransactionUrl?: string;
}
export interface RequestMethodEntity {
getManyMethod?: AxiosMethod;
getOneMethod?: AxiosMethod;
createMethod?: AxiosMethod;
editMethod?: AxiosMethod;
deleteMethod?: AxiosMethod;
batchDeleteMethod?: AxiosMethod;
confirmProcessDataMethod?: AxiosMethod;
batchConfirmProcessDataMethod?: AxiosMethod;
cancelProcessDataMethod?: AxiosMethod;
batchCancelProcessDataMethod?: AxiosMethod;
activateMethod?: AxiosMethod;
batchActivateMethod?: AxiosMethod;
deactivateMethod?: AxiosMethod;
batchDeactivateMethod?: AxiosMethod;
confirmProcessTransactionMethod?: AxiosMethod;
batchConfirmProcessTransactionMethod?: AxiosMethod;
cancelProcessTransactionMethod?: AxiosMethod;
batchCancelProcessTransactionMethod?: AxiosMethod;
rollbackProcessTransactionMethod?: AxiosMethod;
batchRollbackProcessTransactionMethod?: AxiosMethod;
holdProcessTransactionMethod?: AxiosMethod;
batchHoldProcessTransactionMethod?: AxiosMethod;
}
export interface DataServicesConstructorEntity {
baseUrl?: string;
apiUrl?: string;
urls?: ApiURLEntity;
requestMethods?: RequestMethodEntity;
moduleKey?: string;
}

View File

@ -0,0 +1,6 @@
export type FormPageActionEntity = 'create' | 'edit' | 'duplicate';
export interface BaseEntity {
id?: string;
[key: string]: any;
}

View File

@ -0,0 +1,2 @@
export * from './base-data-services.entity';
export * from './base.entity';

View File

@ -0,0 +1,7 @@
import { CommonRemoteDataServices } from '@pos/base';
import { IBaseDataServicesRepository } from '../repositories';
import { DataServicesConstructorEntity } from '../entities';
export function makeCommonDataServices(params: DataServicesConstructorEntity): IBaseDataServicesRepository {
return new CommonRemoteDataServices(params);
}

View File

@ -0,0 +1,7 @@
import { BaseEntity } from '../entities';
import { CommonTransformer } from '../transformers';
import { IBaseTransformerRepository } from '../repositories';
export function makeCommonTransformer<E extends BaseEntity = BaseEntity>(): IBaseTransformerRepository<E> {
return new CommonTransformer<E>();
}

View File

@ -0,0 +1,2 @@
export * from './base-data-services.factory';
export * from './base-transformer.factory';

View File

@ -0,0 +1,49 @@
import { ManagerParamsEntity } from '../entities';
export interface IBaseDataServicesRepository {
handleCustomRequest(manager: ManagerParamsEntity): Promise<void>;
//get data
handleGetOne(manager: ManagerParamsEntity): Promise<void>;
handleGetMany(manager: ManagerParamsEntity): Promise<void>;
//create and edit
handleCreate(manager: ManagerParamsEntity): Promise<void>;
handleEdit(manager: ManagerParamsEntity): Promise<void>;
//delete
handleDelete(manager: ManagerParamsEntity): Promise<void>;
handleBatchDelete(manager: ManagerParamsEntity): Promise<void>;
//confirm process data
handleConfirmProcessData(manager: ManagerParamsEntity): Promise<void>;
handleBatchConfirmProcessData(manager: ManagerParamsEntity): Promise<void>;
//cancel data
handleCancelProcessData(manager: ManagerParamsEntity): Promise<void>;
handleBatchCancelProcessData(manager: ManagerParamsEntity): Promise<void>;
//activate
handleActivate(manager: ManagerParamsEntity): Promise<void>;
handleBatchActivate(manager: ManagerParamsEntity): Promise<void>;
//deactivate
handleDeactivate(manager: ManagerParamsEntity): Promise<void>;
handleBatchDeactivate(manager: ManagerParamsEntity): Promise<void>;
//confirm process transaction
handleConfirmProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
handleBatchConfirmProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
//confirm cancel transaction
handleCancelProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
handleBatchCancelProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
//confirm rollback transaction
handleRollbackProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
handleBatchRollbackProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
//confirm hold transaction
handleHoldProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
handleBatchHoldProcessTransaction(manager: ManagerParamsEntity): Promise<void>;
}

View File

@ -0,0 +1,11 @@
import { BaseEntity } from '../entities';
export interface IBaseTransformerRepository<PayloadEntity extends BaseEntity = BaseEntity> {
transformerGetList(payload: PayloadEntity[]): PayloadEntity[];
transformerGetOne(payload: PayloadEntity): PayloadEntity;
transformerCreate(payload: PayloadEntity): PayloadEntity;
transformerEdit(payload: PayloadEntity): PayloadEntity;
transformerDuplicate(payload: PayloadEntity): PayloadEntity;
transformerFilterIndexTable(payload: any): any;
transformerDefaultFilterIndexTable(payload: any): any;
}

View File

@ -0,0 +1,2 @@
export * from './base-services.repository';
export * from './base-transformer.repository';

View File

@ -0,0 +1,26 @@
import { BaseEntity } from '../entities';
import { IBaseTransformerRepository } from '../repositories';
export abstract class BaseTransformer<E extends BaseEntity = BaseEntity> implements IBaseTransformerRepository<E> {
transformerGetList(payload: any): E[] {
return payload;
}
transformerGetOne(payload: any): E {
return payload;
}
transformerCreate(payload: any): E {
return { ...payload, id: undefined };
}
transformerEdit(payload: any): E {
return { ...payload, id: undefined };
}
transformerDuplicate(payload: any): E {
return { ...payload, id: undefined };
}
transformerFilterIndexTable(payload: any): any {
return payload;
}
transformerDefaultFilterIndexTable(payload: any): any {
return payload;
}
}

View File

@ -0,0 +1,4 @@
import { BaseEntity } from '../entities';
import { BaseTransformer } from '../transformers';
export class CommonTransformer<PayloadEntity extends BaseEntity = BaseEntity> extends BaseTransformer<PayloadEntity> {}

View File

@ -0,0 +1,2 @@
export * from './base.transformer';
export * from './common.transformer';

18
src/base/index.ts Normal file
View File

@ -0,0 +1,18 @@
// Data
export * from './data/local-data-services';
export * from './data/remote-data-services';
// Domain
export * from './domain/entities';
export * from './domain/factories';
export * from './domain/repositories';
export * from './domain/transformers';
// Infrastructure
export * from './infrastructure/constants';
export * from './infrastructure/helpers';
// Presentations
export * from './presentation/hooks';
export * from './presentation/providers';
export * from './presentation/states';

View File

@ -0,0 +1,9 @@
export const API_URL = {
LOGIN: '/v1/auth',
LOGOUT: '/v1/auth/logout',
FORCE_LOGOUT: '/v1/auth/force-logout',
REPORT: '/v1/report',
REPORT_TENANT: '/v1/report',
REPORT_BOOKMARK: '/v1/report-bookmark',
};

View File

@ -0,0 +1,6 @@
export enum AppSource {
POS_ADMIN = 'POS_ADMIN',
POS_COUNTER = 'POS_COUNTER',
QUEUE_ADMIN = 'QUEUE_ADMIN',
QUEUE_CUSTOMER = 'QUEUE_CUSTOMER',
}

View File

@ -0,0 +1,10 @@
export const APP_MODE: 'development' | 'production' = import.meta.env.VITE_APP_MODE;
export const BASE_API_URL: string = import.meta.env.VITE_BASE_API_URL;
export const BASE_ASSET_URL: string = import.meta.env.VITE_BASE_ASSET_URL;
export const BASE_API_REPORT_URL: string = import.meta.env.VITE_BASE_API_REPORT_URL;
export const SUPERSET_URL: string = import.meta.env.VITE_SUPERSET_URL;
export const EMBED_DASHBOARD_ID = import.meta.env.VITE_EMBED_DASHBOARD_ID;
export const DOWLOAD_POS_WINDOWS_URL = import.meta.env.VITE_DOWLOAD_POS_WINDOWS_URL;
export const DOWLOAD_POS_LINUX_DEB_URL = import.meta.env.VITE_DOWLOAD_POS_LINUX_DEB_URL;
export const DOWLOAD_POS_LINUX_SNAP_URL = import.meta.env.VITE_DOWLOAD_POS_LINUX_SNAP_URL;

View File

@ -0,0 +1,5 @@
export const formatDate = 'DD/MM/YYYY';
export const formatTime = 'HH:mm';
export const formatDateTime = `${formatDate} ${formatTime}`;

View File

@ -0,0 +1,14 @@
export * from './environment';
export * from './web-url';
export * from './sidebar';
export * from './module-key';
export * from './page-action';
export * from './request-action';
export * from './request-info';
export * from './request-method';
export * from './status-data';
export * from './api-url';
export * from './query-params';
export * from './privilege';
export * from './format-date';
export * from './app-source';

View File

@ -0,0 +1,47 @@
export * from './report-group.constant';
export const MODULE_KEY = {
DASHBOARD: 'DASHBOARD',
CALENDAR: 'CALENDAR',
BOOKING: 'BOOKING',
REFUND: 'REFUND',
RECONCILIATION: 'RECONCILIATION',
ITEM: {
INDEX: 'ITEM',
ITEM_ITEM_RATE: 'ITEM.ITEM_RATE',
ITEM_ITEM: 'ITEM.ITEM',
ITEM_ITEM_CATEGORY: 'ITEM.ITEM_CATEGORY',
},
SEASON: {
INDEX: 'SEASON',
SEASON_TYPE: 'SEASON.SEASON_TYPE',
SEASON_PERIOD: 'SEASON.SEASON_PERIOD',
},
USER: {
INDEX: 'USER',
USER_USER: 'USER.USER',
USER_USER_PRIVILEGE: 'USER.USER_PRIVILEGE',
},
TENANT: 'TENANT',
CMS: {
INDEX: 'WEB_INFORMATION',
CMS_TERMS_AND_CONDITION: 'WEB_INFORMATION.TERMS_AND_CONDITION',
CMS_FAQ: 'WEB_INFORMATION.FAQ',
CMS_NEWS: 'WEB_INFORMATION.NEWS',
CMS_BANNER: 'WEB_INFORMATION.BANNER',
},
SETTING: {
INDEX: 'SETTING',
SETTING_TAX: 'SETTING.TAX',
SETTING_VIP_CATEGORY: 'SETTING.VIP_CATEGORY',
SETTING_PAYMENT_METHOD: 'SETTING.PAYMENT_METHOD',
SETTING_GATE: 'SETTING.GATE',
SETTING_PROFIT_SHARE_FORMULA: 'SETTING.PROFIT_SHARE_FORMULA',
SETTING_SALES_PRICE_FORMULA: 'SETTING.SALES_PRICE_FORMULA',
SETTING_QR_SUPER_ADMIN: 'SETTING.QR_SUPER_ADMIN',
},
REPORT: { INDEX: 'REPORT', REPORT_TRANSACTION: 'REPORT.TRANSACTION', REPORT_TENANT: 'REPORT.TENANT' },
REPORT_TENANT: 'REPORT_TENANT',
DISCOUNT_CODE: 'DISCOUNT_CODE',
DOWNLOAD_POS_APP: 'DOWNLOAD_POS_APP',
};

View File

@ -0,0 +1,4 @@
export enum REPORT_GROUP {
transaction_report = 'transaction_report',
tenant_report = 'tenant_report',
}

View File

@ -0,0 +1,30 @@
export enum PAGE_ACTION {
SAVE = 'create.save',
DETAIL = 'view',
CREATE = 'create',
EDIT = 'edit',
DELETE = 'delete',
DUPLICATE = 'create.duplicate',
PRINT = 'print',
PRINT_COPY = 'print_copy',
PRINT_PREVIEW = 'print_preview',
RESET_PRINT_COUNT = 'reset_print_count',
APPROVAL_DATA = 'edit.approval_data',
CONFIRM_DATA = 'confirm.confirm_data',
CANCEL_DATA = 'cancel.cancel_data',
ACTIVATE_DATA = 'edit.activate_data',
DEACTIVATE_DATA = 'edit.deactivate_data',
CONFIRM_TRANSACTION = 'confirm.confirm_transaction',
CANCEL_TRANSACTION = 'cancel.cancel_transaction',
ROLLBACK_TRANSACTION = 'confirm.rollback_transaction',
ON_HOLD_TRANSACTION = 'confirm.on_hold_transaction',
OPEN_DRAWER_FILTER = 'view.open_drawer_filter',
LOGS = 'log',
NOTES = 'note',
}

View File

@ -0,0 +1,8 @@
export const defaultAppPrivilege = {
view: true,
create: true,
edit: true,
delete: true,
cancel: true,
confirm: true,
};

View File

@ -0,0 +1,16 @@
export function makeArrayIds({ data, valueWhenNull }: { data: any; valueWhenNull?: string }): string[] {
const ids = [];
if (!data) {
if (valueWhenNull) return [valueWhenNull];
else return [];
} else if (Array.isArray(data)) {
data?.forEach((item) => {
const id = item?.id ?? item?.uuid;
if (item?.id) ids.push(id);
});
} else {
const id = data?.id ?? data?.uuid;
if (id) ids.push(id);
}
return ids;
}

View File

@ -0,0 +1,17 @@
export const REQUEST_ACTION = {
VIEW: 'view',
CREATE: 'create',
EDIT: 'edit',
DELETE: 'delete',
CONFIRM_DATA: 'edit',
CANCEL_DATA: 'edit',
ACTIVATE_DATA: 'edit',
DEACTIVATE_DATA: 'edit',
CONFIRM_PROCESS_TRANSACTION: 'confirm',
ROLLBACK_PROCESS_TRANSACTION: 'reverse',
CANCEL_PROCESS_TRANSACTION: 'cancel',
PRINT: 'print',
};

View File

@ -0,0 +1,12 @@
export const RequestInfoOptions = [
{ label: 'Create Data', value: 'Create Data' },
{ label: 'Edit Data', value: 'Edit Data' },
{ label: 'Edit Active', value: 'Edit Active' },
{ label: 'Edit Inactive', value: 'Edit Inactive' },
{ label: 'Edit Data & Status Active', value: 'Edit Data & Status Active' },
{ label: 'Edit Data & Status Inactive', value: 'Edit Data & Status Inactive' },
{ label: 'Delete Data', value: 'Delete Data' },
{ label: 'Process Transaction', value: 'Process Transaction' },
{ label: 'Cancel Transaction', value: 'Cancel Transaction' },
{ label: 'Rollback Transaction', value: 'Rollback Transaction' },
];

View File

@ -0,0 +1,43 @@
import { RequestMethodEntity } from '../../../domain/entities';
export const DEFAULT_METHOD: RequestMethodEntity = {
getManyMethod: 'GET',
getOneMethod: 'GET',
createMethod: 'POST',
editMethod: 'PUT',
deleteMethod: 'DELETE',
// batchDeleteMethod: 'DELETE',
batchDeleteMethod: 'PUT',
activateMethod: 'PATCH',
// activateMethod: 'PUT',
batchActivateMethod: 'PUT',
deactivateMethod: 'PATCH',
// deactivateMethod: 'PUT',
batchDeactivateMethod: 'PUT',
confirmProcessDataMethod: 'PATCH',
// confirmProcessDataMethod: 'PUT',
batchConfirmProcessDataMethod: 'PUT',
cancelProcessDataMethod: 'PATCH',
// cancelProcessDataMethod: 'PUT',
batchCancelProcessDataMethod: 'PUT',
confirmProcessTransactionMethod: 'PATCH',
// confirmProcessTransactionMethod: 'PUT',
batchConfirmProcessTransactionMethod: 'PUT',
cancelProcessTransactionMethod: 'PATCH',
// cancelProcessTransactionMethod: 'PUT',
batchCancelProcessTransactionMethod: 'PUT',
rollbackProcessTransactionMethod: 'PATCH',
// rollbackProcessTransactionMethod: 'PUT',
batchRollbackProcessTransactionMethod: 'PUT',
holdProcessTransactionMethod: 'PUT',
batchHoldProcessTransactionMethod: 'PUT',
};

View File

@ -0,0 +1 @@
export const sideConf = { width: 230, collapsedWidth: 75 };

View File

@ -0,0 +1,247 @@
import { capitalizeEachWord } from '../../helpers';
export enum DATA_TYPE {
REQUESTED = 'requested',
ORIGINAL = 'original',
}
export enum STATUS_DATA {
DRAFT = 'draft',
ACTIVE = 'active',
INACTIVE = 'inactive',
REQUESTED = 'requested',
OPEN = 'open',
DECLINE = 'declined',
DELETED = 'deleted',
WAITING = 'waiting',
CANCEL = 'cancel',
DONE = 'done',
IN_PROCESS = 'in_process',
CLOSED = 'closed',
TODO = 'todo',
ON_HOLD = 'on_hold',
BLOCKED = 'blocked',
BLACKLIST = 'blacklist',
PROBLEM = 'problem',
POSTED = 'posted',
PAID = 'paid',
APPROVED = 'approved',
PENDING = 'pending',
REJECTED = 'rejected',
SETTLED = 'settled',
CONFIRMED = 'confirmed',
PROCESS_REFUND = 'proses refund',
PROCESS_REFUND_WAHANA = 'proses refund wahana',
REFUNDED = 'refunded',
PARTIAL_REFUND = 'partial refund',
}
export enum STATUS_DATA_COLOR {
// DRAFT = '#4A6785',
// ACTIVE = '#00875a',
// INACTIVE = '#cf1322',
// REQUESTED = '#faad14',
// OPEN = '#0052cc',
// DECLINE = '#cf1322',
// DELETED = '#cf1322',
// WAITING = '#faad14',
// CANCEL = '#cf1322',
// DONE = '#00875a',
// IN_PROCESS = '#0052cc',
// CLOSED = '#cf1322',
// TODO = '#4A6785',
// ON_HOLD = '#4A6785',
// BLOCKED = '#cf1322',
// BLACKLIST = '#cf1322',
DRAFT = '#95A5A6',
ACTIVE = '#3EBD93',
INACTIVE = '#E74C3C',
REQUESTED = '#F39C12',
OPEN = '#6F7CBA',
DECLINE = '#B71C1C',
DELETED = '#FF5252',
WAITING = '#6c5dd0',
CANCEL = '#9B59B6',
// CANCEL = '#FF5252',
// DONE = '#27AE60',
DONE = '#3EBD93',
IN_PROCESS = '#3498DB',
// CLOSED = '#8E44AD',
CLOSED = '#FF5252',
// TODO = '#95A5A6',
TODO = '#9B59B6',
ON_HOLD = '#607D8B',
BLOCKED = '#FF5722',
BLACKLIST = '#D32F2F',
PROBLEM = '#FF5722',
POSTED = '#3EBD93',
PAID = '#3EBD93',
APPROVED = '#3EBD93',
PENDING = '#d4940d',
REJECTED = '#ef4444',
SETTLED = '#5dadd0',
CONFIRMED = '#5dadd0',
PROCESS_REFUND = '#d7a29a',
REFUNDED = '#fe725e',
}
export const STATUS_DATA_OPTIONS = [
{
label: capitalizeEachWord(STATUS_DATA.DRAFT.split('_').join(' ')),
value: STATUS_DATA.DRAFT,
color: STATUS_DATA_COLOR.DRAFT,
},
{
label: capitalizeEachWord(STATUS_DATA.ON_HOLD.split('_').join(' ')),
value: STATUS_DATA.ON_HOLD,
color: STATUS_DATA_COLOR.ON_HOLD,
},
{
label: capitalizeEachWord(STATUS_DATA.OPEN.split('_').join(' ')),
value: STATUS_DATA.OPEN,
color: STATUS_DATA_COLOR.OPEN,
},
{
label: capitalizeEachWord(STATUS_DATA.TODO.split('_').join(' ')),
value: STATUS_DATA.TODO,
color: STATUS_DATA_COLOR.TODO,
},
{
label: capitalizeEachWord(STATUS_DATA.IN_PROCESS.split('_').join(' ')),
value: STATUS_DATA.IN_PROCESS,
color: STATUS_DATA_COLOR.IN_PROCESS,
},
{
label: capitalizeEachWord(STATUS_DATA.WAITING.split('_').join(' ')),
value: STATUS_DATA.WAITING,
color: STATUS_DATA_COLOR.WAITING,
},
{
label: capitalizeEachWord(STATUS_DATA.REQUESTED.split('_').join(' ')),
value: STATUS_DATA.REQUESTED,
color: STATUS_DATA_COLOR.REQUESTED,
},
{
label: capitalizeEachWord(STATUS_DATA.BLOCKED.split('_').join(' ')),
value: STATUS_DATA.BLOCKED,
color: STATUS_DATA_COLOR.BLOCKED,
},
{
label: capitalizeEachWord(STATUS_DATA.PROBLEM.split('_').join(' ')),
value: STATUS_DATA.PROBLEM,
color: STATUS_DATA_COLOR.PROBLEM,
},
{
label: capitalizeEachWord(STATUS_DATA.CANCEL.split('_').join(' ')),
value: STATUS_DATA.CANCEL,
color: STATUS_DATA_COLOR.CANCEL,
},
{
label: capitalizeEachWord(STATUS_DATA.CLOSED.split('_').join(' ')),
value: STATUS_DATA.CLOSED,
color: STATUS_DATA_COLOR.CLOSED,
},
{
label: capitalizeEachWord(STATUS_DATA.DELETED.split('_').join(' ')),
value: STATUS_DATA.DELETED,
color: STATUS_DATA_COLOR.DELETED,
},
{
label: capitalizeEachWord(STATUS_DATA.INACTIVE.split('_').join(' ')),
value: STATUS_DATA.INACTIVE,
color: STATUS_DATA_COLOR.INACTIVE,
},
{
label: capitalizeEachWord(STATUS_DATA.BLACKLIST.split('_').join(' ')),
value: STATUS_DATA.BLACKLIST,
color: STATUS_DATA_COLOR.BLACKLIST,
},
{
label: capitalizeEachWord(STATUS_DATA.DECLINE.split('_').join(' ')),
value: STATUS_DATA.DECLINE,
color: STATUS_DATA_COLOR.DECLINE,
},
{
label: capitalizeEachWord(STATUS_DATA.APPROVED.split('_').join(' ')),
value: STATUS_DATA.APPROVED,
color: STATUS_DATA_COLOR.APPROVED,
},
{
label: capitalizeEachWord(STATUS_DATA.ACTIVE.split('_').join(' ')),
value: STATUS_DATA.ACTIVE,
color: STATUS_DATA_COLOR.ACTIVE,
},
{
label: capitalizeEachWord(STATUS_DATA.DONE.split('_').join(' ')),
value: STATUS_DATA.DONE,
color: STATUS_DATA_COLOR.DONE,
},
{
label: capitalizeEachWord(STATUS_DATA.POSTED.split('_').join(' ')),
value: STATUS_DATA.POSTED,
color: STATUS_DATA_COLOR.POSTED,
},
{
label: capitalizeEachWord(STATUS_DATA.PAID.split('_').join(' ')),
value: STATUS_DATA.PAID,
color: STATUS_DATA_COLOR.PAID,
},
{
label: capitalizeEachWord(STATUS_DATA.PENDING.split('_').join(' ')),
value: STATUS_DATA.PENDING,
color: STATUS_DATA_COLOR.PENDING,
},
{
label: capitalizeEachWord(STATUS_DATA.REJECTED.split('_').join(' ')),
value: STATUS_DATA.REJECTED,
color: STATUS_DATA_COLOR.REJECTED,
},
{
label: capitalizeEachWord(STATUS_DATA.REJECTED.split('_').join(' ')),
value: STATUS_DATA.REJECTED,
color: STATUS_DATA_COLOR.REJECTED,
},
{
label: capitalizeEachWord(STATUS_DATA.SETTLED.split('_').join(' ')),
value: STATUS_DATA.SETTLED,
color: STATUS_DATA_COLOR.SETTLED,
},
{
label: capitalizeEachWord(STATUS_DATA.CONFIRMED.split('_').join(' ')),
value: STATUS_DATA.CONFIRMED,
color: STATUS_DATA_COLOR.CONFIRMED,
},
{
label: capitalizeEachWord(STATUS_DATA.PROCESS_REFUND.split('_').join(' ')),
value: STATUS_DATA.PROCESS_REFUND,
color: STATUS_DATA_COLOR.PROCESS_REFUND,
},
{
label: capitalizeEachWord(STATUS_DATA.PROCESS_REFUND_WAHANA.split('_').join(' ')),
value: STATUS_DATA.PROCESS_REFUND_WAHANA,
color: STATUS_DATA_COLOR.PROCESS_REFUND,
},
{
label: capitalizeEachWord(STATUS_DATA.REFUNDED.split('_').join(' ')),
value: STATUS_DATA.REFUNDED,
color: STATUS_DATA_COLOR.REFUNDED,
},
{
label: capitalizeEachWord(STATUS_DATA.PARTIAL_REFUND.split('_').join(' ')),
value: STATUS_DATA.PARTIAL_REFUND,
color: STATUS_DATA_COLOR.REFUNDED,
},
];
export function makeColorStatus(status: STATUS_DATA): STATUS_DATA_COLOR | undefined {
const color = STATUS_DATA_OPTIONS.find(
(item) => item.value?.split('_').join('') === status?.split('_').join('').toLowerCase(),
);
return color?.color;
}

View File

@ -0,0 +1,12 @@
export enum UserRole {
SUPERADMIN = 'superadmin',
STAFF = 'staff',
TENANT = 'tenant',
QUEUE_ADMIN = 'queue_admin',
}
export const UserRoleOptions = [
{ value: UserRole.SUPERADMIN, label: 'Super Admin' },
{ value: UserRole.QUEUE_ADMIN, label: 'Admin Antrian' },
{ value: UserRole.STAFF, label: 'Staff' },
];

View File

@ -0,0 +1,4 @@
export const WEB_URL = {
ITEM: '/app/item',
ITEM_MASTER: '/app/item-master',
};

View File

@ -0,0 +1,80 @@
import { capitalizeEachWord } from '@pos/base';
export enum ACCESS_CONTROL_ROLE {
SYSTEM = 'System',
GLOBAL = 'Global',
ORGANIZATION = 'Organization',
CONTACT_LEVEL = 'Contact Level',
ACCESS_TO_CONTACT = 'Access to Contact',
SUPER_ADMIN = 'Super Admin',
}
export enum ACCESS_TO_CONTACT_TYPE {
RESTRICTED = 'Restricted',
ADDITIONAL = 'Additional',
}
export enum PRIVILEGE_ACTION {
VIEW = 'VIEW',
CREATE = 'CREATE',
EDIT = 'EDIT',
DELETE = 'DELETE',
CHANGE_STATUS = 'CHANGE_STATUS',
CONFIRM = 'CONFIRM',
VIEW_EXTERNAL_LINK = 'VIEW_EXTERNAL_LINK',
DOWNLOAD = 'DOWNLOAD',
CANCEL = 'CANCEL',
}
export enum PRIVILEGE_CONDITION {
ORGANIZATION = 'ORGANIZATION',
DEPARTMENT = 'DEPARTMENT',
MINISTRY_POSITION = 'MINISTRY_POSITION',
TEAM = 'TEAM',
CARE_GROUP = 'CARE_GROUP',
TASK = 'TASK',
EVENT = 'EVENT',
ONLY_OWN = 'ONLY_OWN',
}
export enum PRIVILEGE_CONDITION_VALUE {
OWN = 'OWN',
ALL = 'ALL',
WITH_CHILD = 'WITH_CHILD',
TRUE = 'TRUE',
FALSE = 'FALSE',
PIC = 'PIC',
COORDINATOR = 'COORDINATOR',
}
export function generatePrivilegeValueOptions() {
const options = {};
Object.values(PRIVILEGE_CONDITION_VALUE).forEach((el) => {
Object.assign(options, {
[`${el}`]: {
label: capitalizeEachWord(el.split('_').join(' ').toLowerCase()),
value: el,
},
});
});
return options;
}
export const AccessControlRoleOpt: any = {
[`${ACCESS_CONTROL_ROLE.SUPER_ADMIN}`]: {
label: 'Super Admin',
value: ACCESS_CONTROL_ROLE.SUPER_ADMIN,
},
[`${ACCESS_CONTROL_ROLE.SYSTEM}`]: {
label: 'System Admin',
value: ACCESS_CONTROL_ROLE.SYSTEM,
},
[`${ACCESS_CONTROL_ROLE.GLOBAL}`]: {
label: 'Global Officer',
value: ACCESS_CONTROL_ROLE.GLOBAL,
},
[`${ACCESS_CONTROL_ROLE.ORGANIZATION}`]: {
label: 'Organization Officer',
value: ACCESS_CONTROL_ROLE.ORGANIZATION,
},
};

View File

@ -0,0 +1,62 @@
import { StoragePrivilege } from '@pos/base/data/local-data-services';
import { omit } from 'lodash';
export * from './access-control.constant';
export const DefaultAccessControl = {
view_index: false,
view_detail: false,
create: false,
update: false,
delete: false,
change_status: false,
confirm: false,
view_external_link: false,
download: false,
};
export const DefaultAccessControlTrue = {
view_index: true,
view_detail: true,
create: true,
update: true,
delete: true,
change_status: true,
confirm: true,
view_external_link: true,
download: true,
};
export enum ControlTypeEnum {
view_index = 'view_index',
view_detail = 'view_detail',
create = 'create',
update = 'update',
delete = 'delete',
change_status = 'change_status',
confirm = 'confirm',
view_external_link = 'view_external_link',
download = 'download',
}
export type ControlType = {
view_index: boolean;
view_detail: boolean;
create: boolean;
update: boolean;
delete: boolean;
change_status: boolean;
confirm: boolean;
view_external_link: boolean;
download: boolean;
};
interface GetProps {
moduleKey: string;
}
export function getAccessControlByModule(props: GetProps): ControlType {
const { moduleKey } = props;
const list: any[] = StoragePrivilege.get() as any;
const control = list?.find((el: any) => el.module_key === moduleKey) ?? DefaultAccessControl;
const fixControl = omit(control, ['module_key']) as ControlType;
return fixControl;
}

View File

@ -0,0 +1,41 @@
import { StorageDefaultURL, StoragePrivilege, WEB_URL, encryptData } from '@pos/base';
import { StorageAccessToken, StorageUserData, StorageActiveAccount } from '@pos/base';
export async function handleLogout(authParam?: string) {
// INDEXED-DB
await StorageUserData.delete(); //remove user data from indexed-db
await StorageAccessToken.delete(); //remove access token from local indexed-db
await StoragePrivilege.delete();
//LOCAL STORAGE
await StorageActiveAccount.delete(); // remove active account from local storage
await StorageDefaultURL.delete();
window.location.replace(
`/auth/login${authParam ? `?u=${encodeURIComponent(encryptData(JSON.stringify(authParam)))}` : ''}`,
);
}
export async function handleLogin(respLogin: any) {
await StorageAccessToken.delete();
try {
// save user id to StorageActiveAccount on Local storage
const userID = respLogin?.id; // adjust key "id" with key that have userID value
await StorageActiveAccount.create({ value: userID });
// save access token to StorageAccessToken
const accessToken = respLogin?.token;
await StorageAccessToken.create('Bearer ' + accessToken);
// save user data information to
const userData = respLogin; // adjustment with value user data
await StorageUserData.create(userData);
// find web url module for first menu to access
await StorageDefaultURL.create({ value: WEB_URL.ITEM });
// save default url to local storage
} catch (error) {
console.error(error);
}
}

View File

@ -0,0 +1 @@
export const ENC_STORAGE_KEY = 'zkwqyo3RpNEh8un2CIAs'; //TODO change value from environment

View File

@ -0,0 +1,13 @@
import { AES } from 'crypto-js';
import enc from 'crypto-js/enc-utf8';
import { ENC_STORAGE_KEY } from './encryption-key';
// Encryption function
export function encryptData(data: any) {
return AES.encrypt(data, ENC_STORAGE_KEY).toString();
}
// Decryption function
export function decryptData(encryptedData: any) {
return AES.decrypt(encryptedData, ENC_STORAGE_KEY).toString(enc);
}

View File

@ -0,0 +1,6 @@
export class AccessDeniedError extends Error {
constructor() {
super('Denied access!');
this.name = 'AccessDeniedError';
}
}

View File

@ -0,0 +1,3 @@
export * from './unexpected-error';
export * from './access-denied-error';
export * from './invalid-credentials-error';

View File

@ -0,0 +1,6 @@
export class InvalidCredentialsError extends Error {
constructor() {
super('Invalid credentials.');
this.name = 'InvalidCredentialsError';
}
}

View File

@ -0,0 +1,25 @@
export class UnexpectedError extends Error {
constructor() {
super('Something went wrong. Please try again later.');
this.name = 'UnexpectedError';
}
}
export class HttpError extends Error {
public errorObject: any;
constructor(object: any) {
super('Something went wrong. Please try again later.');
this.name = 'HttpError';
this.errorObject = object;
}
}
export class ErrorRequest<T = any> extends Error {
constructor(
public data: T = null as any,
public message = 'Internal server error.',
public status = 500,
) {
super(message);
}
}

View File

@ -0,0 +1,21 @@
import * as _ from 'lodash';
export function findMenuItemByPath(menu: any, targetTo: any) {
let result;
_.forEach(menu, (item): any => {
if (item.label && item.label.props && item.label.props.to === targetTo) {
result = item;
return false; // exit the loop
}
if (item.children) {
const nestedResult = findMenuItemByPath(item.children, targetTo);
if (nestedResult) {
result = nestedResult;
return false; // exit the loop
}
}
});
return result || undefined; // return result or undefined explicitly
}

View File

@ -0,0 +1 @@
export * from './filter-menu';

View File

@ -0,0 +1,18 @@
import { CustomFormatConfig } from '@formatjs/intl';
interface CurrencyProps {
currencyOptions?: CustomFormatConfig;
value?: number | bigint;
}
export function currencyFormatter(props: CurrencyProps) {
const { currencyOptions, value } = props;
if (!value) return 'Rp 0,00';
return new Intl.NumberFormat('id', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0,
currencyDisplay: 'narrowSymbol',
...currencyOptions,
}).format(value);
}

View File

@ -0,0 +1,2 @@
export * from './string.formatter';
export * from './currency.formatter';

View File

@ -0,0 +1,54 @@
interface CurrencyEntity {
value: number;
currency?: ' IDR' | 'EUR' | 'JPY';
}
export interface IStringFormatter {
capitalizeFistWord(string: string): string;
capitalizeEachWord(string: string): string;
stringLimiter(string: string, limit: number): string;
upperCase(string: string): string;
lowerCase(string: string): string;
currency(payload: CurrencyEntity): string;
}
export class BaseStringFormatter implements IStringFormatter {
capitalizeFistWord(string: string): string {
if (typeof string !== 'string') return '';
return string.charAt(0).toUpperCase() + string.slice(1);
}
capitalizeEachWord(string: string): string {
if (typeof string !== 'string') return '';
const newStringSplit = string.split(' ');
const newString = newStringSplit.map((item) => {
return item.charAt(0).toUpperCase() + item.slice(1);
});
return newString.join(' ');
}
stringLimiter(string: string, limit: number): string {
if (!limit || !string) return string;
return string.length > limit ? `${string.substring(0, limit)} ...` : string;
}
upperCase(string: string): string {
if (!string) return '';
return string.toUpperCase();
}
lowerCase(string: string): string {
if (!string) return '';
return string.toLowerCase();
}
currency(payload: CurrencyEntity): any {
const { value, currency = 'IDR' } = payload;
if (!value) return '';
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency,
}).format(value);
}
}
const stringFormatter = new BaseStringFormatter();
export const { capitalizeFistWord, capitalizeEachWord, stringLimiter, upperCase, lowerCase, currency } =
stringFormatter;

View File

@ -0,0 +1,76 @@
import axios, { AxiosRequestConfig } from 'axios';
async function getData(params: any, url: string, axiosParams?: AxiosRequestConfig) {
try {
const response = await axios({
url: url,
method: 'get',
params: {
limit: 10,
...params,
},
...(axiosParams ? axiosParams : {}),
});
return response;
} catch (error) {
console.log(error);
}
}
interface Result {
currentPage: number;
data: any[];
}
async function manipulatedGenerateDataPagination(
url: string,
params: any,
prevData: any[] = [],
currentPage = 1,
axiosParams?: AxiosRequestConfig,
): Promise<Result> {
try {
const response = await getData({ ...params, page: currentPage }, url, axiosParams);
const data = response?.data?.data ?? [];
const meta = response?.data?.meta;
if (meta.totalPages <= currentPage) {
return {
currentPage: currentPage,
data: [...prevData, ...data],
};
} else {
const response2 = await manipulatedGenerateDataPagination(
url,
params,
[...prevData, ...data],
currentPage + 1,
axiosParams,
);
const data2: any[] = response2?.data ?? [];
const newCurrentPage: number = response2?.currentPage;
return {
currentPage: newCurrentPage,
data: data2,
};
}
} catch (error) {
return {
currentPage: 1,
data: prevData,
};
}
}
interface GenerateDataPagination {
url: string;
params: any;
axiosParams?: AxiosRequestConfig;
}
export async function generateDataPagination(props: GenerateDataPagination): Promise<Result> {
const { url, params = {}, axiosParams } = props;
const data = await manipulatedGenerateDataPagination(url, params, [], 1, axiosParams);
return data;
}

View File

@ -0,0 +1,12 @@
export * from './auth-handler';
export * from './encryption';
export * from './error-handlings';
export * from './filter-menu';
export * from './notifications';
export * from './formatter';
export * from './timeout';
export * from './localstorage-effect';
export * from './access-control';
export * from './use-query-param';
export * from './get-data-pagination';
export * from './validate-formula';

View File

@ -0,0 +1,12 @@
export const localStorageEffect =
(key: string) =>
({ setSelf, onSet }: any) => {
const savedValue = localStorage.getItem(key);
if (savedValue != null) {
setSelf(JSON.parse(savedValue));
}
onSet((newValue: any, _: any, isReset: any) => {
isReset ? localStorage.removeItem(key) : localStorage.setItem(key, JSON.stringify(newValue));
});
};

View File

@ -0,0 +1,53 @@
import { notification } from 'antd';
import { ReactNode } from 'react';
notification.config({
duration: 1.5,
});
export function notificationSuccess(content?: ReactNode | any): void {
notification.success({
message: 'Success',
description: content,
className: 'no-print',
});
}
export function notificationError(content?: ReactNode | any): void {
notification.error({
message: 'Error',
description: content,
className: 'no-print',
});
}
export function notificationWarning(content?: ReactNode | any): void {
notification.warning({
message: 'Warning',
description: content,
className: 'no-print',
});
}
export function notificationInfo(content?: ReactNode | any): void {
notification.info({
message: 'Info',
description: content,
className: 'no-print',
});
}
interface MultiNotificationParams {
success?: string[];
failed?: string[];
warning?: string[];
info?: string[];
}
export function multiNotification(message: MultiNotificationParams) {
const { success = [], failed = [], warning = [], info = [] } = message;
success.forEach((item) => notificationSuccess(item));
failed.forEach((item) => notificationError(item));
warning.forEach((item) => notificationWarning(item));
info.forEach((item) => notificationInfo(item));
}

View File

@ -0,0 +1,3 @@
export function timeout(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View File

@ -0,0 +1,11 @@
// A custom hook that builds on useLocation to parse
import React from 'react';
import { useLocation } from 'react-router-dom';
// the query string for you.
export function useQueryParam() {
const { search } = useLocation();
return React.useMemo(() => new URLSearchParams(search), [search]);
}

View File

@ -0,0 +1,14 @@
// Fungsi untuk mengecek apakah suatu string merupakan angka
function isNumeric(value: any) {
return !isNaN(value - parseFloat(value));
}
// Fungsi untuk memvalidasi formula number berderet
export function validateFormula(formula: any) {
for (let i = 0; i < formula.length - 1; i++) {
if (isNumeric(formula[i]?.name) && isNumeric(formula[i + 1]?.name)) {
return false; // Formula tidak valid
}
}
return true; // Formula valid
}

View File

@ -0,0 +1,94 @@
Copyright 2011 The Paytone Project Authors (https://github.com/googlefonts/paytoneFont),
with Reserved Font Names "Paytone" and "Paytone One".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,93 @@
Copyright 2021 The Urbanist Project Authors (https://github.com/coreyhu/Urbanist)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,81 @@
Urbanist Variable Font
======================
This download contains Urbanist as both variable fonts and static fonts.
Urbanist is a variable font with this axis:
wght
This means all the styles are contained in these files:
Urbanist/Urbanist-VariableFont_wght.ttf
Urbanist/Urbanist-Italic-VariableFont_wght.ttf
If your app fully supports variable fonts, you can now pick intermediate styles
that arent available as static fonts. Not all apps support variable fonts, and
in those cases you can use the static font files for Urbanist:
Urbanist/static/Urbanist-Thin.ttf
Urbanist/static/Urbanist-ExtraLight.ttf
Urbanist/static/Urbanist-Light.ttf
Urbanist/static/Urbanist-Regular.ttf
Urbanist/static/Urbanist-Medium.ttf
Urbanist/static/Urbanist-SemiBold.ttf
Urbanist/static/Urbanist-Bold.ttf
Urbanist/static/Urbanist-ExtraBold.ttf
Urbanist/static/Urbanist-Black.ttf
Urbanist/static/Urbanist-ThinItalic.ttf
Urbanist/static/Urbanist-ExtraLightItalic.ttf
Urbanist/static/Urbanist-LightItalic.ttf
Urbanist/static/Urbanist-Italic.ttf
Urbanist/static/Urbanist-MediumItalic.ttf
Urbanist/static/Urbanist-SemiBoldItalic.ttf
Urbanist/static/Urbanist-BoldItalic.ttf
Urbanist/static/Urbanist-ExtraBoldItalic.ttf
Urbanist/static/Urbanist-BlackItalic.ttf
Get started
-----------
1. Install the font files you want to use
2. Use your app's font picker to view the font family and all the
available styles
Learn more about variable fonts
-------------------------------
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
https://variablefonts.typenetwork.com
https://medium.com/variable-fonts
In desktop apps
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
Online
https://developers.google.com/fonts/docs/getting_started
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
Installing fonts
MacOS: https://support.apple.com/en-us/HT201749
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
Android Apps
https://developers.google.com/fonts/docs/android
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
License
-------
Please read the full license text (OFL.txt) to understand the permissions,
restrictions and requirements for usage, redistribution, and modification.
You can use them in your products & projects print or digital,
commercial or otherwise.
This isn't legal advice, please consider consulting a lawyer and see the full
license for all details.

View File

@ -0,0 +1,34 @@
@font-face {
font-family: 'Paytone One';
src: url('./Paytone_One/PaytoneOne-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Urbanist';
src: url('./Urbanist/static/Urbanist-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Urbanist';
src: url('./Urbanist/static/Urbanist-Bold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Urbanist';
src: url('./Urbanist/static/Urbanist-Italic.ttf') format('truetype');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Urbanist';
src: url('./Urbanist/static/Urbanist-BoldItalic.ttf') format('truetype');
font-weight: bold;
font-style: italic;
}

View File

@ -0,0 +1,24 @@
<svg width="514" height="164" viewBox="0 0 514 164" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="101" cy="22" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="101" cy="142" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="21" cy="102" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="141" cy="102" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="193" cy="82" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="313" cy="82" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="253" cy="22" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="253" cy="142" r="20" stroke="#667085" stroke-width="2"/>
<path d="M1 102C1 90.9543 9.9543 82 21 82H141C152.046 82 161 90.9543 161 102C161 113.046 152.046 122 141 122H21C9.9543 122 1 113.046 1 102Z" stroke="#667085" stroke-width="2"/>
<path d="M101 162C89.9543 162 81 153.046 81 142L81 22C81 10.9543 89.9543 2 101 2C112.046 2 121 10.9543 121 22L121 142C121 153.046 112.046 162 101 162Z" stroke="#667085" stroke-width="2"/>
<path d="M7.14214 115.995C-0.668351 108.184 -0.668351 95.5211 7.14214 87.7106L86.7107 8.1421C94.5212 0.331614 107.184 0.331607 114.995 8.14209C122.805 15.9526 122.805 28.6159 114.995 36.4264L35.4264 115.995C27.6159 123.805 14.9526 123.805 7.14214 115.995Z" stroke="#667085" stroke-width="2"/>
<circle cx="453" cy="22" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="453" cy="142" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="373" cy="102" r="20" stroke="#667085" stroke-width="2"/>
<circle cx="493" cy="102" r="20" stroke="#667085" stroke-width="2"/>
<path d="M353 102C353 90.9543 361.954 82 373 82H493C504.046 82 513 90.9543 513 102C513 113.046 504.046 122 493 122H373C361.954 122 353 113.046 353 102Z" stroke="#667085" stroke-width="2"/>
<path d="M453 162C441.954 162 433 153.046 433 142L433 22C433 10.9543 441.954 2 453 2C464.046 2 473 10.9543 473 22L473 142C473 153.046 464.046 162 453 162Z" stroke="#667085" stroke-width="2"/>
<path d="M359.142 115.995C351.332 108.184 351.332 95.5211 359.142 87.7106L438.711 8.1421C446.521 0.331614 459.184 0.331607 466.995 8.14209C474.805 15.9526 474.805 28.6159 466.995 36.4264L387.426 115.995C379.616 123.805 366.953 123.805 359.142 115.995Z" stroke="#667085" stroke-width="2"/>
<circle cx="253" cy="82" r="80" stroke="#667085" stroke-width="2"/>
<circle cx="253" cy="82" r="40" stroke="#667085" stroke-width="2"/>
<line x1="8.74228e-08" y1="1" x2="513" y2="1.00004" stroke="#667085" stroke-width="2"/>
<line x1="-8.74228e-08" y1="163" x2="513" y2="163" stroke="#667085" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url('./ag-grid-theme-builder.css');
@import url('../fonts/fonts.less');
* {
font-family: Urbanist, sans-serif;
}
html {
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth;
}
body {
margin: 0;
}
label {
font-size: 15px !important;
}
.ant-modal-title,
.color-primary,
label {
color: #623c7c !important;
}
.bg-warning {
background-color: #d4940d;
}
.bg-turqoise {
background-color: #c2edf0;
}
.bg-softpurple {
background-color: #d5c7f0;
}
.bg-softcyan {
background-color: #c4dcf0;
}
.bg-secondary {
background-color: #78cbbb;
}
.force-px-2 {
.ant-input-number-input {
padding: 2px 4px !important;
}
padding: 2px 4px !important;
}
.bg-primary {
background-color: #7a41a1 !important;
}
.bg-waiting {
background-color: #6c5dd0;
}
.bg-refunded {
background-color: #d7a29a;
}
a {
text-decoration: none !important;
}

Some files were not shown because too many files have changed in this diff Show More