เพิ่มฟัง์ชันและ api การค้นหาหัวข้อปัญหา (support)

This commit is contained in:
puri-ph4tt 2024-02-05 17:57:41 +07:00
parent 12ac44c7c2
commit 7ac8cbc87c
3 changed files with 105 additions and 31 deletions

View file

@ -4,9 +4,21 @@ const support = `${env.API_URL_SUPPORT}`;
export default { export default {
supportMessageStatus: (id: string) => `${support}/issue/${id}/message-status`, supportMessageStatus: (id: string) => `${support}/issue/${id}/message-status`,
supportMessage: (id: string, pageSize: number = 999, page: number = 1) => supportMessage: (id: string, pageSize: number = 999, page: number = 1) =>
`${support}/issue/${id}/message?pageSize=${pageSize}&page=${page}`, `${support}/issue/${id}/message?pageSize=${pageSize}&page=${page}`,
supportIssueUserId: (userId: string) => `${support}/issue?userId=${userId}`, supportIssueUserId: (userId: string) => `${support}/issue?userId=${userId}`,
supportIssue: `${support}/issue`,
supportIssue: (pageSize: number = 999, page: number = 1) =>
`${support}/issue?pageSize=${pageSize}&page=${page}`,
supportSearchIssue: (
search: string,
pageSize: number = 999,
page: number = 1
) => `${support}/issue?pageSize=${pageSize}&page=${page}&search=${search}`,
supportIssueCategory: `${support}/issue-category`, supportIssueCategory: `${support}/issue-category`,
supportNewIssue: `${support}/issue`,
}; };

View file

@ -18,6 +18,7 @@ import { useCounterMixin } from "@/stores/mixin";
export const useSupportStore = defineStore("supportServiceStore", () => { export const useSupportStore = defineStore("supportServiceStore", () => {
const { showLoader, hideLoader, messageError } = useCounterMixin(); const { showLoader, hideLoader, messageError } = useCounterMixin();
const $q = useQuasar(); const $q = useQuasar();
const icon = ref<string>("mdi-account-check");
const userId = ref<string | undefined>(keycloak.subject); const userId = ref<string | undefined>(keycloak.subject);
const userStatus = ref<SupportUserStatus[]>([]); const userStatus = ref<SupportUserStatus[]>([]);
const issue = ref<SupportIssueResponse>(); const issue = ref<SupportIssueResponse>();
@ -27,7 +28,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
const currentIssue = ref<string>(""); const currentIssue = ref<string>("");
const currentTitle = ref<string>(""); const currentTitle = ref<string>("");
const items = ref<any>([{}, {}, {}, {}, {}, {}, {}]); const items = ref<any>([{}, {}, {}, {}, {}, {}, {}]);
const scrollTargetRef = ref(); const scrollContainer = ref();
const socket = io("http://192.168.1.10:3000/", { const socket = io("http://192.168.1.10:3000/", {
auth: { token: keycloak.token }, auth: { token: keycloak.token },
@ -67,26 +68,16 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
}); });
socket.on("message", (r) => { socket.on("message", (r) => {
message.value?.result.message.push({ message.value?.result.message.push(r);
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) { if (issue.value) {
issue.value.result = issue.value.result.map((v) => { issue.value.result = issue.value.result.map((v) => {
if (v.id === r.issueId) { if (v.id === r.issueId) v.lastMessage = r.content;
v.lastMessage = r.content;
}
return v; return v;
}); });
} }
socket.emit("mark-read", { issueId: currentIssue.value });
scrollToEnd();
// console.log(r.id); // console.log(r.id);
// console.log(message.value?.result.message); // console.log(message.value?.result.message);
}); });
@ -101,10 +92,17 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
// console.log("event(read):", messageStatus.value); // console.log("event(read):", messageStatus.value);
}); });
function scrollToEnd(position: Number = 1) {
setTimeout(() => {
scrollContainer.value?.setScrollPercentage("vertical", position);
}, 150);
}
function sendMessage(content: string, to: string) { function sendMessage(content: string, to: string) {
// console.log(content); // console.log(content);
// console.log(to); // console.log(to);
socket.emit("message", { to, content }); socket.emit("message", { to, content });
scrollToEnd();
} }
async function fetchMessageStatus(issueId: string) { async function fetchMessageStatus(issueId: string) {
@ -138,6 +136,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
message.value?.result.message.reverse(); message.value?.result.message.reverse();
socket.emit("join-issue", { issueId }); socket.emit("join-issue", { issueId });
socket.emit("mark-read", { issueId }); socket.emit("mark-read", { issueId });
scrollToEnd();
} }
} }
@ -186,7 +185,7 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
}; };
const res = await http const res = await http
.post(config.API.supportIssue, requestBody) .post(config.API.supportNewIssue, requestBody)
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
}) })
@ -200,6 +199,18 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
} }
} }
async function searchIssue(searchData: string) {
const res = await http
.get(config.API.supportSearchIssue(searchData))
.catch((err) => {
messageError($q, err);
});
if (res && res.data) {
issue.value = res.data;
// console.log(issue.value);
}
}
return { return {
userId, userId,
issue, issue,
@ -215,8 +226,10 @@ export const useSupportStore = defineStore("supportServiceStore", () => {
fetchMessage, fetchMessage,
sendMessage, sendMessage,
newIssue, newIssue,
scrollTargetRef, searchIssue,
items, items,
socket, socket,
scrollContainer,
icon,
}; };
}); });

View file

@ -1,13 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue"; import { onMounted, onUnmounted, ref, watch } from "vue";
import { useSupportStore } from "@/modules/00_support/store/Main"; import { useSupportStore } from "@/modules/00_support/store/Main";
import FormChat from "@/modules/00_support/components/FormChat.vue"; import FormChat from "@/modules/00_support/components/FormChat.vue";
import NewIssue from "@/modules/00_support/components/NewIssue.vue"; import NewIssue from "@/modules/00_support/components/NewIssue.vue";
import moment from "moment";
const store = useSupportStore(); const store = useSupportStore();
const searchData = ref<string>("");
const search = ref<string>(""); function dateIssue(timestamp: string): string {
const icon = ref<string>("mdi-account-check"); const parsedTimestamp = moment(timestamp);
const diff = moment().diff(parsedTimestamp);
if (diff < 1000) {
return "just now";
} else if (diff < 60000) {
return `${Math.floor(diff / 1000)}s`;
} else if (diff < 3600000) {
return `${Math.floor(diff / 60000)}m`;
} else if (diff < 86400000) {
return `${Math.floor(diff / 3600000)}h`;
} else {
const beYear = parsedTimestamp.year() + 543;
const formattedDate = parsedTimestamp.clone().year(beYear).format("DD MMM");
return formattedDate;
}
}
onUnmounted(async () => { onUnmounted(async () => {
store.socket.disconnect(); store.socket.disconnect();
@ -17,6 +35,10 @@ onMounted(async () => {
store.socket.connect(); store.socket.connect();
store.fetchIssue(); store.fetchIssue();
}); });
watch(searchData, () => {
store.searchIssue(searchData.value);
});
</script> </script>
<template> <template>
@ -28,6 +50,7 @@ onMounted(async () => {
<div class="row"> <div class="row">
<!-- Left --> <!-- Left -->
<div class="col-xs-12 col-sm-4 col-md-3"> <div class="col-xs-12 col-sm-4 col-md-3">
<!-- New -->
<div class="q-pt-md bg-white"> <div class="q-pt-md bg-white">
<div class="q-px-md q-pb-md"> <div class="q-px-md q-pb-md">
<q-input <q-input
@ -36,8 +59,9 @@ onMounted(async () => {
dense dense
placeholder="ค้นหา" placeholder="ค้นหา"
debounce="300" debounce="300"
v-model="search" v-model="searchData"
id="inputSearch" id="inputSearch"
@keydown.enter.prevent="store.searchIssue(searchData)"
> >
<template v-slot:prepend <template v-slot:prepend
><q-icon name="mdi-magnify" class="pointer" /> ><q-icon name="mdi-magnify" class="pointer" />
@ -49,6 +73,25 @@ onMounted(async () => {
<new-issue /> <new-issue />
</div> </div>
<q-separator inset /> <q-separator inset />
<!-- No Issue -->
<div
v-if="store.issue?.result.length === 0"
class="col-10 row items-center q-pa-md new"
>
<div class="noactive-avatar">
<q-avatar
color="transparent"
text-color="white"
size="40px"
>
<q-icon name="mdi-close" size="24px" color="negative" />
</q-avatar>
</div>
<div class="col column q-ml-md">
<span class="col text-grey"> ไมพบเรองปรกษา </span>
</div>
</div>
</div> </div>
<!-- List Issue --> <!-- List Issue -->
@ -81,10 +124,14 @@ onMounted(async () => {
:class="{ active: store.currentIssue === item.id }" :class="{ active: store.currentIssue === item.id }"
class="noactive row q-py-md justify-between items-center q-px-md" class="noactive row q-py-md justify-between items-center q-px-md"
> >
<div class="col-10 row items-center"> <div class="col-9 row items-center">
<div class="noactive-avatar"> <div class="noactive-avatar">
<q-avatar color="grey-2" text-color="white" size="40px"> <q-avatar color="grey-2" text-color="white" size="40px">
<q-icon :name="icon" size="24px" color="primary" /> <q-icon
:name="store.icon"
size="24px"
color="primary"
/>
</q-avatar> </q-avatar>
</div> </div>
<div class="col column q-ml-md"> <div class="col column q-ml-md">
@ -102,7 +149,7 @@ onMounted(async () => {
> >
</div> </div>
</div> </div>
<div class="col-2 items-center text-right"> <div class="col-3 items-center text-right">
<q-icon <q-icon
v-if="item.lastMessage?.length === 0" v-if="item.lastMessage?.length === 0"
name="mdi-send" name="mdi-send"
@ -110,7 +157,9 @@ onMounted(async () => {
color="primary" color="primary"
/> />
<div v-else class="column"> <div v-else class="column">
<span class="col text-caption text-grey">20 s</span> <span class="col text-caption text-grey">{{
dateIssue(item.updatedAt)
}}</span>
<div class="col"> <div class="col">
<q-badge <q-badge
v-if="item.unreadCount > 0" v-if="item.unreadCount > 0"
@ -138,7 +187,7 @@ onMounted(async () => {
<!-- Right --> <!-- Right -->
<div v-if="$q.screen.gt.xs" class="col-grow"> <div v-if="$q.screen.gt.xs" class="col-grow">
<form-chat v-if="store.currentIssue.length !== 0" :icon="icon" /> <form-chat v-if="store.currentIssue.length !== 0" />
</div> </div>
</div> </div>
</q-card> </q-card>
@ -160,7 +209,7 @@ onMounted(async () => {
.noactive-avatar { .noactive-avatar {
transition: 0.1s ease all; transition: 0.1s ease all;
border-radius: 50%; border-radius: 50%;
border: 1px solid teal; border: 1px solid $primary;
} }
} }
} }
@ -172,11 +221,11 @@ onMounted(async () => {
.active { .active {
background: #ebf9f7; background: #ebf9f7;
border-left: 5px solid #03a898; border-left: 5px solid $primary;
& { & {
.noactive-avatar { .noactive-avatar {
border-radius: 50%; border-radius: 50%;
border: 1px solid #03a898; border: 1px solid $primary;
} }
} }
} }