From 7afdc64e8f819d02991eec0c7b07e97fbeb175e7 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Fri, 15 May 2026 14:01:31 +0700 Subject: [PATCH 1/5] feat: noti checkin --- package.json | 1 + src/App.vue | 9 +++ src/api/socket.ts | 7 +++ src/app.config.ts | 2 + src/stores/socket.ts | 98 +++++++++++++++++++++++++++++++++ src/style/quasar-variables.sass | 73 ++++++++++++------------ 6 files changed, 154 insertions(+), 36 deletions(-) create mode 100644 src/api/socket.ts create mode 100644 src/stores/socket.ts diff --git a/package.json b/package.json index c36ed30..503f2f9 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "quasar": "^2.11.1", "register-service-worker": "^1.7.2", "simple-vue-camera": "^1.1.3", + "socket.io-client": "^4.8.3", "vite-plugin-pwa": "^0.16.7", "vue": "^3.4.15", "vue-router": "^4.1.6", diff --git a/src/App.vue b/src/App.vue index ea76d33..867210e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,3 +1,12 @@ + + diff --git a/src/api/socket.ts b/src/api/socket.ts new file mode 100644 index 0000000..b018666 --- /dev/null +++ b/src/api/socket.ts @@ -0,0 +1,7 @@ +import env from "./index"; + +const socket = `${env.API_URI}/org-socket`; + +export default { + socket, +}; diff --git a/src/app.config.ts b/src/app.config.ts index c6dbe0c..085d287 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -4,6 +4,7 @@ import leave from '@/api/api.checkin' import history from '@/api/api.history' import message from '@/api/api.message' +import socket from '@/api/socket' const API = { /**leave */ @@ -12,6 +13,7 @@ const API = { ...history, /**message */ ...message, + ...socket, } export default { diff --git a/src/stores/socket.ts b/src/stores/socket.ts new file mode 100644 index 0000000..be2ceee --- /dev/null +++ b/src/stores/socket.ts @@ -0,0 +1,98 @@ +import config from '@/app.config' +import { getToken } from '@/plugins/auth' +import { defineStore } from 'pinia' +import { Notify } from 'quasar' +import { io, Socket } from 'socket.io-client' +interface sockeBackup { + message: string + success?: boolean +} + +export const useSocketStore = defineStore('socket', () => { + let socket: Socket + + async function init() { + socket = io(new URL(config.API.socket).origin, { + auth: { token: await getToken() }, + path: '/api/v1/org-socket', + }) + + socket.on('socket-notification', (payload) => { + let body: sockeBackup = JSON.parse(payload) + notifyStatus(body.message, body.success) + }) + } + + function notifyStatus(message: string, success?: boolean) { + Notify.create({ + group: false, + type: success === undefined || success ? 'positive' : 'negative', + message: `${message}`, + position: 'top', + classes: 'custom-notification-top', // ใส่ class ที่เราสร้างไว้ + timeout: success === undefined || success ? 3000 : 0, + actions: + success === undefined || success + ? [] + : [ + { + icon: 'close', + color: 'white', + round: true, + }, + ], + progress: true, + }) + } + + function fnStyleNotiOrg() { + if (document.getElementById('notify-link-style')) return + const style = document.createElement('style') + style.id = 'notify-link-style' + style.textContent = ` + .notify-link { + padding: 4px 8px; + border-radius: 4px; + text-decoration: none; + color: #fff; + border: 1px solid #fff; + transition: all 0.3s; + cursor: pointer; + margin:0 0 0 5px; + } + .notify-link:hover { + background-color: #ffffff; + color: #21BA45; + } + ` + document.head.appendChild(style) + } + ;(window as any).resetOrgPage = (type: string) => { + localStorage.setItem('org_type', type) + window.location.reload() + } + function notifyStatusOrg(type: string, message: string, success?: boolean) { + fnStyleNotiOrg() + Notify.create({ + message: `${message} ${ + type == 'draft' ? 'ไปยังโครงสร้างแบบร่าง' : 'ไปยังโครงสร้างปัจจุบัน' + }`, + html: true, + group: false, + type: success === undefined || success ? 'positive' : 'negative', + position: 'top', + timeout: 0, + actions: [ + { + icon: 'close', + color: 'white', + round: true, + }, + ], + }) + } + + init() + + return {} +}) diff --git a/src/style/quasar-variables.sass b/src/style/quasar-variables.sass index f2c3dc4..e590103 100644 --- a/src/style/quasar-variables.sass +++ b/src/style/quasar-variables.sass @@ -69,30 +69,30 @@ div $separator-color: #EDEDED !default .bg-teal-1 - background: #e0f2f1a6 !important - + background: #e0f2f1a6 !important + .table_ellipsis - max-width: 200px - white-space: nowrap - overflow: hidden - text-overflow: ellipsis + max-width: 200px + white-space: nowrap + overflow: hidden + text-overflow: ellipsis .table_ellipsis:hover - word-wrap: break-word - overflow: visible - white-space: normal + word-wrap: break-word + overflow: visible + white-space: normal .table_ellipsis2 - max-width: 25vw - white-space: nowrap - overflow: hidden - text-overflow: ellipsis + max-width: 25vw + white-space: nowrap + overflow: hidden + text-overflow: ellipsis .table_ellipsis2:hover - word-wrap: break-word - overflow: visible - white-space: normal - transition: width 2s + word-wrap: break-word + overflow: visible + white-space: normal + transition: width 2s $muti-tab: #87d4cc .text-muti-tab @@ -100,35 +100,33 @@ $muti-tab: #87d4cc .bg-muti-tab background: $muti-tab !important - /* editor */ .q-editor font-size: 1rem line-height: 1.5rem font-weight: 400 - + .q-editor h1, .q-menu h1 - font-size: 1.5rem - line-height: 2rem - font-weight: 400 - margin-block-start: 0em - margin-block-end: 0em + font-size: 1.5rem + line-height: 2rem + font-weight: 400 + margin-block-start: 0em + margin-block-end: 0em .q-editor h2, .q-menu h2 - font-size: 1.25rem - line-height: 1.5rem - font-weight: 400 - margin-block-start: 0em - margin-block-end: 0em - + font-size: 1.25rem + line-height: 1.5rem + font-weight: 400 + margin-block-start: 0em + margin-block-end: 0em .q-editor h3, .q-menu h3 - font-size: 1.1rem - line-height: 1.5rem - font-weight: 400 - margin-block-start: 0em - margin-block-end: 0em + font-size: 1.1rem + line-height: 1.5rem + font-weight: 400 + margin-block-start: 0em + margin-block-end: 0em .q-editor p, .q-menu p margin: 0 @@ -136,4 +134,7 @@ $muti-tab: #87d4cc /* q-tree */ .q-tree - color: #c8d3db + color: #c8d3db + +.custom-notification-top + margin-top: 180px !important From ce97f7984a409c70c0c0528ac0176bd664714629 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Wed, 20 May 2026 11:28:59 +0700 Subject: [PATCH 2/5] fix: Watch notification counter on socket --- src/stores/socket.ts | 5 ++++- src/views/HomeView.vue | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/stores/socket.ts b/src/stores/socket.ts index be2ceee..28c1e3b 100644 --- a/src/stores/socket.ts +++ b/src/stores/socket.ts @@ -1,3 +1,4 @@ +import { ref } from 'vue' import config from '@/app.config' import { getToken } from '@/plugins/auth' import { defineStore } from 'pinia' @@ -10,6 +11,7 @@ interface sockeBackup { export const useSocketStore = defineStore('socket', () => { let socket: Socket + const notificationCounter = ref(0); async function init() { socket = io(new URL(config.API.socket).origin, { @@ -19,6 +21,7 @@ export const useSocketStore = defineStore('socket', () => { socket.on('socket-notification', (payload) => { let body: sockeBackup = JSON.parse(payload) + notificationCounter.value++ notifyStatus(body.message, body.success) }) } @@ -94,5 +97,5 @@ export const useSocketStore = defineStore('socket', () => { init() - return {} + return { notificationCounter } }) diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 9b76ae3..b5ffaa2 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -11,6 +11,7 @@ import { import { useQuasar } from 'quasar' import { format } from 'date-fns' import Camera from 'simple-vue-camera' +import { storeToRefs } from 'pinia' import config from '@/app.config' import http from '@/plugins/http' @@ -18,6 +19,7 @@ import { useCounterMixin } from '@/stores/mixin' import { usePermissions } from '@/composables/usePermissions' import { usePrivacyStore } from '@/stores/privacy' import { usePositionKeycloakStore } from '@/stores/positionKeycloak' +import { useSocketStore } from '@/stores/socket' import type { FormRef, OptionReason } from '@/interface/response/checkin' @@ -32,6 +34,8 @@ const { syncPermissionStates, requestCameraPermission, } = usePermissions() +const socketStore = useSocketStore() +const { notificationCounter } = storeToRefs(socketStore) const privacyStore = usePrivacyStore() const positionKeycloakStore = usePositionKeycloakStore() const MOCK_CHECK_DELAY_MS = 800 @@ -1248,6 +1252,11 @@ watch( } } ) + +/** Watch notification counter on socket */ +watch(notificationCounter, () => { + startChecking() +})