From 8645fc4c450a7f8bfe5db9914566ec03d1e802e1 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Tue, 4 Mar 2025 11:02:26 +0700 Subject: [PATCH 001/431] feat: add apex charts --- package.json | 2 ++ pnpm-lock.yaml | 78 ++++++++++++++++++++++++++++++++++++++++++ src/boot/components.ts | 2 ++ 3 files changed, 82 insertions(+) diff --git a/package.json b/package.json index bd2467ca..bdc29566 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@quasar/extras": "^1.16.12", "@tato30/vue-pdf": "^1.11.0", "@vuepic/vue-datepicker": "^8.8.1", + "apexcharts": "^4.5.0", "axios": "^1.7.4", "cropperjs": "^1.6.2", "keycloak-js": "^25.0.4", @@ -34,6 +35,7 @@ "udsv": "^0.6.0", "uuid": "^10.0.0", "vue": "^3.4.38", + "vue3-apexcharts": "^1.7.0", "vue-dragscroll": "^4.0.6", "vue-i18n": "^9.14.0", "vue-pdf": "^4.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cba44ed..9543f9bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@vuepic/vue-datepicker': specifier: ^8.8.1 version: 8.8.1(vue@3.4.38(typescript@5.5.4)) + apexcharts: + specifier: ^4.5.0 + version: 4.5.0 axios: specifier: ^1.7.4 version: 1.7.4 @@ -77,6 +80,9 @@ importers: vue-router: specifier: ^4.4.3 version: 4.4.3(vue@3.4.38(typescript@5.5.4)) + vue3-apexcharts: + specifier: ^1.7.0 + version: 1.8.0(apexcharts@4.5.0)(vue@3.4.38(typescript@5.5.4)) devDependencies: '@faker-js/faker': specifier: ^9.3.0 @@ -725,6 +731,31 @@ packages: '@socket.io/component-emitter@3.1.1': resolution: {integrity: sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==} + '@svgdotjs/svg.draggable.js@3.0.6': + resolution: {integrity: sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==} + peerDependencies: + '@svgdotjs/svg.js': ^3.2.4 + + '@svgdotjs/svg.filter.js@3.0.9': + resolution: {integrity: sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==} + engines: {node: '>= 0.8.0'} + + '@svgdotjs/svg.js@3.2.4': + resolution: {integrity: sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==} + + '@svgdotjs/svg.resize.js@2.0.5': + resolution: {integrity: sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==} + engines: {node: '>= 14.18'} + peerDependencies: + '@svgdotjs/svg.js': ^3.2.4 + '@svgdotjs/svg.select.js': ^4.0.1 + + '@svgdotjs/svg.select.js@4.0.2': + resolution: {integrity: sha512-5gWdrvoQX3keo03SCmgaBbD+kFftq0F/f2bzCbNnpkkvW6tk4rl4MakORzFuNjvXPWwB4az9GwuvVxQVnjaK2g==} + engines: {node: '>= 14.18'} + peerDependencies: + '@svgdotjs/svg.js': ^3.2.4 + '@tato30/vue-pdf@1.11.0': resolution: {integrity: sha512-GQNfVqq8if6/tezgcW1E0iU7kO5VfzMihdU6sfRnw0ewDYcP43JFt3r2fmDggyAY5lu7ArEwCFvlWi7WbvleCw==} peerDependencies: @@ -979,6 +1010,9 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + '@yr/monotone-cubic-spline@1.0.3': + resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1052,6 +1086,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apexcharts@4.5.0: + resolution: {integrity: sha512-E7ZkrVqPNBUWy/Rmg8DEIqHNBmElzICE/oxOX5Ekvs2ICQUOK/VkEkMH09JGJu+O/EA0NL31hxlmF+wrwrSLaQ==} + aproba@1.2.0: resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} @@ -3569,6 +3606,12 @@ packages: peerDependencies: vue: ^3.2.0 + vue3-apexcharts@1.8.0: + resolution: {integrity: sha512-5tSD4mXTBbIJ9ir+58qHE6oNtIe0RNgqIRYMKpcsIaxkKtwUww4JhvPkpUFlmiW4OJbbdklgjleXq1lfcM4gdA==} + peerDependencies: + apexcharts: '>=4.0.0' + vue: '>=3.0.0' + vue@3.4.38: resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==} peerDependencies: @@ -4186,6 +4229,25 @@ snapshots: '@socket.io/component-emitter@3.1.1': {} + '@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.4)': + dependencies: + '@svgdotjs/svg.js': 3.2.4 + + '@svgdotjs/svg.filter.js@3.0.9': + dependencies: + '@svgdotjs/svg.js': 3.2.4 + + '@svgdotjs/svg.js@3.2.4': {} + + '@svgdotjs/svg.resize.js@2.0.5(@svgdotjs/svg.js@3.2.4)(@svgdotjs/svg.select.js@4.0.2(@svgdotjs/svg.js@3.2.4))': + dependencies: + '@svgdotjs/svg.js': 3.2.4 + '@svgdotjs/svg.select.js': 4.0.2(@svgdotjs/svg.js@3.2.4) + + '@svgdotjs/svg.select.js@4.0.2(@svgdotjs/svg.js@3.2.4)': + dependencies: + '@svgdotjs/svg.js': 3.2.4 + '@tato30/vue-pdf@1.11.0(vue@3.4.38(typescript@5.5.4))': dependencies: pdfjs-dist: 4.5.136 @@ -4549,6 +4611,8 @@ snapshots: '@xtuc/long@4.2.2': {} + '@yr/monotone-cubic-spline@1.0.3': {} + abbrev@1.1.1: optional: true @@ -4625,6 +4689,15 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + apexcharts@4.5.0: + dependencies: + '@svgdotjs/svg.draggable.js': 3.0.6(@svgdotjs/svg.js@3.2.4) + '@svgdotjs/svg.filter.js': 3.0.9 + '@svgdotjs/svg.js': 3.2.4 + '@svgdotjs/svg.resize.js': 2.0.5(@svgdotjs/svg.js@3.2.4)(@svgdotjs/svg.select.js@4.0.2(@svgdotjs/svg.js@3.2.4)) + '@svgdotjs/svg.select.js': 4.0.2(@svgdotjs/svg.js@3.2.4) + '@yr/monotone-cubic-spline': 1.0.3 + aproba@1.2.0: {} aproba@2.0.0: @@ -7513,6 +7586,11 @@ snapshots: '@vue/devtools-api': 6.6.3 vue: 3.4.38(typescript@5.5.4) + vue3-apexcharts@1.8.0(apexcharts@4.5.0)(vue@3.4.38(typescript@5.5.4)): + dependencies: + apexcharts: 4.5.0 + vue: 3.4.38(typescript@5.5.4) + vue@3.4.38(typescript@5.5.4): dependencies: '@vue/compiler-dom': 3.4.38 diff --git a/src/boot/components.ts b/src/boot/components.ts index 0987be70..9983cfed 100644 --- a/src/boot/components.ts +++ b/src/boot/components.ts @@ -4,10 +4,12 @@ import '@vuepic/vue-datepicker/dist/main.css'; import GlobalDialog from 'components/GlobalDialog.vue'; import GlobalLoading from 'components/GlobalLoading.vue'; import VueDragscroll from 'vue-dragscroll'; +import VueApexCharts from 'vue3-apexcharts'; export default boot(({ app }) => { app.component('global-dialog', GlobalDialog); app.component('global-loading', GlobalLoading); app.component('VueDatePicker', VueDatePicker); + app.component('VueApexCharts', VueApexCharts); app.use(VueDragscroll); }); From 6f256cc2543229701f1dfcf50de103edd1180b5b Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Tue, 4 Mar 2025 11:06:00 +0700 Subject: [PATCH 002/431] refactor: show menu dash board --- src/layouts/DrawerComponent.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layouts/DrawerComponent.vue b/src/layouts/DrawerComponent.vue index dde0f1ed..4a3928f0 100644 --- a/src/layouts/DrawerComponent.vue +++ b/src/layouts/DrawerComponent.vue @@ -171,7 +171,6 @@ onMounted(async () => { { label: 'menu.overall', icon: 'mdi-monitor-dashboard', - disabled: true, children: [ { label: 'report', route: '' }, { label: 'dashboard', route: '' }, From 4be37ad2688f22ae20ecd402fd24218324bf8196 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Tue, 4 Mar 2025 11:22:31 +0700 Subject: [PATCH 003/431] feat: add route report and dashboard --- src/layouts/DrawerComponent.vue | 4 ++-- src/router/routes.ts | 32 +++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/layouts/DrawerComponent.vue b/src/layouts/DrawerComponent.vue index 4a3928f0..5e07ca3a 100644 --- a/src/layouts/DrawerComponent.vue +++ b/src/layouts/DrawerComponent.vue @@ -172,8 +172,8 @@ onMounted(async () => { label: 'menu.overall', icon: 'mdi-monitor-dashboard', children: [ - { label: 'report', route: '' }, - { label: 'dashboard', route: '' }, + { label: 'report', route: '/report' }, + { label: 'dashboard', route: '/dash-board' }, ], }, ]; diff --git a/src/router/routes.ts b/src/router/routes.ts index 785f6601..6d752495 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -75,16 +75,16 @@ const routes: RouteRecordRaw[] = [ name: 'productAndService', component: () => import('pages/04_product-service/MainPage.vue'), }, - { - path: '/quotation', - name: 'Quotation', - component: () => import('pages/05_quotation/MainPage.vue'), - }, { path: '/workflow', name: 'Workflow', component: () => import('pages/04_flow-managment/MainPage.vue'), }, + { + path: '/quotation', + name: 'Quotation', + component: () => import('pages/05_quotation/MainPage.vue'), + }, { path: '/document-management', name: 'document-management', @@ -105,15 +105,20 @@ const routes: RouteRecordRaw[] = [ name: 'TaskOrder', component: () => import('pages/09_task-order/MainPage.vue'), }, + { + path: '/invoice', + name: '/Invoice', + component: () => import('pages/10_invoice/MainPage.vue'), + }, { path: '/credit-note', name: 'CreditNote', component: () => import('pages/11_credit-note/MainPage.vue'), }, { - path: '/invoice', - name: '/Invoice', - component: () => import('pages/10_invoice/MainPage.vue'), + path: '/debit-note', + name: 'debitNote', + component: () => import('pages/12_debit-note/MainPage.vue'), }, { path: '/receipt', @@ -121,9 +126,14 @@ const routes: RouteRecordRaw[] = [ component: () => import('pages/13_receipt/MainPage.vue'), }, { - path: '/debit-note', - name: 'debitNote', - component: () => import('pages/12_debit-note/MainPage.vue'), + path: '/report', + name: 'report', + component: () => import('pages/14_report/MainPage.vue'), + }, + { + path: '/dash-board', + name: 'dashBoard', + component: () => import('pages/15_dash-board/MainPage.vue'), }, ], }, From 780a2629798afacbf59fda2654ed5596b806c33e Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Tue, 4 Mar 2025 11:25:13 +0700 Subject: [PATCH 004/431] feat: add directory report and dashboard --- src/pages/14_report/MainPage.vue | 11 +++++++++++ src/pages/15_dash-board/MainPage.vue | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/pages/14_report/MainPage.vue create mode 100644 src/pages/15_dash-board/MainPage.vue diff --git a/src/pages/14_report/MainPage.vue b/src/pages/14_report/MainPage.vue new file mode 100644 index 00000000..081ecf2e --- /dev/null +++ b/src/pages/14_report/MainPage.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/pages/15_dash-board/MainPage.vue b/src/pages/15_dash-board/MainPage.vue new file mode 100644 index 00000000..081ecf2e --- /dev/null +++ b/src/pages/15_dash-board/MainPage.vue @@ -0,0 +1,11 @@ + + + From b892924a18c4be9c2f4b4b7be6de0d7c0eed8816 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Tue, 4 Mar 2025 13:31:35 +0700 Subject: [PATCH 005/431] feat: add title --- src/pages/14_report/MainPage.vue | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/pages/14_report/MainPage.vue b/src/pages/14_report/MainPage.vue index 081ecf2e..7f3a32b7 100644 --- a/src/pages/14_report/MainPage.vue +++ b/src/pages/14_report/MainPage.vue @@ -1,11 +1,24 @@ - + + From ff95d207d2eb9a7c63f21cc88be31aa1131ce895 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Thu, 31 Oct 2024 17:53:10 +0700 Subject: [PATCH 006/431] feat: add notification type --- src/stores/notification/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/stores/notification/index.ts b/src/stores/notification/index.ts index 1243de88..5aa43fe5 100644 --- a/src/stores/notification/index.ts +++ b/src/stores/notification/index.ts @@ -4,7 +4,12 @@ import { api } from 'src/boot/axios'; import { PaginationResult } from 'src/types'; import { createDataRefBase } from '../utils'; -export type Notification = {}; +export type Notification = { + title: string; + detail: string; + receiverId?: string; + groupId?: string[]; +}; export const useNotification = defineStore('noti-store', () => { const state = createDataRefBase(); From 838490d90d974258a2016f780987191bfed3afa7 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Thu, 31 Oct 2024 17:56:46 +0700 Subject: [PATCH 007/431] fix: get possibly undefined type --- src/stores/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/utils/index.ts b/src/stores/utils/index.ts index 109bce33..84f504a7 100644 --- a/src/stores/utils/index.ts +++ b/src/stores/utils/index.ts @@ -532,7 +532,7 @@ export function createDataRefBase( defaultPageSize = 30, ) { return { - data: ref(), + data: ref([]), page: ref(defaultPage), pageMax: ref(defaultPageMax), pageSize: ref(defaultPageSize), From 74d80a163de55a936cd39f0acb53fb38993ff38f Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Thu, 31 Oct 2024 17:59:51 +0700 Subject: [PATCH 008/431] feat: fetch notification from api --- src/layouts/MainLayout.vue | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 168b895d..edee3b05 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -17,6 +17,7 @@ import { useConfigStore } from 'src/stores/config'; import { useNavigator } from 'src/stores/navigator'; import { initLang, initTheme, Lang, setLang } from 'src/utils/ui'; import { baseUrl } from 'stores/utils'; +import { useNotification } from 'src/stores/notification'; const useMyBranch = useMyBranchStore(); const { fetchListMyBranch } = useMyBranch; @@ -37,8 +38,11 @@ interface Notification { const $q = useQuasar(); const loaderStore = useLoader(); const navigatorStore = useNavigator(); +const notificationStore = useNotification(); const configStore = useConfigStore(); +const { data: notificationData } = storeToRefs(notificationStore); + const { visible } = storeToRefs(loaderStore); const { t } = useI18n({ useScope: 'global' }); const userStore = useUserStore(); @@ -129,6 +133,14 @@ onMounted(async () => { await configStore.getConfig(); + { + const noti = await notificationStore.getNotificationList(); + + if (noti) { + notificationData.value = noti.result; + } + } + await fetchListMyBranch(getUserId() ?? ''); leftDrawerOpen.value = $q.screen.gt.xs ? true : false; @@ -324,10 +336,10 @@ onMounted(async () => { clickable class="q-py-sm" v-ripple - v-for="item in !filterUnread - ? notification - : notification.filter((v) => !v.read)" - :key="item.id" + v-for="(item, i) in !filterUnread + ? notificationData + : notificationData" + :key="i" > { {{ item.title }} - {{ item.content }} + {{ item.detail }} @@ -356,7 +368,7 @@ onMounted(async () => { :delay="1000" :offset="[10, 10]" > - {{ item.content }} + {{ item.detail }} From 9c04b209926f3d7b2837d1f0ad70c5a3d0624b97 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Fri, 8 Nov 2024 09:23:53 +0700 Subject: [PATCH 009/431] fix: wrong variable name --- src/stores/notification/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/stores/notification/index.ts b/src/stores/notification/index.ts index 5aa43fe5..a1b2593c 100644 --- a/src/stores/notification/index.ts +++ b/src/stores/notification/index.ts @@ -33,9 +33,12 @@ export const useNotification = defineStore('noti-store', () => { return res.data; } - async function updateNotification(paymentId: string, payload: Notification) { + async function updateNotification( + notificationId: string, + payload: Notification, + ) { const res = await api.put( - `/notification/${paymentId}`, + `/notification/${notificationId}`, payload, ); if (res.status >= 400) return null; From 80cb67323b01827816f81de169ad31277a5c3722 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:10:45 +0700 Subject: [PATCH 010/431] refactor: update data receive from backend --- src/layouts/MainLayout.vue | 43 ++++++-------------------------- src/stores/notification/index.ts | 5 ++-- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index edee3b05..be00b93f 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -18,6 +18,7 @@ import { useNavigator } from 'src/stores/navigator'; import { initLang, initTheme, Lang, setLang } from 'src/utils/ui'; import { baseUrl } from 'stores/utils'; import { useNotification } from 'src/stores/notification'; +import moment from 'moment'; const useMyBranch = useMyBranchStore(); const { fetchListMyBranch } = useMyBranch; @@ -28,13 +29,6 @@ interface NotificationButton { active: boolean; } -interface Notification { - id: string; - title: string; - content: string; - read: boolean; -} - const $q = useQuasar(); const loaderStore = useLoader(); const navigatorStore = useNavigator(); @@ -82,20 +76,6 @@ const notiMenu = ref([ active: false, }, ]); -const notification = ref([ - { - id: '1', - title: 'Unread', - content: 'Unread', - read: false, - }, - { - id: '3', - title: 'Read', - content: 'Already read', - read: true, - }, -]); function setActive(button: NotificationButton) { notiMenu.value = notiMenu.value.map((current) => ({ @@ -290,7 +270,7 @@ onMounted(async () => {
- + { From ace2aec85b4747ae7148e10b39f687f3e87567e6 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Wed, 5 Mar 2025 13:28:50 +0700 Subject: [PATCH 011/431] feat: unread count --- src/layouts/MainLayout.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index be00b93f..11bb48e7 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -19,6 +19,7 @@ import { initLang, initTheme, Lang, setLang } from 'src/utils/ui'; import { baseUrl } from 'stores/utils'; import { useNotification } from 'src/stores/notification'; import moment from 'moment'; +import { computed } from 'vue'; const useMyBranch = useMyBranchStore(); const { fetchListMyBranch } = useMyBranch; @@ -47,7 +48,9 @@ const leftDrawerOpen = ref(false); const leftDrawerMini = ref(false); const filterUnread = ref(false); -const unread = ref(1); +const unread = computed( + () => notificationData.value.filter((v) => !v.read).length || 0, +); // const filterRole = ref(); const userImage = ref(); const userGender = ref(''); @@ -316,9 +319,9 @@ onMounted(async () => { clickable class="q-py-sm" v-ripple - v-for="(item, i) in !filterUnread + v-for="(item, i) in filterUnread ? notificationData.filter((v) => !v.read) - : notificationData.filter((v) => v.read)" + : notificationData" @click="notificationStore.getNotification(item.id)" :key="i" > From ac0d3649d4c6031457e8a77639bb1933eabaa074 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:26:22 +0700 Subject: [PATCH 012/431] chore: clean --- src/layouts/MainLayout.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 11bb48e7..f1f2f470 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -344,7 +344,6 @@ onMounted(async () => {
{{ moment(item.createdAt).fromNow() }} - 5 s Date: Wed, 5 Mar 2025 16:07:37 +0700 Subject: [PATCH 013/431] feat: add stores of report --- src/stores/report/index.ts | 71 ++++++++++++++++++++++++++++++++++++++ src/stores/report/types.ts | 41 ++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 src/stores/report/index.ts create mode 100644 src/stores/report/types.ts diff --git a/src/stores/report/index.ts b/src/stores/report/index.ts new file mode 100644 index 00000000..0f0ab14f --- /dev/null +++ b/src/stores/report/index.ts @@ -0,0 +1,71 @@ +import { ref } from 'vue'; +import { defineStore } from 'pinia'; +import { Pagination, Status } from '../types'; +import { api } from 'src/boot/axios'; +import { Report, ReportProduct, ReportQuotation, ReportSale } from './types'; + +const ENDPOINT = 'report'; + +export async function getReportQuotation() { + const res = await api.get(`/${ENDPOINT}/quotation`); + if (res.status < 400) { + res.data; + return res.data; + } + return null; +} + +export async function getReportInvoice() { + const res = await api.get(`/${ENDPOINT}/invoice`); + if (res.status < 400) { + return res.data; + } + return null; +} + +export async function getReportReceipt() { + const res = await api.get(`/${ENDPOINT}/receipt`); + if (res.status < 400) { + return res.data; + } + return null; +} + +export async function getReportSale() { + const res = await api.get(`/${ENDPOINT}/sale`); + + if (res.status < 400) { + return res.data; + } + return null; +} + +export async function getReportProduct() { + const res = await api.get(`/${ENDPOINT}/Product`); + if (res.status < 400) { + return res.data; + } + return null; +} + +export const useReportStore = defineStore('report-store', () => { + const dataReportQuotation = ref([]); + const dataReportInvoice = ref([]); + const dataReportReceipt = ref([]); + const dataReportSale = ref(); + const dataReportProduct = ref([]); + + return { + dataReportQuotation, + dataReportInvoice, + dataReportReceipt, + dataReportSale, + dataReportProduct, + + getReportQuotation, + getReportInvoice, + getReportReceipt, + getReportSale, + getReportProduct, + }; +}); diff --git a/src/stores/report/types.ts b/src/stores/report/types.ts new file mode 100644 index 00000000..92215ae3 --- /dev/null +++ b/src/stores/report/types.ts @@ -0,0 +1,41 @@ +import { QuotationStatus } from 'src/stores/quotations/types'; +import { ProductGroup } from '../product-service/types'; +import { User } from '../user'; +import { CustomerBranch } from '../customer'; + +export type ReportQuotation = { + updatedAt: Date | null; + createdAt: Date | null; + status: QuotationStatus; + code: string; +}; + +export enum Status { + PaymentInProcess = 'PaymentInProcess', + PaymentSuccess = 'PaymentSuccess', + PaymentWait = 'PaymentWait', + PaymentRetry = 'PaymentRetry', +} + +// use with Invoice and Receipt +export type Report = { + createdAt: Date | null; + status: Status; + code: string; +}; + +export type ReportProduct = { + updatedAt: Date | null; + createdAt: Date | null; + order: number; + did: number; + sale: number; + name: string; + code: string; +}; + +export type ReportSale = { + byCustomer: (Omit & { _count: number })[]; + bySale: (User & { _count: number })[]; + byProductGroup: (Omit & { _count: number })[]; +}; From 2e32ad28d79ab5543e1f43d9a26b3c89e56cecbf Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Wed, 5 Mar 2025 16:36:54 +0700 Subject: [PATCH 014/431] feat: add i18n --- src/i18n/eng.ts | 40 ++++++++++++++++++++++++++++++++++++++++ src/i18n/tha.ts | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts index c822240c..3b7d494a 100644 --- a/src/i18n/eng.ts +++ b/src/i18n/eng.ts @@ -1336,4 +1336,44 @@ export default { Succeed: 'Completed', }, }, + + report: { + report: { + title: 'Report', + view: { + Document: 'Document Status Report', + Invoice: 'Payment Report', + Product: 'Product and Service Report', + admin: { + Product: 'Product Movement Report', + Sale: 'Sales Summary Report', + }, + }, + document: { + code: 'Code', + status: 'Quotation Status', + createAt: 'Created Date', + updateAt: 'Updated Date', + }, + table: { + code: 'Code', + status: 'Status', + createAt: 'Created Date', + }, + product: { + did: 'Processed Quantity', + sale: 'Sold Quantity', + name: 'Product Name', + code: 'Product Code', + }, + sale: { + byCustomer: 'Sales by Branch', + byProductGroup: 'Sales by Product Category', + bySale: 'Sales by Salesperson', + code: 'Code', + name: 'Name', + count: 'Sold Quantity', + }, + }, + }, }; diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts index c3205cbc..8657680f 100644 --- a/src/i18n/tha.ts +++ b/src/i18n/tha.ts @@ -1315,4 +1315,43 @@ export default { Succeed: 'เสร็จสิ้น', }, }, + + report: { + title: 'รายงาน', + view: { + Document: 'รายงานสถานะเอกสาร', + Invoice: 'รายงานการชำระเงิน', + Product: 'รายงานสินค้าและบริการ', + admin: { + Product: 'รายงานการเคลื่อนไหวของสินค้า', + Sale: 'รายงานสรุปยอดขาย', + }, + }, + document: { + code: 'รหัส', + status: 'สถานะใบเสนอราคา', + createAt: 'วันที่สร้าง', + updateAt: 'วันที่แก้ไข', + }, + table: { + code: 'รหัส', + status: 'สถานะ', + createAt: 'วันที่สร้าง', + }, + product: { + did: 'จำนวนที่ออกไปดำเนินการ', + sale: 'จำนวนที่ขายออกไป', + name: 'ชื่อสินค้า', + code: 'รหัสสินค้า', + }, + + sale: { + byCustomer: 'ยอดขายตามสาขา', + byProductGroup: 'ยอดขายตามประเภทสินค้า', + bySale: 'ยอดขายตามพนักงานขาย', + code: 'รหัส', + name: 'ชื่อ', + count: 'จำนวนที่ขาย', + }, + }, }; From ba10fc960932860f594ebc9969654b462443c657 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Wed, 5 Mar 2025 16:37:53 +0700 Subject: [PATCH 015/431] feat: set constant variable --- src/pages/14_report/constants.ts | 153 +++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/pages/14_report/constants.ts diff --git a/src/pages/14_report/constants.ts b/src/pages/14_report/constants.ts new file mode 100644 index 00000000..fc2b9883 --- /dev/null +++ b/src/pages/14_report/constants.ts @@ -0,0 +1,153 @@ +import { QTableProps } from 'quasar'; +import { Invoice, Receipt } from 'src/stores/payment/types'; +import { + Report, + ReportProduct, + ReportQuotation, +} from 'src/stores/report/types'; +import { formatNumberDecimal } from 'src/stores/utils'; +import { dateFormatJS } from 'src/utils/datetime'; + +export enum ViewMode { + Document = 'document', + Invoice = 'invoice', + Receipt = 'receipt', + Product = 'product', + Sale = 'sale', +} + +type ColumnsSale = { + code: string; + name: string; + _count: number; +}; + +type ColumnsBySale = ColumnsSale & { + url?: string; + id?: string; + gender?: string; +}; + +export const colReportQuotation = [ + { + name: 'code', + align: 'center', + label: 'report.document.code', + field: (data: ReportQuotation) => data.code, + }, + { + name: '#status', + align: 'center', + label: 'report.document.status', + field: '', + }, + { + name: 'createAt', + align: 'center', + label: 'report.document.createAt', + field: (data: ReportQuotation) => dateFormatJS({ date: data.createdAt }), + }, + { + name: 'updateAt', + align: 'center', + label: 'report.document.updateAt', + field: (data: ReportQuotation) => dateFormatJS({ date: data.updatedAt }), + }, +] as const satisfies QTableProps['columns']; + +export const colReport = [ + { + name: 'code', + align: 'center', + label: 'report.table.code', + field: (data: Report) => data.code, + }, + { + name: '#status', + align: 'center', + label: 'report.table.status', + field: '', + }, + { + name: 'createAt', + align: 'center', + label: 'report.table.createAt', + field: (data: Report) => dateFormatJS({ date: data.createdAt }), + }, +] as const satisfies QTableProps['columns']; + +export const colReportProduct = [ + { + name: 'code', + align: 'center', + label: 'report.product.code', + field: (data: ReportProduct) => data.code, + }, + { + name: 'name', + align: 'center', + label: 'report.product.name', + field: (data: ReportProduct) => data.name, + }, + { + name: 'sale', + align: 'center', + label: 'report.product.sale', + field: (data: ReportProduct) => data.sale, + }, + { + name: 'did', + align: 'center', + label: 'report.product.did', + field: (data: ReportProduct) => data.did, + }, +] as const satisfies QTableProps['columns']; + +export const colReportSale = [ + { + name: 'code', + align: 'left', + label: 'report.sale.code', + field: (data: ColumnsSale) => data.code, + }, + { + name: '#name', + align: 'left', + label: 'report.sale.name', + field: '', + }, + { + name: 'count', + align: 'center', + label: 'report.sale.count', + field: (data: ColumnsSale) => data._count, + }, +] as const satisfies QTableProps['columns']; + +export const colReportBySale = [ + { + name: 'code', + align: 'left', + label: 'report.sale.code', + field: (data: ColumnsBySale) => data.code, + }, + { + name: '#name', + align: 'left', + label: 'report.sale.name', + field: '', + }, + { + name: 'count', + align: 'center', + label: 'report.sale.count', + field: (data: ColumnsBySale) => data._count, + }, +] as const satisfies QTableProps['columns']; + +export const pageTabs = [ + { label: 'Document', value: ViewMode.Document, by: ['user'] }, + { label: 'Invoice', value: ViewMode.Invoice, by: ['user'] }, + { label: 'Product', value: ViewMode.Product, by: ['user', 'admin'] }, + { label: 'Sale', value: ViewMode.Sale, by: ['admin'] }, +]; From 7f14e6be46316f726324fd75643b8ea6c2288b19 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Wed, 5 Mar 2025 16:39:20 +0700 Subject: [PATCH 016/431] feat: new Table and expansion --- src/components/14_report/Expansion.vue | 29 ++++++++++ src/pages/14_report/Table/TableReport.vue | 64 +++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/components/14_report/Expansion.vue create mode 100644 src/pages/14_report/Table/TableReport.vue diff --git a/src/components/14_report/Expansion.vue b/src/components/14_report/Expansion.vue new file mode 100644 index 00000000..581c677b --- /dev/null +++ b/src/components/14_report/Expansion.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/pages/14_report/Table/TableReport.vue b/src/pages/14_report/Table/TableReport.vue new file mode 100644 index 00000000..29214126 --- /dev/null +++ b/src/pages/14_report/Table/TableReport.vue @@ -0,0 +1,64 @@ + + From 724f71ae4f2a5ab6fb1f4314a030210e8081b1fa Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Wed, 5 Mar 2025 16:40:47 +0700 Subject: [PATCH 017/431] refactor: import and bind value --- src/pages/14_report/MainPage.vue | 346 ++++++++++++++++++++++++++++++- 1 file changed, 342 insertions(+), 4 deletions(-) diff --git a/src/pages/14_report/MainPage.vue b/src/pages/14_report/MainPage.vue index 7f3a32b7..b8464197 100644 --- a/src/pages/14_report/MainPage.vue +++ b/src/pages/14_report/MainPage.vue @@ -1,24 +1,362 @@ From 49d860a10816b180fd276a3c7037fec33e3e2b20 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Wed, 5 Mar 2025 16:52:36 +0700 Subject: [PATCH 018/431] refactor: handle roles executive --- src/i18n/eng.ts | 5 +---- src/i18n/tha.ts | 5 +---- src/pages/14_report/MainPage.vue | 17 ++++++++--------- src/pages/14_report/constants.ts | 2 +- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts index 3b7d494a..51e53bf9 100644 --- a/src/i18n/eng.ts +++ b/src/i18n/eng.ts @@ -1344,10 +1344,7 @@ export default { Document: 'Document Status Report', Invoice: 'Payment Report', Product: 'Product and Service Report', - admin: { - Product: 'Product Movement Report', - Sale: 'Sales Summary Report', - }, + Sale: 'Sales Summary Report', }, document: { code: 'Code', diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts index 8657680f..7d7b561f 100644 --- a/src/i18n/tha.ts +++ b/src/i18n/tha.ts @@ -1322,10 +1322,7 @@ export default { Document: 'รายงานสถานะเอกสาร', Invoice: 'รายงานการชำระเงิน', Product: 'รายงานสินค้าและบริการ', - admin: { - Product: 'รายงานการเคลื่อนไหวของสินค้า', - Sale: 'รายงานสรุปยอดขาย', - }, + Sale: 'รายงานสรุปยอดขาย', }, document: { code: 'รหัส', diff --git a/src/pages/14_report/MainPage.vue b/src/pages/14_report/MainPage.vue index b8464197..9c72fdee 100644 --- a/src/pages/14_report/MainPage.vue +++ b/src/pages/14_report/MainPage.vue @@ -62,24 +62,23 @@ onMounted(async () => { navigatorStore.current.path = [{ text: '' }]; }); -const isAdmin = computed(() => { +const isExecutive = computed(() => { const roles = userRoles.value; - - return roles.includes('head_of_admin') || roles.includes('head_of_account'); + return roles.includes('executive'); }); const filteredTabs = computed(() => { return pageTabs.filter((tab) => { - if (isAdmin.value) { - return !(tab.by.length === 1 && tab.by.includes('user')); - } else { - return !(tab.by.length === 1 && tab.by.includes('admin')); + if (!isExecutive.value) { + return !tab.by.includes('admin'); } + + return true; }); }); const pageState = reactive({ - currentTab: isAdmin.value ? ViewMode.Product : ViewMode.Document, + currentTab: isExecutive.value ? ViewMode.Product : ViewMode.Document, }); async function fetchReportTab() { @@ -152,7 +151,7 @@ watch([() => pageState.currentTab], async () => { : 'app-text-muted' " > - {{ $t(`report.view${isAdmin ? '.admin' : ''}.${tab.label}`) }} + {{ $t(`report.view.${tab.label}`) }} diff --git a/src/pages/14_report/constants.ts b/src/pages/14_report/constants.ts index fc2b9883..dd4af0e7 100644 --- a/src/pages/14_report/constants.ts +++ b/src/pages/14_report/constants.ts @@ -148,6 +148,6 @@ export const colReportBySale = [ export const pageTabs = [ { label: 'Document', value: ViewMode.Document, by: ['user'] }, { label: 'Invoice', value: ViewMode.Invoice, by: ['user'] }, - { label: 'Product', value: ViewMode.Product, by: ['user', 'admin'] }, + { label: 'Product', value: ViewMode.Product, by: ['user'] }, { label: 'Sale', value: ViewMode.Sale, by: ['admin'] }, ]; From 8023de14378a1e9c4f337420c9c21b772a00ded5 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Tue, 4 Mar 2025 13:39:48 +0700 Subject: [PATCH 019/431] feat: add title --- src/pages/15_dash-board/MainPage.vue | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/15_dash-board/MainPage.vue b/src/pages/15_dash-board/MainPage.vue index 081ecf2e..bd02a8aa 100644 --- a/src/pages/15_dash-board/MainPage.vue +++ b/src/pages/15_dash-board/MainPage.vue @@ -1,11 +1,20 @@ From 5ec7012119b961a7e0d75e03d4d33715554fa2e7 Mon Sep 17 00:00:00 2001 From: puriphatt Date: Wed, 5 Mar 2025 14:11:06 +0700 Subject: [PATCH 020/431] feat: add dashboard localization for English and Thai --- src/i18n/eng.ts | 33 ++++++++++++++++++++++++++++++++- src/i18n/tha.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts index 51e53bf9..aa6cedb5 100644 --- a/src/i18n/eng.ts +++ b/src/i18n/eng.ts @@ -1336,7 +1336,6 @@ export default { Succeed: 'Completed', }, }, - report: { report: { title: 'Report', @@ -1373,4 +1372,36 @@ export default { }, }, }, + dashboard: { + title: 'Dashboard', + newComer: 'New Interested Party', + openQuotation: 'Open Quotation', + paidSales: 'Paid Sales', + pendingSales: 'Pending Sales', + + receipt: { + title: 'Tax Invoice Summary', + caption: 'By Total Amount', + total: 'Total Amount', + paid: 'Partially & Fully Paid', + pending: 'Pending Payment', + cancel: 'Cancelled', + }, + opportunity: { + title: 'Sales Opportunity Screening', + caption: 'By Sales Opportunity Status', + }, + quotation: { + title: 'Quotations & Sales Orders', + caption: 'By Document Status', + waitCustomer: 'Waiting for Customer', + inProgress: 'In Progress', + complete: 'Complete', + cancel: 'Cancel', + }, + sales: { + title: 'Top 5 Highest Sales', + caption: 'Based on Tax Invoices', + }, + }, }; diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts index 7d7b561f..8d165a65 100644 --- a/src/i18n/tha.ts +++ b/src/i18n/tha.ts @@ -1351,4 +1351,36 @@ export default { count: 'จำนวนที่ขาย', }, }, + dashboard: { + title: 'Dashboard', + newComer: 'ผู้สนใจใหม่', + openQuotation: 'ใบเสนอราคาที่เปิดอยู่', + paidSales: 'ยอดขายที่ชำระแล้ว', + pendingSales: 'ยอดขายที่รอชำระ', + + receipt: { + title: 'สรุปยอดใบกำกับภาษี', + caption: 'ตามจำนวนเงินรวม', + total: 'ยอดรวมทั้งหมด', + paid: 'ชำระไม่ครบและชำระครบ', + pending: 'รอชำระ', + cancel: 'ยกเลิก', + }, + opportunity: { + title: 'การคัดกรองโอกาสทางการขาย', + caption: 'ตามสถานะโอกาสทางการขาย', + }, + quotation: { + title: 'ใบเสนอราคาและใบสั่งขาย', + caption: 'ตามสถานะเอกสาร', + waitCustomer: 'รอลูกค้าตอบรับ', + inProgress: 'กำลังดำเนินการ', + complete: 'เสร็จสิ้น', + cancel: 'ยกเลิก', + }, + sales: { + title: '5 อันดับยอดขายสูงสุด', + caption: 'ตามใบกำกับภาษี', + }, + }, }; From f168b43b1afdf0e711afa52ac06b0f07815e933c Mon Sep 17 00:00:00 2001 From: puriphatt Date: Wed, 5 Mar 2025 14:11:16 +0700 Subject: [PATCH 021/431] feat: add new chart components for opportunity, quotation status, receipt, and sales --- .../15_dash-board/chart/ChartOpportunity.vue | 58 ++++++++ .../chart/ChartQuotationStatus.vue | 107 ++++++++++++++ .../15_dash-board/chart/ChartReceipt.vue | 131 ++++++++++++++++++ src/pages/15_dash-board/chart/ChartSales.vue | 75 ++++++++++ 4 files changed, 371 insertions(+) create mode 100644 src/pages/15_dash-board/chart/ChartOpportunity.vue create mode 100644 src/pages/15_dash-board/chart/ChartQuotationStatus.vue create mode 100644 src/pages/15_dash-board/chart/ChartReceipt.vue create mode 100644 src/pages/15_dash-board/chart/ChartSales.vue diff --git a/src/pages/15_dash-board/chart/ChartOpportunity.vue b/src/pages/15_dash-board/chart/ChartOpportunity.vue new file mode 100644 index 00000000..23900231 --- /dev/null +++ b/src/pages/15_dash-board/chart/ChartOpportunity.vue @@ -0,0 +1,58 @@ + + diff --git a/src/pages/15_dash-board/chart/ChartQuotationStatus.vue b/src/pages/15_dash-board/chart/ChartQuotationStatus.vue new file mode 100644 index 00000000..8792c6dd --- /dev/null +++ b/src/pages/15_dash-board/chart/ChartQuotationStatus.vue @@ -0,0 +1,107 @@ + + diff --git a/src/pages/15_dash-board/chart/ChartReceipt.vue b/src/pages/15_dash-board/chart/ChartReceipt.vue new file mode 100644 index 00000000..b48db65a --- /dev/null +++ b/src/pages/15_dash-board/chart/ChartReceipt.vue @@ -0,0 +1,131 @@ + + diff --git a/src/pages/15_dash-board/chart/ChartSales.vue b/src/pages/15_dash-board/chart/ChartSales.vue new file mode 100644 index 00000000..2cafbef2 --- /dev/null +++ b/src/pages/15_dash-board/chart/ChartSales.vue @@ -0,0 +1,75 @@ + + From 1c9874a9e792c4418b0de178297f1ceafa69356f Mon Sep 17 00:00:00 2001 From: puriphatt Date: Wed, 5 Mar 2025 14:11:20 +0700 Subject: [PATCH 022/431] feat: enhance dashboard with new chart components and role/year selection --- src/pages/15_dash-board/MainPage.vue | 137 ++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 4 deletions(-) diff --git a/src/pages/15_dash-board/MainPage.vue b/src/pages/15_dash-board/MainPage.vue index bd02a8aa..0a82c3a7 100644 --- a/src/pages/15_dash-board/MainPage.vue +++ b/src/pages/15_dash-board/MainPage.vue @@ -1,20 +1,149 @@ - + From 9a196b7077b886caf66000da47b03c18b4162dc0 Mon Sep 17 00:00:00 2001 From: puriphatt Date: Wed, 5 Mar 2025 16:00:14 +0700 Subject: [PATCH 023/431] feat: integrate quotation statistics into dashboard and adjust layout for responsiveness --- src/pages/15_dash-board/MainPage.vue | 34 ++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/pages/15_dash-board/MainPage.vue b/src/pages/15_dash-board/MainPage.vue index 0a82c3a7..58ca2bd4 100644 --- a/src/pages/15_dash-board/MainPage.vue +++ b/src/pages/15_dash-board/MainPage.vue @@ -12,9 +12,13 @@ import ChartSales from './chart/ChartSales.vue'; // NOTE: Stores & Type import { useNavigator } from 'src/stores/navigator'; +import { useQuotationStore } from 'src/stores/quotations'; +import { storeToRefs } from 'pinia'; // NOTE: Variable const navigatorStore = useNavigator(); +const quotationStore = useQuotationStore(); +const { stats: quotationStats } = storeToRefs(quotationStore); const state = reactive({ role: 'admin', @@ -36,6 +40,12 @@ const option = reactive({ onMounted(async () => { navigatorStore.current.title = 'dashboard.title'; navigatorStore.current.path = [{ text: '' }]; + + const ret = await quotationStore.getQuotationStats(); + if (ret) { + quotationStats.value = Object.assign(quotationStats.value, ret); + console.log(quotationStats.value); + } });