Skip to content

Commit

Permalink
chore: change ui
Browse files Browse the repository at this point in the history
  • Loading branch information
Olutunde22 committed Jan 4, 2024
1 parent 16f57ef commit 8932c59
Show file tree
Hide file tree
Showing 18 changed files with 1,244 additions and 386 deletions.
26 changes: 15 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/avatar.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ayesha of Nigcomsat</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/avatar.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.3.0/model-viewer.min.js"></script>
<title>Ayesha of Nigcomsat</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@google/model-viewer": "^3.3.0",
"@heroicons/react": "^2.0.18",
"@tanstack/react-query": "^5.7.0",
"axios": "^1.6.0",
Expand All @@ -19,8 +20,11 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.48.2",
"react-loader-spinner": "^5.3.4",
"react-router-dom": "^6.18.0",
"socket.io-client": "^4.7.2"
"react-markdown": "^9.0.1",
"react-router-dom": "^6.20.1",
"rehype-sanitize": "^6.0.0",
"socket.io-client": "^4.7.2",
"three": "^0.160.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
Expand Down
Binary file removed public/avatar.png
Binary file not shown.
Binary file modified src/assets/avatar.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: 3 additions & 0 deletions src/assets/mic-filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/assets/mic-off.svg

This file was deleted.

2 changes: 1 addition & 1 deletion src/assets/send-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
267 changes: 123 additions & 144 deletions src/components/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,134 @@
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/solid';
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import { cx } from 'class-variance-authority';
import { useAtom } from 'jotai';
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import { ThreeDots } from 'react-loader-spinner';
import { connectionAtom, messageAtom } from '../state/atoms';
import { MessageType } from '../types';
import { useMutation } from "@tanstack/react-query";
import axios from "axios";
import { cx } from "class-variance-authority";
import { useAtom } from "jotai";
import React, { forwardRef, useEffect, useRef, useState } from "react";
import { ThreeDots } from "react-loader-spinner";
import { connectionAtom, messageAtom } from "../state/atoms";
import { MessageType } from "../types";
import ReactMarkdown from "react-markdown";
import rehypeSanitize from "rehype-sanitize";

type Message = {
message: MessageType;
createFeedback: (data: unknown) => void;
isLast?: boolean;
isFirst?: boolean;
isConnected?: boolean;
message: MessageType;
createFeedback: (data: unknown) => void;
isLast?: boolean;
isFirst?: boolean;
isConnected?: boolean;
};

const Message = forwardRef<HTMLDivElement, Message>(
({ message, isFirst, isLast, isConnected, createFeedback }, ref) => {
const [feedback, setFeedback] = useState<'like' | 'dislike' | undefined>();

const containerClass = cx('flex w-full', {
'justify-start rounded-md relative': message.role === 'assistant',
'animate-pop': message.role === 'assistant' && isLast && !message.typing,
'justify-end rounded-md': message.role === 'user',
});

const itemClass = cx('p-4 dark:text-white text-black rounded-b-xl max-w-[35rem]', {
'rounded-tr-xl bg-[#ffcb0520] relative': message.role === 'assistant',
'rounded-tl-xl dark:bg-[#ffffff26] bg-[#00000026] ': message.role === 'user',
});

const nowTyping = message.typing && isLast;
const wasTyping = message.typing && !isLast;

const handleFeedback = (which: 'like' | 'dislike') => {
if (!isFirst) {
setFeedback(which);
createFeedback({
orgId: message.orgId,
sessionId: message.sessionId,
feedback: which,
});
}
};

if (wasTyping) return null;

return (
<section className={containerClass} ref={isLast ? ref : undefined}>
<span className={itemClass}>
{nowTyping ? (
<ThreeDots
height='20'
width='20'
radius='5'
color='white'
ariaLabel='three-dots-loading'
visible={true}
/>
) : (
<p>
{message.message}
<span className={cx({ hidden: isFirst || !isLast || !isConnected })}>
<button
className='absolute top-0 -right-5'
onClick={() => handleFeedback('like')}
disabled={!!feedback}
>
<HandThumbUpIcon
className={cx('h-5 w-5 text-gray-400 ', {
'text-green-300': feedback === 'like',
'hover:text-green-500': !feedback,
})}
/>
</button>
<button
className='absolute top-0 -right-12'
onClick={() => handleFeedback('dislike')}
disabled={!!feedback}
>
<HandThumbDownIcon
className={cx('h-5 w-5 text-gray-400', {
'text-red-300': feedback === 'dislike',
'hover:text-red-500': !feedback,
})}
/>
</button>
</span>
</p>
)}
</span>
</section>
);
},
({ message, isLast }, ref) => {
const containerClass = cx("flex w-full text-xs md:text-base", {
"justify-start rounded-md relative": message.role === "assistant",
"animate-pop": message.role === "assistant" && isLast && !message.typing,
"justify-end rounded-md": message.role === "user",
});

const itemClass = cx(
"p-4 text-white rounded-b-xl max-w-[80%] md:max-w-[35rem]",
{
"rounded-tr-xl bg-white bg-opacity-10 relative": message.role === "assistant",
"rounded-tl-xl user-message-gradient":
message.role === "user",
}
);

const nowTyping = message.typing && isLast;
const wasTyping = message.typing && !isLast;

if (wasTyping) return null;

return (
<section className={containerClass} ref={isLast ? ref : undefined}>
<span className={itemClass}>
{nowTyping ? (
<ThreeDots
height="20"
width="20"
radius="5"
color="white"
ariaLabel="three-dots-loading"
visible={true}
/>
) : (
<>
<ReactMarkdown
components={{
code(props) {
const { children } = props;
return <code className="font-sans">{children}</code>;
},
}}
className="[&>pre]:whitespace-pre-line"
rehypePlugins={[rehypeSanitize]}
>
{message.message}
</ReactMarkdown>
</>
)}
</span>
</section>
);
}
);

const Dialog: React.FC = () => {
const [messages] = useAtom(messageAtom);
const [{ totalDislikes, messenger, connected }, setConnection] = useAtom(connectionAtom);
const [isEscalated, setIsEscalated] = useState(false);

const { mutate: createFeedback } = useMutation({
mutationKey: ['createFeedback'],
mutationFn: (data: unknown) => axios.post(`${import.meta.env.VITE_BASE_URL}/feedback`, data),
onSuccess: () => {
setConnection((prev) => ({
...prev,
totalDislikes: prev?.totalDislikes ? prev.totalDislikes + 1 : 1,
}));
},
});

if (!isEscalated && totalDislikes === 3) {
setIsEscalated(true);
}

useEffect(() => {
if (isEscalated) {
setIsEscalated(false);
messenger?.call({
action: 'prompt',
adhoc: 'escalate-level-1',
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEscalated]);

const lastMessageRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (lastMessageRef.current) {
lastMessageRef.current.scrollIntoView({ behavior: 'smooth' });
}
}, [messages]);

return (
<div className='flex flex-1 flex-col gap-y-8 p-3 md:px-6 h-[75%] overflow-y-scroll scrollbar-hide'>
{messages.map((message, index, arr) => (
<Message
key={index}
message={message}
isFirst={index === 0}
isLast={index === arr.length - 1}
isConnected={connected}
createFeedback={createFeedback}
ref={lastMessageRef}
/>
))}
</div>
);
const [messages] = useAtom(messageAtom);
const [{ totalDislikes, messenger, connected }, setConnection] =
useAtom(connectionAtom);
const [isEscalated, setIsEscalated] = useState(false);

const { mutate: createFeedback } = useMutation({
mutationKey: ["createFeedback"],
mutationFn: (data: unknown) =>
axios.post(`${import.meta.env.VITE_BASE_URL}/feedback`, data),
onSuccess: () => {
setConnection((prev) => ({
...prev,
totalDislikes: prev?.totalDislikes ? prev.totalDislikes + 1 : 1,
}));
},
});

if (!isEscalated && totalDislikes === 3) {
setIsEscalated(true);
}

useEffect(() => {
if (isEscalated) {
setIsEscalated(false);
messenger?.call({
action: "prompt",
adhoc: "escalate-level-1",
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEscalated]);

const lastMessageRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (lastMessageRef.current) {
lastMessageRef.current.scrollIntoView({ behavior: "smooth" });
}
}, [messages]);

return (
<div className="flex flex-1 flex-col gap-y-4 md:gap-y-8 p-3 md:px-6 h-full overflow-y-scroll scrollbar-hide">
{messages.map((message, index, arr) => (
<Message
key={index}
message={message}
isFirst={index === 0}
isLast={index === arr.length - 1}
isConnected={connected}
createFeedback={createFeedback}
ref={lastMessageRef}
/>
))}
</div>
);
};

export default Dialog;
16 changes: 8 additions & 8 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ const Header: React.FC<Props> = ({ agentName }) => {

return (
<>
<div className='flex justify-between items-center p-4 sticky top-0 mb-5'>
<div className='flex items-center gap-5'>
<div className='w-14 h-14 rounded-full overflow-hidden bg-gray-100'>
<div className='flex justify-between items-center px-2 py-1 md:p-4 sticky top-0 mb-2 md:mb-5 lg:hidden'>
<div className='flex items-center gap-3 md:gap-5'>
<div className='w-8 h-8 md:w-14 md:h-14 rounded-full overflow-hidden bg-gray-100'>
<img src={avatar} alt='avatar' />
</div>
<div className='flex flex-col justify-between'>
<h1 className='font-bold text-xl dark:text-white text-black'>Ask {agentName}</h1>
<h1 className='font-bold text-sm md:text-xl text-white'>Ask {agentName}</h1>

{connected ? (
<Radio
Expand All @@ -47,12 +47,12 @@ const Header: React.FC<Props> = ({ agentName }) => {
wrapperClass='radio-wrapper'
/>
) : (
<div className='relative flex gap-x-3 '>
<div className='relative flex gap-x-1 md:gap-x-3 '>
<section>
<img src={wifiIcon} className='h-5 w-5 dark:invert-[100%]' alt='' />
<img src={cancelIcon} className='h-5 w-5 absolute top-0' alt='' />
<img src={wifiIcon} className='h-3 w-3 md:h-5 md:w-5 mt-1 md:mt-0 dark:invert-[100%]' alt='' />
<img src={cancelIcon} className='h-3 w-3 md:h-5 md:w-5 absolute top-1 md:top-0' alt='' />
</section>
<p className='leading-5 text-sm text-rose-500'>
<p className='leading-5 text-xs md:text-sm text-rose-500'>
Your chat session has ended due to inactivity.
</p>
</div>
Expand Down
Loading

0 comments on commit 8932c59

Please sign in to comment.