Skip to content

Commit

Permalink
Chatbot (#36)
Browse files Browse the repository at this point in the history
* Chat UI

* Implementation for the search functionality

* add/send_message

* fix/minor_error

* fix/minor_error

* Search Functionality

* Implemented Cards in user dashboard

* Hotfix/search functionality had a bug.

* add/chat

* fix/lint

* Cards

* Added provider_id in serviceData

* Added service_iamge_url

* add/chat

* fix/builderror

---------

Co-authored-by: call203 <proground.developer@gmail.com>
Co-authored-by: Kaustubh Trivedi <kaus12tri@gmail.com>
  • Loading branch information
3 people authored Dec 4, 2023
1 parent 3e84529 commit c0b075c
Show file tree
Hide file tree
Showing 15 changed files with 414 additions and 48 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@tanstack/react-table": "^8.10.7",
"axios": "^1.6.0",
"class-variance-authority": "^0.7.0",
"classname": "^0.0.0",
"clsx": "^2.0.0",
"cookies-next": "^4.0.0",
"eslint-config-prettier": "^9.0.0",
Expand Down
Binary file added public/close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/right-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'
import { selectAuthState, setAuthState } from '@/store/authSlice'
import { useDispatch, useSelector } from 'react-redux'
import { deleteCookie, getCookie } from 'cookies-next'
import { setUserName, setUserState, setProfileId, setUserId } from '@/store/userSlice'
import { setUserName, setUserState, setProfileId, setUserId, setUserType } from '@/store/userSlice'
import { getUserProfile } from '@/lib/utils'

const Header = () => {
Expand All @@ -22,6 +22,7 @@ const Header = () => {
dispatch(setUserName(res?.username))
dispatch(setProfileId(res?.profileId))
dispatch(setUserId(res?.userId))
dispatch(setUserType(res?.userType))
})
}
}, [])
Expand Down
2 changes: 0 additions & 2 deletions src/components/apis/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ export async function apiRequest({ method, path, body, header }: apiRequesProps)
} else if (method === 'DELETE') {
res = await customAxios.delete(path, body)
}
console.log(res)
return { status: res?.status || res?.data?.status, message: res?.data }
} catch (error) {
console.log(error)
if (axios.isAxiosError(error)) {
// console.log(error.response?.data?.message)
return {
Expand Down
70 changes: 70 additions & 0 deletions src/components/user-dashboard/DataCards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card'
import { TUserDashboardTable } from './columns'
import { Button } from '../ui/button'

interface IProps {
data: TUserDashboardTable[]
userType: string
clickChat: () => void
handleClickInfoForChat: (value: TUserDashboardTable) => void
}

export default function DataCards({ data, userType, clickChat, handleClickInfoForChat }: IProps) {
console.log(userType)
return (
<div className="grid grid-cols-4 gap-7">
{data?.map((service: TUserDashboardTable, Index: number) => (
<Card key={Index}>
<CardHeader>
<CardTitle>Service</CardTitle>
<CardDescription>Type of servide: {service?.short_description}</CardDescription>
</CardHeader>
<CardContent>
<ul>
<li>Provider: {service?.provider}</li>
<li>Availability {service?.availability}</li>
<li>Pricing: {service?.pricing}</li>
</ul>
</CardContent>
<CardFooter>
<div>
{userType === 'service_provider' ? (
<>
<div className="flex space-x-5">
<Button
onClick={() => {
clickChat()
handleClickInfoForChat(service)
}}
>
Contact
</Button>
<Button>Delete Service</Button>
</div>
</>
) : (
<>
<Button
onClick={() => {
clickChat()
handleClickInfoForChat(service)
}}
>
Contact
</Button>
</>
)}
</div>
</CardFooter>
</Card>
))}
</div>
)
}
52 changes: 52 additions & 0 deletions src/components/user-dashboard/chatUserList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Chat } from '@/store/chatSlilce'

interface ChatUserListProps {
userList: Chat[]
handleChatFromUserList: (item: Chat) => void
}

const ChatUserList = ({ userList, handleChatFromUserList }: ChatUserListProps) => {
return (
<div className="flex flex-col px-3 overflow-y-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>UserId</TableHead>
<TableHead>Message</TableHead>
<TableHead>Date</TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{userList.map((item: Chat, index: number) => {
const date = new Date(item?.date_sent)
const month = date.getMonth() + 1
const day = date.getDate()
return (
<TableRow key={index}>
<TableCell>{item?.user_id}</TableCell>
<TableCell className="text-gray-400">{item?.message_content}</TableCell>
<TableCell>{day + '/' + month}</TableCell>
<TableCell className="text-mainblue" onClick={() => handleChatFromUserList(item)}>
Chat
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</div>
)
}

ChatUserList.displayName = 'ChatUserList'

export { ChatUserList }
171 changes: 171 additions & 0 deletions src/components/user-dashboard/chatbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, { useCallback, useEffect, useState } from 'react'
import Image from 'next/image'
import { Button } from '../ui/button'
import { apiRequest } from '../apis/default'
import { useDispatch, useSelector } from 'react-redux'
import { Chat, selectChatListState, setChatListState } from '@/store/chatSlilce'
import { selectUserId, selectUserType } from '@/store/userSlice'
import { ChatUserList } from './chatUserList'
import { TUserDashboardTable } from './columns'

export interface ChatBoxProps {
handleChatBox: () => void
contactInfo?: TUserDashboardTable
}

const ChatBox = ({ handleChatBox, contactInfo }: ChatBoxProps) => {
const chatlist = useSelector(selectChatListState)
const userType = useSelector(selectUserType)

const userId = useSelector(selectUserId)
const dispatch = useDispatch()
const [message, setMessage] = useState<string>('')
const [userList, setUserList] = useState<Chat[]>([])
const [userIdFromList, setUserIdFromList] = useState<number>(0)
const [userListTable, setUserListTable] = useState(userType === 'service_provider' ? true : false)

const getMessages = useCallback(() => {
apiRequest({
method: 'GET',
path: '/contacts',
}).then(res => {
if (res?.status === 200) {
const messages = res.message.filter((item: Chat) =>
userType === 'user'
? Number(item.provider_id) === Number(contactInfo?.provider_id) &&
Number(item.user_id) === userId
: Number(item.provider_id) === userId,
)

dispatch(setChatListState(messages))
if (userType === 'service_provider') {
const reverseArray = res.message.reverse()
const tempArray: Chat[] = []
reverseArray.forEach((item: Chat) => {
const check_exist = tempArray.filter(item2 => item2.user_id === item.user_id)
if (check_exist.length === 0) {
tempArray.push(item)
}
})
setUserList(tempArray)
}
}
})
}, [dispatch, contactInfo?.provider_id, userId, userType])

useEffect(() => {
getMessages()
}, [getMessages, contactInfo?.provider_id])

const handleMessageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setMessage(event.target.value)
}

const handlePressSend = () => {
const chatform = {
user_id: userType === 'user' ? userId : userIdFromList,
provider_id: Number(contactInfo?.provider_id),
who: userType,
message_content: message,
date_sent: new Date().toISOString(),
}
const newList: Chat[] = chatlist.concat(chatform)
dispatch(setChatListState(newList))
apiRequest({
method: 'POST',
path: '/contacts',
body: chatform,
}).then(res => {
if (res?.status === 200) {
dispatch(setChatListState(res.message))
}
})
setMessage('')
}

const handleChatFromUserList = (info: Chat) => {
setUserIdFromList(info.user_id)
const messages = chatlist.filter(
(item: Chat) => item.provider_id === userId && item.user_id === info.user_id,
)

dispatch(setChatListState(messages))
handleUserListTable()
}

const handleUserListTable = () => {
setUserListTable(!userListTable)
}

return (
<div className="flex flex-col lg:w-2/5 lg:h-3/5 h-2/4 py-7 px-7 mx-3 fixed inset-x-0 bottom-0 z-50 bg-white rounded-xl shadow-2xl">
<div className="flex justify-between items-stretch pb-3">
<div className=" text-lg font-bold ">Message</div>
<div className="self-center">
<Image src={'/close.png'} width={18} height={18} alt="close" onClick={handleChatBox} />
</div>
</div>

<hr className="bg-black" />
{userListTable && (
<ChatUserList userList={userList} handleChatFromUserList={handleChatFromUserList} />
)}
{!userListTable && (
<>
<div className="overflow-y-auto pb-20 flex-grow ">
<div className="flex flex-col px-3">
{chatlist?.length > 0 &&
chatlist.map((item: Chat, index: number) => {
const conditionPotion =
userType === 'user'
? item.who === 'user'
? 'justify-end'
: 'justify-start'
: item.who === 'user'
? 'justify-start'
: 'justify-end'
const conditionBackground =
userType === 'user'
? item.who === 'user'
? 'bg-mainblue text-white'
: 'bg-slate-300'
: item.who === 'user'
? 'bg-slate-300'
: 'bg-mainblue text-white'

return (
<div key={index} className={`py-3 flex ${conditionPotion}`}>
<span className={`rounded-3xl px-4 py-2 ${conditionBackground}`}>
{item.message_content}
</span>
</div>
)
})}
</div>
</div>
<div className="py-2 insent-x-0 bottom-0 lg:flex lg:justify-between items-stretch">
<input
type="text"
placeholder="Text Message"
value={message}
onChange={handleMessageChange}
className="px-3 py-3 w-full "
/>
<Button
className="ml-2 bg-slate-300 rounded-md text-slate-500 self-center "
onClick={() => {
message && handlePressSend()
}}
>
Send
</Button>
</div>
</>
)}
</div>
)
}

ChatBox.displayName = 'ChatBox'

export { ChatBox }
3 changes: 3 additions & 0 deletions src/components/user-dashboard/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import { ColumnDef } from '@tanstack/react-table'

export type TUserDashboardTable = {
// Provider is mainly what (global eg. Plumber, Electrician, etc) service do they provide
service_id: string
provider_id: string
provider: string
short_description: string
pricing: string
availability: string
service_image_url: string
}

export const columns: ColumnDef<TUserDashboardTable>[] = [
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,6 @@ export const getUserProfile = async ({ token }: handleTokenProps) => {
username: username,
profileId: profileId,
userId: userId,
userType: userRes?.message?.user_type,
}
}
Loading

0 comments on commit c0b075c

Please sign in to comment.