Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OV-42: Add Chat Component #82

Merged
merged 81 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
3c6293f
OV-42: + files structures for a chat box component
lfelix3011 Aug 23, 2024
4047a6b
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Aug 23, 2024
7ffad94
OV-42: + files structures for a chat box component
lfelix3011 Aug 26, 2024
dff5b3b
OV-42: * files structures for a chat box component
lfelix3011 Aug 26, 2024
6780ebb
OV-42: - files structures for a chat box component
lfelix3011 Aug 26, 2024
9e2d1e4
OV-42: + files types, enums and validations in bundle
lfelix3011 Aug 26, 2024
e2e0ee7
OV-42: * files types, enums and validations in bundle front
lfelix3011 Aug 26, 2024
8f4afac
OV-42: * chat structure to send message to ai
lfelix3011 Aug 27, 2024
3dbd725
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Aug 27, 2024
36f2c02
OV-42: * chat structure to send message to ai to manage state with redux
lfelix3011 Aug 28, 2024
252fe70
OV-42: * Improvement in chat strcuture and funcionality
lfelix3011 Aug 28, 2024
2c36868
OV-42: * adjust to send message and store values using redux
lfelix3011 Aug 28, 2024
ba0591b
OV-42: * adjust to uses the same name that the pr of the backend has …
lfelix3011 Aug 28, 2024
2713f1b
OV-42: * adjust to uses all general component form Componets forlder
lfelix3011 Aug 28, 2024
1890a4b
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Aug 29, 2024
0d615a8
OV-42: * adjust to put as it was originally
lfelix3011 Aug 29, 2024
f861789
OV-42: * Use Box as="img" to avoid using html tags
lfelix3011 Aug 29, 2024
8d79ee3
OV-42: * Use Image Component of ChakraUI
lfelix3011 Aug 29, 2024
43948aa
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 1, 2024
8b1dc7d
OV-42: - chat deleteed request as backend dosent need it
lfelix3011 Sep 1, 2024
9251d98
OV-42: - onchange on input as formik manage that change
lfelix3011 Sep 3, 2024
0f3870c
OV-42: - aditional path as it just necessary to use ROOT and HTTP Met…
lfelix3011 Sep 3, 2024
65b554e
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 3, 2024
4d7144d
OV-42: - remove chat form and just leave chat footer
lfelix3011 Sep 3, 2024
a94a016
OV-42: * use of generic component user avatar
lfelix3011 Sep 3, 2024
385a7e4
OV-42: * Use of custom variant for headers
lfelix3011 Sep 3, 2024
1ae5406
OV-42: * Unified logic to manage messages respond and request to show…
lfelix3011 Sep 3, 2024
45186cc
OV-42: * property name from message to generatedText
lfelix3011 Sep 3, 2024
52edee7
OV-42: * use of httpMehtods insted of plain string
lfelix3011 Sep 3, 2024
dc6316f
OV-42: * use of relative path for imports
lfelix3011 Sep 3, 2024
6df9209
OV-42: * use of relative path for imports and useMemo to group Messages
lfelix3011 Sep 3, 2024
7cefade
OV-42: + custom hook to clean up chat
lfelix3011 Sep 3, 2024
b44e8df
OV-42: * use of relative path for imports
lfelix3011 Sep 3, 2024
e39565a
OV-42: + keepAlaive and credentials for request, to use in chat app
lfelix3011 Sep 3, 2024
2911244
OV-42: * message box lenght and space
lfelix3011 Sep 3, 2024
1c36f42
OV-42: * use of prettier
lfelix3011 Sep 3, 2024
4eb0965
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 3, 2024
1fd6e86
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 5, 2024
6d11703
OV-144: + initial helper to transofrm payload to text to AI
lfelix3011 Sep 5, 2024
1eecedd
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 5, 2024
62d62ce
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 5, 2024
087b3ac
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 5, 2024
82d80b9
OV-144: + generate text with AI using chat component
lfelix3011 Sep 6, 2024
0aa2b36
OV-166: + add mock icon
JKaypa Sep 6, 2024
45eeef3
Merge remote-tracking branch 'origin/task/OV-42-add-chat-component' i…
JKaypa Sep 6, 2024
2e5906d
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 6, 2024
c95d555
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 7, 2024
35b598e
OV-144: * reponse to be an object of title and description insted of …
lfelix3011 Sep 7, 2024
e932afe
OV-166: * merge
JKaypa Sep 8, 2024
ad4a90c
OV-166: + integreate chat modal
JKaypa Sep 9, 2024
b11249e
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 9, 2024
2257349
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 9, 2024
61eddae
OV-42: * styles adjustments to not use sx
lfelix3011 Sep 9, 2024
297ad8e
OV-42: * use of prettier
lfelix3011 Sep 9, 2024
6c7ac8b
Merge remote-tracking branch 'origin/task/OV-42-add-chat-component' i…
JKaypa Sep 9, 2024
1a3ee35
Merge branch 'task/OV-42-add-chat-component' of github.com:BinaryStud…
JKaypa Sep 9, 2024
59ac100
OV-166: + add open ai icon
JKaypa Sep 9, 2024
2f9ff34
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 9, 2024
043bfc1
OV-144: * genearete text with corresponding animation and visual
lfelix3011 Sep 10, 2024
ea8967b
OV-42: - unsued params
lfelix3011 Sep 10, 2024
eb48f1f
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 10, 2024
c79bbad
OV-166: * make sugested changes
JKaypa Sep 10, 2024
94875b9
OV-42: * use general loader when waiting respond
lfelix3011 Sep 10, 2024
d0f9ceb
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 10, 2024
2d9f332
OV-42: * adjust in styles format and use of general loader
lfelix3011 Sep 10, 2024
ca317b6
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 10, 2024
0d490c0
Merge pull request #225 from BinaryStudioAcademy/task/OV-166-add-ai-i…
nikita-remeslov Sep 11, 2024
44d1e65
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 11, 2024
427079c
OV-144: * suggestion, to make code simplier, and tone not required
lfelix3011 Sep 11, 2024
62aa93b
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 11, 2024
3176164
OV-144: * styles that were remove on last merge
lfelix3011 Sep 11, 2024
71eab4a
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 11, 2024
809d2d8
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 11, 2024
4911635
Merge branch 'next' of https://github.com/BinaryStudioAcademy/bsa-202…
lfelix3011 Sep 11, 2024
b2299a0
OV-42: * use correct loader and avatars
lfelix3011 Sep 11, 2024
9a43874
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 11, 2024
c94df94
OV-42: * simplify styles for input and button
lfelix3011 Sep 11, 2024
e5413b8
Merge branch 'task/OV-42-add-chat-component' of https://github.com/Bi…
lfelix3011 Sep 11, 2024
8677bcf
OV-144: * use route form bundle insted of relative import, and chain …
lfelix3011 Sep 12, 2024
ab60c04
Merge pull request #228 from BinaryStudioAcademy/task/OV-144-add-scri…
nikita-remeslov Sep 12, 2024
9e9ba40
OV-42: + merge
sergiy4 Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions backend/src/bundles/chat/chat.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { HttpCode, HTTPMethod } from '~/common/http/http.js';
import { type Logger } from '~/common/logger/logger.js';
import { MAX_TOKEN } from '~/common/services/open-ai/libs/constants/constants.js';
import {
ChatPath,
ChatApiPath,
OpenAIRole,
} from '~/common/services/open-ai/libs/enums/enums.js';
import { type OpenAIService } from '~/common/services/open-ai/open-ai.service.js';
Expand All @@ -34,7 +34,7 @@ class ChatController extends BaseController {
this.chatService = chatService;

this.addRoute({
path: ChatPath.ROOT,
path: ChatApiPath.ROOT,
method: HTTPMethod.POST,
validation: {
body: textGenerationValidationSchema,
Expand All @@ -49,7 +49,7 @@ class ChatController extends BaseController {
});

this.addRoute({
path: ChatPath.ROOT,
path: ChatApiPath.ROOT,
method: HTTPMethod.DELETE,
handler: (options) =>
this.deleteSession(
Expand Down
2 changes: 1 addition & 1 deletion backend/src/common/services/open-ai/libs/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { OpenAIRole } from './open-ai-role.enum.js';
export { ChatPath } from 'shared';
export { ChatApiPath } from 'shared';
55 changes: 55 additions & 0 deletions frontend/src/bundles/chat/chat-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ApiPath, ContentType } from '~/bundles/common/enums/enums.js';
import { type Http } from '~/framework/http/http.js';
import { BaseHttpApi } from '~/framework/http-api/http-api.js';
import { type Storage } from '~/framework/storage/storage.js';

import { ChatApiPath } from './enums/enums.js';
import {
type DeleteChatResponseDto,
type GenerateTextRequestDto,
type GenerateTextResponseDto,
} from './types/types.js';

type Constructor = {
baseUrl: string;
http: Http;
storage: Storage;
};

class ChatApi extends BaseHttpApi {
public constructor({ baseUrl, http, storage }: Constructor) {
super({ path: ApiPath.CHAT, baseUrl, http, storage });
}

public async sendMessage(
payload: GenerateTextRequestDto,
): Promise<GenerateTextResponseDto> {
const response = await this.load(
this.getFullEndpoint(ChatApiPath.ROOT, {}),
{
method: 'POST',
contentType: ContentType.JSON,
payload: JSON.stringify(payload),
hasAuth: true,
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
},
);

return await response.json<GenerateTextResponseDto>();
}

public async deleteChat(): Promise<DeleteChatResponseDto> {
const response = await this.load(
this.getFullEndpoint(ChatApiPath.ROOT, {}),
{
method: 'DELETE',
contentType: ContentType.JSON,
payload: JSON.stringify({}),
o-nedashkivska marked this conversation as resolved.
Show resolved Hide resolved
hasAuth: true,
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
},
);

return await response.json<DeleteChatResponseDto>();
}
}

export { ChatApi };
15 changes: 15 additions & 0 deletions frontend/src/bundles/chat/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { config } from '~/framework/config/config.js';
import { http } from '~/framework/http/http.js';
import { storage } from '~/framework/storage/storage.js';

import { ChatApi } from './chat-api.js';

const chatApi = new ChatApi({
baseUrl: config.ENV.API.ORIGIN_URL,
storage,
http,
});

export { chatApi };
export { type GenerateTextRequestDto } from './types/types.js';
export { textGenerationValidationSchema } from './validation-schemas/validation-schemas.js';
18 changes: 18 additions & 0 deletions frontend/src/bundles/chat/components/chat-body/chat-body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Box } from '~/bundles/common/components/components.js';

import { type Message } from '../../types/types.js';
import { MessageList } from '../message-list/message-list.js';
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved

type Properties = {
messages: Message[];
};

const ChatBody: React.FC<Properties> = ({ messages }) => {
return (
<Box sx={{ minH: '400px', maxH: '400px', overflowY: 'auto', mb: '2' }}>
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
<MessageList messages={messages} />
</Box>
);
};

export { ChatBody };
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
Button,
Flex,
FormProvider,
Input,
} from '~/bundles/common/components/components.js';
import { useAppForm, useMemo } from '~/bundles/common/hooks/hooks.js';

import {
type GenerateTextRequestDto,
textGenerationValidationSchema,
} from '../../chat.js';
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
import { DEFAULT_CHAT_FORM_PAYLOAD } from './constants/constants.js';

type Properties = {
onSendMessage: (payload: GenerateTextRequestDto) => void;
};

const ChatFooter: React.FC<Properties> = ({ onSendMessage }) => {
const form = useAppForm<GenerateTextRequestDto>({
initialValues: DEFAULT_CHAT_FORM_PAYLOAD,
validationSchema: textGenerationValidationSchema,
onSubmit: (data: GenerateTextRequestDto, { resetForm }) => {
onSendMessage(data);
resetForm();
},
});
const { handleSubmit, values } = form;

const isEmpty = useMemo(
() => Object.values(values).some((value) => value.trim().length === 0),
[values],
);

return (
<FormProvider value={form}>
<form onSubmit={handleSubmit}>
<Flex alignItems={'flex-start'} w={'100%'} gap={5}>
<Input
type="text"
label=""
placeholder="Send a message"
name="message"
value={values.message}
sx={{ w: '100%' }}
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
/>
<Button
type="submit"
label="Send"
size="md"
isDisabled={isEmpty}
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
sx={{ w: '100px' }}
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
/>
</Flex>
</form>
</FormProvider>
);
};

export { ChatFooter };
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type GenerateTextRequestDto } from '~/bundles/chat/types/types.js';

const DEFAULT_CHAT_FORM_PAYLOAD: GenerateTextRequestDto = {
message: '',
};

export { DEFAULT_CHAT_FORM_PAYLOAD };
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Box, Heading, Text } from '~/bundles/common/components/components.js';

type Properties = {
title: string;
comment: string;
};

const ChatHeader: React.FC<Properties> = ({ title, comment }) => {
return (
<Box
sx={{
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
bg: 'background.600',
p: '20px',
color: 'white',
borderTopLeftRadius: 'xl',
borderTopRightRadius: 'xl',
}}
>
<Heading variant="H2" mb={2}>
{title}
</Heading>
<Text variant="bodySmall">{comment}</Text>
</Box>
);
};

export { ChatHeader };
35 changes: 35 additions & 0 deletions frontend/src/bundles/chat/components/chat/chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Box } from '~/bundles/common/components/components.js';

import {
type GenerateTextRequestDto,
type Message,
} from '../../types/types.js';
import { ChatBody } from '../chat-body/chat-body.js';
import { ChatFooter } from '../chat-footer/chat-footer.js';
import { ChatHeader } from '../chat-header/chat-header.js';
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved

type Properties = {
messages: Message[];
onSendMessage: (payload: GenerateTextRequestDto) => void;
headerTitle: string;
headerComment: string;
};

const Chat: React.FC<Properties> = ({
messages,
onSendMessage,
headerTitle,
headerComment,
}) => {
return (
<Box>
<ChatHeader title={headerTitle} comment={headerComment} />
<Box sx={{ p: '10' }}>
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
<ChatBody messages={messages} />
<ChatFooter onSendMessage={onSendMessage} />
</Box>
</Box>
);
};

export { Chat };
1 change: 1 addition & 0 deletions frontend/src/bundles/chat/components/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Chat } from './chat/chat.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Box, Flex, Text } from '~/bundles/common/components/components.js';

import { MessageSender } from '../../enums/enums.js';
import { type Message } from '../../types/types.js';
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved

type Properties = {
message: Message;
};

const MessageBox: React.FC<Properties> = ({ message }) => {
const { sender, text } = message;

return (
<Box
alignSelf={
sender === MessageSender.USER ? 'flex-start' : 'flex-end'
}
width="100%"
>
<Flex
ml={sender === MessageSender.USER ? 0 : 3}
mr={sender === MessageSender.USER ? 3 : 0}
direction={'column'}
alignItems={
sender === MessageSender.USER ? 'flex-end' : 'flex-start'
}
>
<Text
color={'black'}
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
bg={'background.50'}
p={3}
borderRadius="md"
width={'max'}
>
{text}
</Text>
</Flex>
</Box>
);
};

export { MessageBox };
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Flex, VStack } from '~/bundles/common/components/components.js';
import { UserAvatar } from '~/bundles/users/components/components.js';

import { MessageSender } from '../../enums/enums.js';
import { type Message, type MessageGroup } from '../../types/types.js';
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
import { MessageBox } from '../message-box/message-box.js';
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved

type Properties = {
messages: Message[];
};

const MessageList: React.FC<Properties> = ({ messages }) => {
const groupedMessages: MessageGroup[] = [];
let currentGroup: MessageGroup | null = null;

for (const message of messages) {
const { sender } = message;
if (!currentGroup || currentGroup.sender !== sender) {
currentGroup = { sender: sender, messages: [message] };
groupedMessages.push(currentGroup);
} else {
currentGroup.messages.push(message);
}
}
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved

return (
<VStack spacing={4} align="stretch" p={4} h="400px" overflowY="auto">
{groupedMessages.map((group: MessageGroup, groupIndex: number) => {
const { sender, messages } = group;
lfelix3011 marked this conversation as resolved.
Show resolved Hide resolved
return (
<Flex
key={groupIndex}
direction={
sender === MessageSender.USER
? 'row-reverse'
: 'row'
}
alignItems="flex-start"
>
<UserAvatar
username={
sender === MessageSender.USER ? 'FL' : 'AI'
}
/>
<VStack
spacing={2}
align={
sender === MessageSender.USER
? 'flex-end'
: 'flex-start'
}
width={'max'}
>
{messages.map((message: Message, index: number) => (
<MessageBox key={index} message={message} />
))}
</VStack>
</Flex>
);
})}
</VStack>
);
};

export { MessageList };
2 changes: 2 additions & 0 deletions frontend/src/bundles/chat/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { MessageSender } from './message-sender.js';
export { ChatApiPath } from 'shared';
6 changes: 6 additions & 0 deletions frontend/src/bundles/chat/enums/message-sender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const MessageSender = {
USER: 'USER',
AI: 'AI',
} as const;

export { MessageSender };
Loading
Loading