Skip to content

Commit 54c6637

Browse files
authored
Merge pull request #76 from Dialogue-Bot/DIAL-42-implement-test-your-bot
feat: update
2 parents efe4875 + 2084585 commit 54c6637

31 files changed

+1237
-157
lines changed

client/package-lock.json

Lines changed: 328 additions & 118 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"crypto-js": "^4.2.0",
6767
"date-fns": "^3.3.1",
6868
"dayjs": "^1.11.10",
69-
"dialogue-chatbox": "^1.0.10",
69+
"dialogue-chatbox": "^1.0.12",
7070
"embla-carousel-react": "^8.0.0-rc23",
7171
"fast-glob": "^3.3.2",
7272
"firebase": "^10.8.0",

client/src/apis/live-chat.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
11
import { ENDPOINTS } from '@/constants'
22
import http_client from '@/lib/http-client'
3+
import { TConversation, TConversationLiveChatQuery } from '@/types/live-chat'
4+
import { TBaseResponse, TResPagination } from '@/types/share'
35

46
class LiveChatApi {
5-
async getConversations() {
6-
return http_client.get(ENDPOINTS.CONVERSATION_LIVE_CHAT.INDEX)
7+
async getConversations(
8+
q?: TConversationLiveChatQuery,
9+
): Promise<TResPagination<TConversation>> {
10+
return http_client.get(ENDPOINTS.CONVERSATION_LIVE_CHAT.INDEX, {
11+
params: q,
12+
})
13+
}
14+
15+
async getConversation(
16+
channelId: string,
17+
userId: string,
18+
): Promise<TBaseResponse<TConversation>> {
19+
return http_client.get(
20+
`${ENDPOINTS.CONVERSATION_LIVE_CHAT.INDEX}/${userId}/${channelId}`,
21+
)
722
}
823
}
24+
25+
export const liveChatApi = new LiveChatApi()
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { Layout as AppLayout } from './app'
22
export { Layout as AuthLayout } from './auth'
3-
export { Layout as SettingLayout } from './setting'
3+
export { Layout as LiveChatLayout } from './live-chat'
44
export { Layout as PublishLayout } from './publish'
5+
export { Layout as SettingLayout } from './setting'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './layout'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Outlet } from 'react-router-dom'
2+
import Sidebar from './sidebar'
3+
4+
export const Layout = () => {
5+
return (
6+
<div className='flex h-screen-header'>
7+
<Sidebar />
8+
<div className='flex-1'>
9+
<Outlet />
10+
</div>
11+
</div>
12+
)
13+
}
14+
15+
export default Layout
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ConversationItem } from '@/components/pages/live-chat'
2+
import { queryConversationsOptions } from '@/lib/query-options/live-chat'
3+
import { TConversationLiveChatQuery } from '@/types/live-chat'
4+
import { urlSearchParamsToObject } from '@/utils'
5+
import { useSuspenseQuery } from '@tanstack/react-query'
6+
import { useTranslation } from 'react-i18next'
7+
import { useSearchParams } from 'react-router-dom'
8+
9+
const Sidebar = () => {
10+
const { t } = useTranslation('conversations')
11+
const [search] = useSearchParams()
12+
const { data } = useSuspenseQuery(
13+
queryConversationsOptions(
14+
urlSearchParamsToObject(search) as TConversationLiveChatQuery,
15+
),
16+
)
17+
return (
18+
<aside className='max-w-80 w-full border-r border-input'>
19+
{data.items.length > 0 ? (
20+
<ul>
21+
{data.items.map((item) => {
22+
return (
23+
<li key={`${item.userId}-${item.channelId}`}>
24+
<ConversationItem conversation={item} />
25+
</li>
26+
)
27+
})}
28+
</ul>
29+
) : (
30+
<div className='p-4 flex items-center justify-center'>
31+
<p className='text-muted-foreground'>{t('empty_conversation')}</p>
32+
</div>
33+
)}
34+
</aside>
35+
)
36+
}
37+
38+
export default Sidebar

client/src/components/pages/flow-detail/test-your-bot.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ const TestYourBot = () => {
1717
showTestBot && (
1818
<div
1919
className={cn(
20-
'fixed right-4 z-10 rounded-md shadow w-80 transition-transform duration-300 bottom-4 flex flex-col gap-4 bg-white h-[500px]',
20+
'fixed right-4 z-10 rounded-md shadow w-80 transition-transform duration-300 bottom-4 flex flex-col gap-4 bg-white h-[500px] overflow-hidden',
2121
)}
2222
>
23-
<ChatBox channelId={channel.contactId} className='h-full w-full'
23+
<ChatBox
24+
channelId={channel.contactId}
25+
className='h-full w-full'
2426
isTest
27+
isShowClose={false}
2528
/>
2629
</div>
2730
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ROUTES } from '@/constants'
2+
import { TConversation } from '@/types/live-chat'
3+
import { useNavigate } from 'react-router-dom'
4+
5+
type Props = {
6+
conversation: TConversation
7+
}
8+
9+
export const ConversationItem = ({ conversation }: Props) => {
10+
const navigate = useNavigate()
11+
return (
12+
<div
13+
className='p-4 border-b border-input'
14+
onClick={() => {
15+
navigate(
16+
`${ROUTES.PRIVATE.CONVERSATION.INDEX}/${conversation.userId}/${conversation.channelId}`,
17+
)
18+
}}
19+
>
20+
<div>
21+
<span>
22+
{conversation.userId} - {conversation.channel.contactName}
23+
</span>
24+
</div>
25+
</div>
26+
)
27+
}
28+
29+
export default ConversationItem
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './conversation-item'

client/src/i18n/resources.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import channelEn from '@/locales/en/channel.json'
22
import chatbotsEn from '@/locales/en/chatbots.json'
33
import commonEN from '@/locales/en/common.json'
4+
import conversationsEn from '@/locales/en/conversations.json'
45
import flowDetailEn from '@/locales/en/flow-detail.json'
56
import forgotPassEN from '@/locales/en/forgot_pass.json'
67
import formsEN from '@/locales/en/forms.json'
@@ -20,6 +21,7 @@ import verifyAccountEn from '@/locales/en/verify-account.json'
2021
import channelVi from '@/locales/vi/channel.json'
2122
import chatbotsVi from '@/locales/vi/chatbots.json'
2223
import commonVI from '@/locales/vi/common.json'
24+
import conversationsVi from '@/locales/vi/conversations.json'
2325
import flowDetailVi from '@/locales/vi/flow-detail.json'
2426
import forgotPassVI from '@/locales/vi/forgot_pass.json'
2527
import formsVI from '@/locales/vi/forms.json'
@@ -59,6 +61,7 @@ const resources = {
5961
training: trainingEn,
6062
requestVerifyAccount: requestVerifyAccountEn,
6163
verifyAccount: verifyAccountEn,
64+
conversations: conversationsEn,
6265
},
6366
[ELang.VI]: {
6467
common: commonVI,
@@ -80,6 +83,7 @@ const resources = {
8083
training: trainingVi,
8184
requestVerifyAccount: requestVerifyAccountVi,
8285
verifyAccount: verifyAccountVi,
86+
conversations: conversationsVi,
8387
},
8488
} as const
8589

client/src/lib/loader.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import {
2121
queryIntentsOption,
2222
settingQueryOption,
2323
} from './query-options'
24+
import {
25+
queryConversationOption,
26+
queryConversationsOptions,
27+
} from './query-options/live-chat'
2428

2529
export const authLoader = async ({ request }: any) => {
2630
const redirectUrl = new URL(request.url).searchParams.get('redirect')
@@ -155,3 +159,20 @@ export const verifyAccountLoader = async ({ request }: any) => {
155159

156160
return null
157161
}
162+
163+
export const conversationsLoader = async ({ request }: any) => {
164+
const query = queryStringToObject(request.url)
165+
useAppLayoutStore.getState().setTitle(i18n.t('common:conversations'))
166+
167+
await queryClient.ensureQueryData(queryConversationsOptions(query))
168+
169+
return null
170+
}
171+
172+
export const conversationLoader = async ({ params }: any) => {
173+
await queryClient.ensureQueryData(
174+
queryConversationOption(params.channelId, params.userId),
175+
)
176+
177+
return null
178+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { liveChatApi } from '@/apis/live-chat'
2+
import { TConversationLiveChatQuery } from '@/types/live-chat'
3+
import { queryOptions } from '@tanstack/react-query'
4+
5+
export const queryConversationsOptions = (q: TConversationLiveChatQuery) => {
6+
return queryOptions({
7+
queryKey: ['conversations', q],
8+
queryFn: async () => {
9+
const res = await liveChatApi.getConversations(q)
10+
11+
return res.data
12+
},
13+
})
14+
}
15+
16+
export const queryConversationOption = (channelId: string, userId: string) => {
17+
return queryOptions({
18+
queryKey: ['conversation', channelId],
19+
queryFn: async () => {
20+
const res = await liveChatApi.getConversation(channelId, userId)
21+
22+
return res.data
23+
},
24+
})
25+
}

client/src/locales/en/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"profiles": "Profiles",
1818
"save": "Save",
1919
"channels": "Channels",
20+
"conversations": "Conversations",
21+
"users": "Users",
2022
"search": "Search",
2123
"add_intent": "Add intent",
2224
"trained": "Trained",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"empty_conversation": "Not have any conversation yet",
3+
"select_conversation": "Select a conversation to start chatting"
4+
}

client/src/locales/vi/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
"add_intent": "Thêm ý định",
2121
"trained": "Đã đào tạo",
2222
"not_trained": "Chưa đào tạo",
23+
"conversations": "Cuộc trò chuyện",
24+
"users": "Người dùng",
2325
"row_actions": {
2426
"update": "Cập nhật",
2527
"delete": "Xóa",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"empty_conversation": "Không có cuộc trò chuyện nào",
3+
"select_conversation": "Chọn một cuộc trò chuyện để bắt đầu trò chuyện"
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const ConversationDetail = () => {
2+
return <div>ConversationDetail</div>
3+
}
4+
5+
export default ConversationDetail

client/src/pages/conversations.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
import { useTranslation } from 'react-i18next'
2+
13
const Conversations = () => {
2-
return <div>Conversations</div>
4+
const { t } = useTranslation('conversations')
5+
return (
6+
<div className='flex items-center justify-center h-full'>
7+
<p className='text-muted-foreground text-center text-lg font-medium'>
8+
{t('select_conversation')}
9+
</p>
10+
</div>
11+
)
312
}
413

514
export default Conversations

client/src/pages/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ export const RequestVerifyAccount = lazy(
1919
)
2020
export const VerifyAccount = lazy(() => import('./verify-account'))
2121
export { default as LandingPage } from './landing-page'
22+
export const Conversations = lazy(() => import('./conversations'))
23+
export const ConversationDetail = lazy(() => import('./conversation-detail'))

client/src/router.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
AddIntent,
33
Channels,
4+
ConversationDetail,
5+
Conversations,
46
FlowDetail,
57
Flows,
68
ForgotPassword,
@@ -20,6 +22,7 @@ import { createBrowserRouter, redirect } from 'react-router-dom'
2022
import {
2123
AppLayout,
2224
AuthLayout,
25+
LiveChatLayout,
2326
PublishLayout,
2427
SettingLayout,
2528
} from '@/components/layouts'
@@ -34,6 +37,7 @@ import {
3437
articlesLoader,
3538
authLoader,
3639
channelsLoader,
40+
conversationsLoader,
3741
flowDetailLoader,
3842
flowsLoader,
3943
intentLoader,
@@ -155,6 +159,21 @@ export const router = createBrowserRouter([
155159
},
156160
],
157161
},
162+
{
163+
path: ROUTES.PRIVATE.CONVERSATION.INDEX,
164+
Component: LiveChatLayout,
165+
children: [
166+
{
167+
index: true,
168+
Component: Conversations,
169+
loader: conversationsLoader,
170+
},
171+
{
172+
path: `${ROUTES.PRIVATE.CONVERSATION.INDEX}/:userId/:channelId`,
173+
Component: ConversationDetail,
174+
},
175+
],
176+
},
158177
],
159178
},
160179
{

client/src/types/live-chat.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
import { TChannel } from './channel'
2+
import { TBaseQuery } from './share'
3+
14
export type TConversation = {
25
id: string
36
userId: string
47
channelId: string
58
createdAt: string
69
updatedAt: string
710
lastMessage: TMessage
11+
channel: TChannel
812
}
913

1014
export type TMessage = {
@@ -36,3 +40,7 @@ export type TMessage = {
3640
url?: string
3741
}
3842
}
43+
44+
export type TConversationLiveChatQuery = {
45+
channelId?: string
46+
} & TBaseQuery
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ALTER TABLE "messages" ADD COLUMN "message" text DEFAULT '';--> statement-breakpoint
2+
ALTER TABLE "messages" ADD COLUMN "template" json DEFAULT '{}'::json;--> statement-breakpoint
3+
ALTER TABLE "messages" DROP COLUMN IF EXISTS "data";

0 commit comments

Comments
 (0)