mirror of
https://github.com/StanGirard/quivr.git
synced 2024-12-01 21:57:51 +03:00
feat(frontend & backend): thumbs for message feedback (#2360)
# Description Please include a summary of the changes and the related issue. Please also include relevant motivation and context. ## Checklist before requesting a review Please delete options that are not relevant. - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented hard-to-understand areas - [ ] I have ideally added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged ## Screenshots (if appropriate): --------- Co-authored-by: Stan Girard <girard.stanislas@gmail.com>
This commit is contained in:
parent
d7d1a0155b
commit
da8e7513e6
1
.gitignore
vendored
1
.gitignore
vendored
@ -81,3 +81,4 @@ airwallexpayouts.py
|
|||||||
application.log
|
application.log
|
||||||
backend/celerybeat-schedule.db
|
backend/celerybeat-schedule.db
|
||||||
|
|
||||||
|
backend/application.log.*
|
||||||
|
@ -13,6 +13,7 @@ from modules.brain.service.brain_service import BrainService
|
|||||||
from modules.chat.controller.chat.brainful_chat import BrainfulChat
|
from modules.chat.controller.chat.brainful_chat import BrainfulChat
|
||||||
from modules.chat.dto.chats import ChatItem, ChatQuestion
|
from modules.chat.dto.chats import ChatItem, ChatQuestion
|
||||||
from modules.chat.dto.inputs import (
|
from modules.chat.dto.inputs import (
|
||||||
|
ChatMessageProperties,
|
||||||
ChatUpdatableProperties,
|
ChatUpdatableProperties,
|
||||||
CreateChatProperties,
|
CreateChatProperties,
|
||||||
QuestionAndAnswer,
|
QuestionAndAnswer,
|
||||||
@ -152,6 +153,32 @@ async def update_chat_metadata_handler(
|
|||||||
return chat_service.update_chat(chat_id=chat_id, chat_data=chat_data)
|
return chat_service.update_chat(chat_id=chat_id, chat_data=chat_data)
|
||||||
|
|
||||||
|
|
||||||
|
# update existing message
|
||||||
|
@chat_router.put(
|
||||||
|
"/chat/{chat_id}/{message_id}", dependencies=[Depends(AuthBearer())], tags=["Chat"]
|
||||||
|
)
|
||||||
|
async def update_chat_message(
|
||||||
|
chat_message_properties: ChatMessageProperties,
|
||||||
|
chat_id: UUID,
|
||||||
|
message_id: UUID,
|
||||||
|
current_user: UserIdentity = Depends(get_current_user),
|
||||||
|
) :
|
||||||
|
|
||||||
|
chat = chat_service.get_chat_by_id(
|
||||||
|
chat_id # pyright: ignore reportPrivateUsage=none
|
||||||
|
)
|
||||||
|
if str(current_user.id) != chat.user_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403, # pyright: ignore reportPrivateUsage=none
|
||||||
|
detail="You should be the owner of the chat to update it.", # pyright: ignore reportPrivateUsage=none
|
||||||
|
)
|
||||||
|
return chat_service.update_chat_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
message_id=message_id,
|
||||||
|
chat_message_properties=chat_message_properties.dict(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# create new chat
|
# create new chat
|
||||||
@chat_router.post("/chat", dependencies=[Depends(AuthBearer())], tags=["Chat"])
|
@chat_router.post("/chat", dependencies=[Depends(AuthBearer())], tags=["Chat"])
|
||||||
async def create_chat_handler(
|
async def create_chat_handler(
|
||||||
|
@ -32,3 +32,14 @@ class ChatUpdatableProperties:
|
|||||||
|
|
||||||
def __init__(self, chat_name: Optional[str]):
|
def __init__(self, chat_name: Optional[str]):
|
||||||
self.chat_name = chat_name
|
self.chat_name = chat_name
|
||||||
|
|
||||||
|
|
||||||
|
class ChatMessageProperties(BaseModel, extra="ignore"):
|
||||||
|
thumbs: Optional[bool]
|
||||||
|
|
||||||
|
def dict(self, *args, **kwargs):
|
||||||
|
chat_dict = super().dict(*args, **kwargs)
|
||||||
|
if chat_dict.get("thumbs"):
|
||||||
|
# Set thumbs to boolean value or None if not present
|
||||||
|
chat_dict["thumbs"] = bool(chat_dict["thumbs"])
|
||||||
|
return chat_dict
|
||||||
|
@ -16,6 +16,7 @@ class GetChatHistoryOutput(BaseModel):
|
|||||||
None # string because UUID is not JSON serializable
|
None # string because UUID is not JSON serializable
|
||||||
)
|
)
|
||||||
metadata: Optional[dict] | None = None
|
metadata: Optional[dict] | None = None
|
||||||
|
thumbs: Optional[bool] | None = None
|
||||||
|
|
||||||
def dict(self, *args, **kwargs):
|
def dict(self, *args, **kwargs):
|
||||||
chat_history = super().dict(*args, **kwargs)
|
chat_history = super().dict(*args, **kwargs)
|
||||||
|
@ -27,6 +27,7 @@ class ChatHistory:
|
|||||||
prompt_id: Optional[UUID]
|
prompt_id: Optional[UUID]
|
||||||
brain_id: Optional[UUID]
|
brain_id: Optional[UUID]
|
||||||
metadata: Optional[dict] = None
|
metadata: Optional[dict] = None
|
||||||
|
thumbs: Optional[bool] = None
|
||||||
|
|
||||||
def __init__(self, chat_dict: dict):
|
def __init__(self, chat_dict: dict):
|
||||||
self.chat_id = chat_dict.get("chat_id", "")
|
self.chat_id = chat_dict.get("chat_id", "")
|
||||||
@ -38,6 +39,7 @@ class ChatHistory:
|
|||||||
self.prompt_id = chat_dict.get("prompt_id")
|
self.prompt_id = chat_dict.get("prompt_id")
|
||||||
self.brain_id = chat_dict.get("brain_id")
|
self.brain_id = chat_dict.get("brain_id")
|
||||||
self.metadata = chat_dict.get("metadata")
|
self.metadata = chat_dict.get("metadata")
|
||||||
|
self.thumbs = chat_dict.get("thumbs")
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return asdict(self)
|
return asdict(self)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from models.settings import get_supabase_client
|
from models.settings import get_supabase_client
|
||||||
|
from modules.chat.dto.inputs import ChatMessageProperties
|
||||||
from modules.chat.entity.chat import Chat
|
from modules.chat.entity.chat import Chat
|
||||||
from modules.chat.repository.chats_interface import ChatsInterface
|
from modules.chat.repository.chats_interface import ChatsInterface
|
||||||
|
|
||||||
@ -102,3 +103,13 @@ class Chats(ChatsInterface):
|
|||||||
|
|
||||||
def delete_chat_history(self, chat_id):
|
def delete_chat_history(self, chat_id):
|
||||||
self.db.table("chat_history").delete().match({"chat_id": chat_id}).execute()
|
self.db.table("chat_history").delete().match({"chat_id": chat_id}).execute()
|
||||||
|
|
||||||
|
def update_chat_message(self, chat_id, message_id, chat_message_properties: ChatMessageProperties ):
|
||||||
|
response = (
|
||||||
|
self.db.table("chat_history")
|
||||||
|
.update(chat_message_properties)
|
||||||
|
.match({"message_id": message_id, "chat_id": chat_id})
|
||||||
|
.execute()
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from modules.chat.dto.inputs import CreateChatHistory, QuestionAndAnswer
|
from modules.chat.dto.inputs import ChatMessageProperties, CreateChatHistory, QuestionAndAnswer
|
||||||
from modules.chat.entity.chat import Chat
|
from modules.chat.entity.chat import Chat
|
||||||
|
|
||||||
|
|
||||||
@ -78,3 +78,10 @@ class ChatsInterface(ABC):
|
|||||||
Delete chat history
|
Delete chat history
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update_chat_message(self, chat_id, message_id, chat_message_properties: ChatMessageProperties):
|
||||||
|
"""
|
||||||
|
Update chat message
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
@ -7,6 +7,7 @@ from logger import get_logger
|
|||||||
from modules.brain.service.brain_service import BrainService
|
from modules.brain.service.brain_service import BrainService
|
||||||
from modules.chat.dto.chats import ChatItem
|
from modules.chat.dto.chats import ChatItem
|
||||||
from modules.chat.dto.inputs import (
|
from modules.chat.dto.inputs import (
|
||||||
|
ChatMessageProperties,
|
||||||
ChatUpdatableProperties,
|
ChatUpdatableProperties,
|
||||||
CreateChatHistory,
|
CreateChatHistory,
|
||||||
CreateChatProperties,
|
CreateChatProperties,
|
||||||
@ -102,6 +103,7 @@ class ChatService:
|
|||||||
brain_id=str(brain.id) if brain else None,
|
brain_id=str(brain.id) if brain else None,
|
||||||
prompt_title=prompt.title if prompt else None,
|
prompt_title=prompt.title if prompt else None,
|
||||||
metadata=message.metadata,
|
metadata=message.metadata,
|
||||||
|
thumbs=message.thumbs,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return enriched_history
|
return enriched_history
|
||||||
@ -193,3 +195,14 @@ class ChatService:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def update_chat_message(
|
||||||
|
self, chat_id, message_id, chat_message_properties: ChatMessageProperties
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
return self.repository.update_chat_message(
|
||||||
|
chat_id, message_id, chat_message_properties
|
||||||
|
).data
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
pass
|
||||||
|
@ -16,6 +16,7 @@ export const QADisplay = ({ content, index }: QADisplayProps): JSX.Element => {
|
|||||||
prompt_title,
|
prompt_title,
|
||||||
metadata,
|
metadata,
|
||||||
brain_id,
|
brain_id,
|
||||||
|
thumbs,
|
||||||
} = content;
|
} = content;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,6 +37,8 @@ export const QADisplay = ({ content, index }: QADisplayProps): JSX.Element => {
|
|||||||
brainId={brain_id}
|
brainId={brain_id}
|
||||||
index={index}
|
index={index}
|
||||||
metadata={metadata} // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
metadata={metadata} // eslint-disable-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
messageId={message_id}
|
||||||
|
thumbs={thumbs}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useChat } from "@/app/chat/[chatId]/hooks/useChat";
|
||||||
|
import { useChatApi } from "@/lib/api/chat/useChatApi";
|
||||||
import { CopyButton } from "@/lib/components/ui/CopyButton";
|
import { CopyButton } from "@/lib/components/ui/CopyButton";
|
||||||
import Icon from "@/lib/components/ui/Icon/Icon";
|
import Icon from "@/lib/components/ui/Icon/Icon";
|
||||||
import { useChatContext } from "@/lib/context";
|
import { useChatContext } from "@/lib/context";
|
||||||
@ -23,6 +25,8 @@ type MessageRowProps = {
|
|||||||
};
|
};
|
||||||
brainId?: string;
|
brainId?: string;
|
||||||
index?: number;
|
index?: number;
|
||||||
|
messageId?: string;
|
||||||
|
thumbs?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MessageRow = React.forwardRef(
|
export const MessageRow = React.forwardRef(
|
||||||
@ -35,6 +39,8 @@ export const MessageRow = React.forwardRef(
|
|||||||
children,
|
children,
|
||||||
brainId,
|
brainId,
|
||||||
index,
|
index,
|
||||||
|
messageId,
|
||||||
|
thumbs: initialThumbs,
|
||||||
}: MessageRowProps,
|
}: MessageRowProps,
|
||||||
ref: React.Ref<HTMLDivElement>
|
ref: React.Ref<HTMLDivElement>
|
||||||
) => {
|
) => {
|
||||||
@ -44,9 +50,97 @@ export const MessageRow = React.forwardRef(
|
|||||||
});
|
});
|
||||||
const { setSourcesMessageIndex, sourcesMessageIndex } = useChatContext();
|
const { setSourcesMessageIndex, sourcesMessageIndex } = useChatContext();
|
||||||
const { isMobile } = useDevice();
|
const { isMobile } = useDevice();
|
||||||
|
const { updateChatMessage } = useChatApi();
|
||||||
|
const { chatId } = useChat();
|
||||||
|
const [thumbs, setThumbs] = useState<boolean | undefined | null>(
|
||||||
|
initialThumbs
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setThumbs(initialThumbs);
|
||||||
|
}, [initialThumbs]);
|
||||||
|
|
||||||
const messageContent = text ?? "";
|
const messageContent = text ?? "";
|
||||||
|
|
||||||
|
const thumbsUp = async () => {
|
||||||
|
if (chatId && messageId) {
|
||||||
|
await updateChatMessage(chatId, messageId, {
|
||||||
|
thumbs: thumbs ? null : true,
|
||||||
|
});
|
||||||
|
setThumbs(thumbs ? null : true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const thumbsDown = async () => {
|
||||||
|
if (chatId && messageId) {
|
||||||
|
await updateChatMessage(chatId, messageId, {
|
||||||
|
thumbs: thumbs === false ? null : false,
|
||||||
|
});
|
||||||
|
setThumbs(thumbs === false ? null : false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderMessageHeader = () => {
|
||||||
|
if (!isUserSpeaker) {
|
||||||
|
return (
|
||||||
|
<div className={styles.message_header}>
|
||||||
|
<QuestionBrain brainName={brainName} brainId={brainId} />
|
||||||
|
<QuestionPrompt promptName={promptName} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={styles.message_header}>
|
||||||
|
<Icon name="user" color="dark-grey" size="normal" />
|
||||||
|
<span className={styles.me}>Me</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderIcons = () => {
|
||||||
|
if (!isUserSpeaker && messageContent !== "🧠") {
|
||||||
|
return (
|
||||||
|
<div className={styles.icons_wrapper}>
|
||||||
|
<CopyButton handleCopy={handleCopy} size="normal" />
|
||||||
|
{!isMobile && (
|
||||||
|
<div className={styles.sources_icon_wrapper}>
|
||||||
|
<Icon
|
||||||
|
name="file"
|
||||||
|
handleHover={true}
|
||||||
|
color={sourcesMessageIndex === index ? "primary" : "black"}
|
||||||
|
size="normal"
|
||||||
|
onClick={() => {
|
||||||
|
setSourcesMessageIndex(
|
||||||
|
sourcesMessageIndex === index ? undefined : index
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Icon
|
||||||
|
name="thumbsUp"
|
||||||
|
handleHover={true}
|
||||||
|
color={thumbs ? "primary" : "black"}
|
||||||
|
size="normal"
|
||||||
|
onClick={async () => {
|
||||||
|
await thumbsUp();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
name="thumbsDown"
|
||||||
|
handleHover={true}
|
||||||
|
color={thumbs === false ? "primary" : "black"}
|
||||||
|
size="normal"
|
||||||
|
onClick={async () => {
|
||||||
|
await thumbsDown();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
@ -54,44 +148,12 @@ export const MessageRow = React.forwardRef(
|
|||||||
${isUserSpeaker ? styles.user : styles.brain}
|
${isUserSpeaker ? styles.user : styles.brain}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{!isUserSpeaker ? (
|
{renderMessageHeader()}
|
||||||
<div className={styles.message_header}>
|
|
||||||
<QuestionBrain brainName={brainName} brainId={brainId} />
|
|
||||||
<QuestionPrompt promptName={promptName} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={styles.message_header}>
|
|
||||||
<Icon name="user" color="dark-grey" size="normal" />
|
|
||||||
<span className={styles.me}>Me</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{}
|
|
||||||
<div ref={ref} className={styles.message_row_content}>
|
<div ref={ref} className={styles.message_row_content}>
|
||||||
{children ?? (
|
{children ?? (
|
||||||
<>
|
<>
|
||||||
<MessageContent text={messageContent} isUser={isUserSpeaker} />
|
<MessageContent text={messageContent} isUser={isUserSpeaker} />
|
||||||
{!isUserSpeaker && messageContent !== "🧠" && (
|
{renderIcons()}
|
||||||
<div className={styles.icons_wrapper}>
|
|
||||||
<CopyButton handleCopy={handleCopy} size="normal" />
|
|
||||||
{!isMobile && (
|
|
||||||
<div className={styles.sources_icon_wrapper}>
|
|
||||||
<Icon
|
|
||||||
name="file"
|
|
||||||
handleHover={true}
|
|
||||||
color={
|
|
||||||
sourcesMessageIndex === index ? "primary" : "black"
|
|
||||||
}
|
|
||||||
size="normal"
|
|
||||||
onClick={() => {
|
|
||||||
setSourcesMessageIndex(
|
|
||||||
sourcesMessageIndex === index ? undefined : index
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,6 +22,7 @@ export type ChatMessage = {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
sources?: Source[];
|
sources?: Source[];
|
||||||
};
|
};
|
||||||
|
thumbs?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type NotificationStatus = "Pending" | "Done";
|
type NotificationStatus = "Pending" | "Done";
|
||||||
|
@ -7,6 +7,14 @@ import {
|
|||||||
ChatQuestion,
|
ChatQuestion,
|
||||||
} from "@/app/chat/[chatId]/types";
|
} from "@/app/chat/[chatId]/types";
|
||||||
|
|
||||||
|
export type ChatUpdatableProperties = {
|
||||||
|
chat_name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChatMessageUpdatableProperties = {
|
||||||
|
thumbs?: boolean | null;
|
||||||
|
};
|
||||||
|
|
||||||
export const createChat = async (
|
export const createChat = async (
|
||||||
name: string,
|
name: string,
|
||||||
axiosInstance: AxiosInstance
|
axiosInstance: AxiosInstance
|
||||||
@ -59,9 +67,6 @@ export const getChatItems = async (
|
|||||||
): Promise<ChatItem[]> =>
|
): Promise<ChatItem[]> =>
|
||||||
(await axiosInstance.get<ChatItem[]>(`/chat/${chatId}/history`)).data;
|
(await axiosInstance.get<ChatItem[]>(`/chat/${chatId}/history`)).data;
|
||||||
|
|
||||||
export type ChatUpdatableProperties = {
|
|
||||||
chat_name?: string;
|
|
||||||
};
|
|
||||||
export const updateChat = async (
|
export const updateChat = async (
|
||||||
chatId: string,
|
chatId: string,
|
||||||
chat: ChatUpdatableProperties,
|
chat: ChatUpdatableProperties,
|
||||||
@ -70,3 +75,17 @@ export const updateChat = async (
|
|||||||
return (await axiosInstance.put<ChatEntity>(`/chat/${chatId}/metadata`, chat))
|
return (await axiosInstance.put<ChatEntity>(`/chat/${chatId}/metadata`, chat))
|
||||||
.data;
|
.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateChatMessage = async (
|
||||||
|
chatId: string,
|
||||||
|
messageId: string,
|
||||||
|
chatMessageUpdatableProperties: ChatMessageUpdatableProperties,
|
||||||
|
axiosInstance: AxiosInstance
|
||||||
|
): Promise<ChatItem> => {
|
||||||
|
return (
|
||||||
|
await axiosInstance.put<ChatItem>(
|
||||||
|
`/chat/${chatId}/${messageId}`,
|
||||||
|
chatMessageUpdatableProperties
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
};
|
||||||
|
@ -7,12 +7,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
addQuestion,
|
addQuestion,
|
||||||
AddQuestionParams,
|
AddQuestionParams,
|
||||||
|
ChatMessageUpdatableProperties,
|
||||||
ChatUpdatableProperties,
|
ChatUpdatableProperties,
|
||||||
createChat,
|
createChat,
|
||||||
deleteChat,
|
deleteChat,
|
||||||
getChatItems,
|
getChatItems,
|
||||||
getChats,
|
getChats,
|
||||||
updateChat,
|
updateChat,
|
||||||
|
updateChatMessage,
|
||||||
} from "./chat";
|
} from "./chat";
|
||||||
|
|
||||||
// TODO: split './chat.ts' into multiple files, per function for example
|
// TODO: split './chat.ts' into multiple files, per function for example
|
||||||
@ -33,5 +35,10 @@ export const useChatApi = () => {
|
|||||||
chatId: string,
|
chatId: string,
|
||||||
questionAndAnswer: QuestionAndAnwser
|
questionAndAnswer: QuestionAndAnwser
|
||||||
) => addQuestionAndAnswer(chatId, questionAndAnswer, axiosInstance),
|
) => addQuestionAndAnswer(chatId, questionAndAnswer, axiosInstance),
|
||||||
|
updateChatMessage: async (
|
||||||
|
chatId: string,
|
||||||
|
messageId: string,
|
||||||
|
props: ChatMessageUpdatableProperties
|
||||||
|
) => updateChatMessage(chatId, messageId, props, axiosInstance),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ interface IconProps {
|
|||||||
classname?: string;
|
classname?: string;
|
||||||
hovered?: boolean;
|
hovered?: boolean;
|
||||||
handleHover?: boolean;
|
handleHover?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon = ({
|
export const Icon = ({
|
||||||
|
@ -18,6 +18,8 @@ import {
|
|||||||
FaRegFileAlt,
|
FaRegFileAlt,
|
||||||
FaRegKeyboard,
|
FaRegKeyboard,
|
||||||
FaRegStar,
|
FaRegStar,
|
||||||
|
FaRegThumbsDown,
|
||||||
|
FaRegThumbsUp,
|
||||||
FaRegUserCircle,
|
FaRegUserCircle,
|
||||||
FaSun,
|
FaSun,
|
||||||
FaTwitter,
|
FaTwitter,
|
||||||
@ -114,6 +116,8 @@ export const iconList: { [name: string]: IconType } = {
|
|||||||
software: CgSoftwareDownload,
|
software: CgSoftwareDownload,
|
||||||
star: FaRegStar,
|
star: FaRegStar,
|
||||||
sun: FaSun,
|
sun: FaSun,
|
||||||
|
thumbsDown: FaRegThumbsDown,
|
||||||
|
thumbsUp: FaRegThumbsUp,
|
||||||
twitter: FaTwitter,
|
twitter: FaTwitter,
|
||||||
unlock: FaUnlock,
|
unlock: FaUnlock,
|
||||||
upload: FiUpload,
|
upload: FiUpload,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
create type "public"."user_identity_company_size" as enum ('1-10', '10-25', '25-50', '50-100', '100-250', '250-500', '500-1000', '1000-5000', '+5000');
|
||||||
|
|
||||||
|
alter table "public"."chat_history" drop column "user_feedback";
|
||||||
|
|
||||||
|
alter table "public"."chat_history" add column "thumbs" boolean;
|
||||||
|
|
||||||
|
alter table "public"."user_identity" add column "company_size" user_identity_company_size;
|
||||||
|
|
||||||
|
alter table "public"."user_identity" add column "usage_purpose" text;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user