diff --git a/package.json b/package.json index d4105dd..32ac41f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "moment": "^2.29.4", "pinia": "^2.0.29", "quasar": "^2.11.1", + "socket.io-client": "^4.7.4", "structure-chart": "^0.0.9", "vue": "^3.4.15", "vue-router": "^4.1.6", diff --git a/src/modules/00_support/interface/index/Main.ts b/src/modules/00_support/interface/index/Main.ts index 6fbe82e..fc25ca2 100644 --- a/src/modules/00_support/interface/index/Main.ts +++ b/src/modules/00_support/interface/index/Main.ts @@ -1,8 +1,63 @@ -interface DataOption { +export interface Pagination { + result: T; + page: number; + pageSize: number; + total: number; +} +export interface SupportIssueCategory { id: string; name: string; } -export type { - DataOption, +export interface SupportIssue { + id: string; + createdByUserId: string; + createdByUserName: string; + createdAt: string; + updatedAt: string; + title: string; + status: string; + unreadCount: number; + lastMessage?: string; + category: SupportIssueCategory; +} + +export interface SupportIssueWithMessage + extends Omit { + message: SupportIssueMessage[]; +} + +export interface SupportIssueMessage { + id: string; + fromUserId: string; + fromUserName: string; + createdAt: string; + updatedAt: string; + content: string; + read: boolean; + issueId: string; +} + +export interface SupportMessageStatus { + fromUserId: string; + fromUserName: string; + lastAccessDate: string; + issueId: string; +} + +export interface SupportMessageStatusResponse { + result: SupportMessageStatus[]; +} + +export interface SupportUserStatus { + socketId: string; + userId: string; + name: string; + role: string[]; +} + +export type SupportIssueResponse = Pagination; +export type SupportIssueCategoryResponse = { + result: SupportIssueCategory[]; }; +export type SupportMessageResponse = Pagination; diff --git a/src/modules/00_support/store/Main.ts b/src/modules/00_support/store/Main.ts index d01c300..e0aa050 100644 --- a/src/modules/00_support/store/Main.ts +++ b/src/modules/00_support/store/Main.ts @@ -1,10 +1,222 @@ import { defineStore } from "pinia"; import { ref } from "vue"; +import { io } from "socket.io-client"; +import { useQuasar } from "quasar"; +import http from "@/plugins/http"; +import config from "@/app.config"; +import type { + SupportMessageResponse, + SupportIssueResponse, + SupportIssueCategoryResponse, + SupportMessageStatusResponse, + SupportUserStatus, + SupportMessageStatus, +} from "@/modules/00_support/interface/index/Main"; +import keycloak from "@/plugins/keycloak"; +import { useCounterMixin } from "@/stores/mixin"; -export const useEvalutuonStore = defineStore("supportServiceStore", () => { - const index = ref(0); +export const useSupportStore = defineStore("supportServiceStore", () => { + const { showLoader, hideLoader, messageError } = useCounterMixin(); + const $q = useQuasar(); + const userId = ref(keycloak.subject); + const userStatus = ref([]); + const issue = ref(); + const issueCategory = ref(); + const messageStatus = ref(); + const message = ref(); + const currentIssue = ref(""); + const currentTitle = ref(""); + const items = ref([{}, {}, {}, {}, {}, {}, {}]); + const scrollTargetRef = ref(); + + const socket = io("http://192.168.1.10:3000/", { + auth: { token: keycloak.token }, + autoConnect: false, + }); + + socket.on("users", (data: SupportUserStatus[]) => { + userStatus.value = data; + }); + + socket.on("online", (r) => { + userStatus.value.push({ + socketId: r.socketId, + userId: r.userId, + name: r.name, + role: r.role, + }); + // console.log("online: ", userStatus.value); + }); + + socket.on("offline", (socketId: string) => { + if (socketId === socket.id) return; + userStatus.value = userStatus.value.filter((v) => v.socketId !== socketId); + // console.log("offline: ", userStatus.value); + }); + + socket.on("notify-message", (r) => { + if (issue.value) { + issue.value.result = issue.value.result.map((v) => { + if (v.id === r.issueId) { + v.unreadCount++; + v.lastMessage = r.content; + } + return v; + }); + } + }); + + socket.on("message", (r) => { + message.value?.result.message.push({ + id: r.id, + fromUserId: r.fromUserId, + fromUserName: r.fromUserName, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + content: r.content, + read: r.read, + issueId: r.issueId, + }); + socket.emit("mark-read", { issueId: currentIssue.value }); + if (issue.value) { + issue.value.result = issue.value.result.map((v) => { + if (v.id === r.issueId) { + v.lastMessage = r.content; + } + return v; + }); + } + + // console.log(r.id); + // console.log(message.value?.result.message); + }); + + socket.on("read", (r: SupportMessageStatus) => { + if (messageStatus.value) { + messageStatus.value.result = messageStatus.value.result.map((v) => { + if (v.fromUserId !== r.fromUserId) return r; + return v; + }); + } + // console.log("event(read):", messageStatus.value); + }); + + function sendMessage(content: string, to: string) { + // console.log(content); + // console.log(to); + socket.emit("message", { to, content }); + } + + async function fetchMessageStatus(issueId: string) { + showLoader(); + const res = await http + .get(config.API.supportMessageStatus(issueId)) + .catch((err) => { + messageError($q, err); + }) + .finally(() => { + hideLoader(); + }); + if (res && res.data) { + messageStatus.value = res.data; + // console.log(messageStatus.value); + } + } + + async function fetchMessage(issueId: string) { + showLoader(); + const res = await http + .get(config.API.supportMessage(issueId)) + .catch((err) => { + messageError($q, err); + }) + .finally(() => { + hideLoader(); + }); + if (res && res.data) { + message.value = await res.data; + message.value?.result.message.reverse(); + socket.emit("join-issue", { issueId }); + socket.emit("mark-read", { issueId }); + } + } + + async function fetchIssue() { + if (!userId.value) return; + showLoader(); + const res = await http + .get(config.API.supportIssueUserId(userId.value)) + .catch((err) => { + messageError($q, err); + }) + .finally(() => { + hideLoader(); + }); + + if (res && res.data) { + issue.value = res.data; + // console.log(JSON.stringify(res.data, null, 2)); + } + } + + async function fetchIssueCategory() { + showLoader(); + + const res = await http + .get(config.API.supportIssueCategory) + .catch((err) => { + messageError($q, err); + }) + .finally(() => { + hideLoader(); + }); + + if (res && res.data) { + issueCategory.value = res.data; + // console.log(JSON.stringify(res.data, null, 2)); + } + } + + async function newIssue(title: string, categoryId: string) { + showLoader(); + + const requestBody = { + title: title, + categoryId: categoryId, + }; + + const res = await http + .post(config.API.supportIssue, requestBody) + .catch((err) => { + messageError($q, err); + }) + .finally(() => { + hideLoader(); + }); + + if (res) { + fetchIssue(); + // console.log(JSON.stringify(res.data, null, 2)); + } + } return { - index, + userId, + issue, + issueCategory, + message, + messageStatus, + userStatus, + currentIssue, + currentTitle, + fetchIssue, + fetchIssueCategory, + fetchMessageStatus, + fetchMessage, + sendMessage, + newIssue, + scrollTargetRef, + items, + socket, }; });