mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-12-14 17:03:29 +03:00
feat(frontend): disabled searchBar if no remaining credits or no brain selected (#2788)
# 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):
This commit is contained in:
parent
2e4b80138c
commit
bfdc5c8cf8
@ -6,12 +6,15 @@ import { Text } from "@tiptap/extension-text";
|
|||||||
import { Extension, useEditor } from "@tiptap/react";
|
import { Extension, useEditor } from "@tiptap/react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
|
||||||
|
|
||||||
import { useBrainMention } from "./useBrainMention";
|
import { useBrainMention } from "./useBrainMention";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const useCreateEditorState = (placeholder?: string) => {
|
export const useCreateEditorState = (placeholder?: string) => {
|
||||||
const { t } = useTranslation(["chat"]);
|
const { t } = useTranslation(["chat"]);
|
||||||
const { BrainMention, items } = useBrainMention();
|
const { BrainMention, items } = useBrainMention();
|
||||||
|
const { remainingCredits } = useUserSettingsContext();
|
||||||
|
|
||||||
const PreventEnter = Extension.create({
|
const PreventEnter = Extension.create({
|
||||||
addKeyboardShortcuts: () => {
|
addKeyboardShortcuts: () => {
|
||||||
@ -24,7 +27,7 @@ export const useCreateEditorState = (placeholder?: string) => {
|
|||||||
|
|
||||||
const editor = useEditor(
|
const editor = useEditor(
|
||||||
{
|
{
|
||||||
autofocus: true,
|
autofocus: !!remainingCredits,
|
||||||
onFocus: () => {
|
onFocus: () => {
|
||||||
editor?.commands.focus("end");
|
editor?.commands.focus("end");
|
||||||
},
|
},
|
||||||
|
@ -13,9 +13,11 @@
|
|||||||
.chat_wrapper {
|
.chat_wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: Spacings.$spacing05;
|
padding: Spacings.$spacing05;
|
||||||
|
|
||||||
&.with_brain {
|
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import { CurrentBrain } from "@/lib/components/CurrentBrain/CurrentBrain";
|
|||||||
import Icon from "@/lib/components/ui/Icon/Icon";
|
import Icon from "@/lib/components/ui/Icon/Icon";
|
||||||
import { LoaderIcon } from "@/lib/components/ui/LoaderIcon/LoaderIcon";
|
import { LoaderIcon } from "@/lib/components/ui/LoaderIcon/LoaderIcon";
|
||||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||||
|
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
|
||||||
|
|
||||||
import { ChatEditor } from "./components/ChatEditor/ChatEditor";
|
import { ChatEditor } from "./components/ChatEditor/ChatEditor";
|
||||||
import { useChatInput } from "./hooks/useChatInput";
|
import { useChatInput } from "./hooks/useChatInput";
|
||||||
@ -12,10 +13,11 @@ import styles from "./index.module.scss";
|
|||||||
export const ChatInput = (): JSX.Element => {
|
export const ChatInput = (): JSX.Element => {
|
||||||
const { setMessage, submitQuestion, generatingAnswer, message } =
|
const { setMessage, submitQuestion, generatingAnswer, message } =
|
||||||
useChatInput();
|
useChatInput();
|
||||||
|
const { remainingCredits } = useUserSettingsContext();
|
||||||
const { currentBrain } = useBrainContext();
|
const { currentBrain } = useBrainContext();
|
||||||
|
|
||||||
const handleSubmitQuestion = () => {
|
const handleSubmitQuestion = () => {
|
||||||
if (message.trim() !== "") {
|
if (message.trim() !== "" && remainingCredits && currentBrain) {
|
||||||
submitQuestion();
|
submitQuestion();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -30,12 +32,14 @@ export const ChatInput = (): JSX.Element => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={styles.chat_container}>
|
<div className={styles.chat_container}>
|
||||||
<CurrentBrain allowingRemoveBrain={false} />
|
<CurrentBrain
|
||||||
|
allowingRemoveBrain={false}
|
||||||
|
remainingCredits={remainingCredits}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`${styles.chat_wrapper} ${
|
||||||
${styles.chat_wrapper}
|
!remainingCredits ? styles.disabled : ""
|
||||||
${currentBrain ? styles.with_brain : ""}
|
}`}
|
||||||
`}
|
|
||||||
>
|
>
|
||||||
<ChatEditor
|
<ChatEditor
|
||||||
message={message}
|
message={message}
|
||||||
@ -49,7 +53,7 @@ export const ChatInput = (): JSX.Element => {
|
|||||||
name="followUp"
|
name="followUp"
|
||||||
size="large"
|
size="large"
|
||||||
color="accent"
|
color="accent"
|
||||||
disabled={!message}
|
disabled={!message || !remainingCredits || !currentBrain}
|
||||||
handleHover={true}
|
handleHover={true}
|
||||||
onClick={handleSubmitQuestion}
|
onClick={handleSubmitQuestion}
|
||||||
/>
|
/>
|
||||||
|
@ -53,23 +53,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.shortcuts_card_wrapper {
|
|
||||||
background-color: var(--background-2);
|
|
||||||
padding: Spacings.$spacing05;
|
|
||||||
gap: Spacings.$spacing03;
|
|
||||||
border-radius: Radius.$big;
|
|
||||||
|
|
||||||
.shortcut_wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: Spacings.$spacing02;
|
|
||||||
|
|
||||||
.shortcut {
|
|
||||||
color: var(--primary-0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,13 +72,6 @@ const Search = (): JSX.Element => {
|
|||||||
<SearchBar />
|
<SearchBar />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.shortcuts_card_wrapper}>
|
|
||||||
<div className={styles.shortcut_wrapper}>
|
|
||||||
<span>Press</span>
|
|
||||||
<span className={styles.shortcut}>@</span>
|
|
||||||
<span>to select a brain</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<UploadDocumentModal />
|
<UploadDocumentModal />
|
||||||
<AddBrainModal />
|
<AddBrainModal />
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
@use "styles/Spacings.module.scss";
|
@use "styles/Spacings.module.scss";
|
||||||
@use "styles/Typography.module.scss";
|
@use "styles/Typography.module.scss";
|
||||||
|
|
||||||
.current_brain_wrapper {
|
%header_style {
|
||||||
background-color: var(--background-2);
|
background-color: var(--background-2);
|
||||||
padding-inline: Spacings.$spacing05;
|
padding-inline: Spacings.$spacing05;
|
||||||
padding-block: Spacings.$spacing01;
|
padding-block: Spacings.$spacing01;
|
||||||
font-size: Typography.$small;
|
font-size: Typography.$small;
|
||||||
color: var(--text-1);
|
}
|
||||||
|
|
||||||
|
@mixin textColor($color) {
|
||||||
|
color: var(--#{$color});
|
||||||
|
}
|
||||||
|
|
||||||
|
.current_brain_wrapper {
|
||||||
|
@extend %header_style;
|
||||||
|
@include textColor(text-1);
|
||||||
|
|
||||||
.brain_infos {
|
.brain_infos {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -20,7 +28,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
@include Typography.EllipsisOverflow;
|
@include Typography.EllipsisOverflow;
|
||||||
|
|
||||||
.title {
|
.title,
|
||||||
|
.brain_name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,10 +44,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brain_name {
|
.brain_name {
|
||||||
color: var(--text-3);
|
@include textColor(text-3);
|
||||||
@include Typography.EllipsisOverflow;
|
@include Typography.EllipsisOverflow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no_brain_selected,
|
||||||
|
.no_credits_left {
|
||||||
|
@extend %header_style;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no_brain_selected {
|
||||||
|
@include textColor(warning);
|
||||||
|
|
||||||
|
.strong {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no_credits_left {
|
||||||
|
@include textColor(dangerous);
|
||||||
|
}
|
||||||
|
@ -9,10 +9,12 @@ import { Icon } from "../ui/Icon/Icon";
|
|||||||
|
|
||||||
interface CurrentBrainProps {
|
interface CurrentBrainProps {
|
||||||
allowingRemoveBrain: boolean;
|
allowingRemoveBrain: boolean;
|
||||||
|
remainingCredits: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CurrentBrain = ({
|
export const CurrentBrain = ({
|
||||||
allowingRemoveBrain,
|
allowingRemoveBrain,
|
||||||
|
remainingCredits,
|
||||||
}: CurrentBrainProps): JSX.Element => {
|
}: CurrentBrainProps): JSX.Element => {
|
||||||
const { currentBrain, setCurrentBrainId } = useBrainContext();
|
const { currentBrain, setCurrentBrainId } = useBrainContext();
|
||||||
const { isDarkMode } = useUserSettingsContext();
|
const { isDarkMode } = useUserSettingsContext();
|
||||||
@ -20,8 +22,24 @@ export const CurrentBrain = ({
|
|||||||
setCurrentBrainId(null);
|
setCurrentBrainId(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!remainingCredits) {
|
||||||
|
return (
|
||||||
|
<div className={styles.no_credits_left}>
|
||||||
|
<span>
|
||||||
|
You’ve run out of credits! Upgrade your plan now to continue chatting.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentBrain) {
|
if (!currentBrain) {
|
||||||
return <></>;
|
return (
|
||||||
|
<div className={styles.no_brain_selected}>
|
||||||
|
<span>
|
||||||
|
Press <strong className={styles.strong}>@</strong> to select a Brain
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -18,9 +18,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: Spacings.$spacing05;
|
padding: Spacings.$spacing05;
|
||||||
|
|
||||||
&.with_brain {
|
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search_icon {
|
.search_icon {
|
||||||
|
@ -6,6 +6,7 @@ import { useChatInput } from "@/app/chat/[chatId]/components/ActionsBar/componen
|
|||||||
import { useChat } from "@/app/chat/[chatId]/hooks/useChat";
|
import { useChat } from "@/app/chat/[chatId]/hooks/useChat";
|
||||||
import { useChatContext } from "@/lib/context";
|
import { useChatContext } from "@/lib/context";
|
||||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||||
|
import { useUserSettingsContext } from "@/lib/context/UserSettingsProvider/hooks/useUserSettingsContext";
|
||||||
|
|
||||||
import styles from "./SearchBar.module.scss";
|
import styles from "./SearchBar.module.scss";
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ export const SearchBar = ({
|
|||||||
const { setMessages } = useChatContext();
|
const { setMessages } = useChatContext();
|
||||||
const { addQuestion } = useChat();
|
const { addQuestion } = useChat();
|
||||||
const { currentBrain, setCurrentBrainId } = useBrainContext();
|
const { currentBrain, setCurrentBrainId } = useBrainContext();
|
||||||
|
const { remainingCredits } = useUserSettingsContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentBrainId(null);
|
setCurrentBrainId(null);
|
||||||
@ -33,7 +35,7 @@ export const SearchBar = ({
|
|||||||
}, [message]);
|
}, [message]);
|
||||||
|
|
||||||
const submit = async (): Promise<void> => {
|
const submit = async (): Promise<void> => {
|
||||||
if (!searching) {
|
if (!!remainingCredits && !!currentBrain && !searching) {
|
||||||
setSearching(true);
|
setSearching(true);
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
try {
|
try {
|
||||||
@ -50,18 +52,15 @@ export const SearchBar = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={styles.search_bar_wrapper}>
|
||||||
|
<CurrentBrain
|
||||||
|
allowingRemoveBrain={true}
|
||||||
|
remainingCredits={remainingCredits}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`${styles.editor_wrapper} ${
|
||||||
${styles.search_bar_wrapper}
|
!remainingCredits ? styles.disabled : ""
|
||||||
${currentBrain ? styles.with_brain : ""}
|
}`}
|
||||||
`}
|
|
||||||
>
|
|
||||||
<CurrentBrain allowingRemoveBrain={true} />
|
|
||||||
<div
|
|
||||||
className={`
|
|
||||||
${styles.editor_wrapper}
|
|
||||||
${currentBrain ? styles.with_brain : ""}
|
|
||||||
`}
|
|
||||||
>
|
>
|
||||||
<Editor
|
<Editor
|
||||||
message={message}
|
message={message}
|
||||||
@ -75,7 +74,11 @@ export const SearchBar = ({
|
|||||||
<LuSearch
|
<LuSearch
|
||||||
className={`
|
className={`
|
||||||
${styles.search_icon}
|
${styles.search_icon}
|
||||||
${isDisabled ? styles.disabled : ""}
|
${
|
||||||
|
isDisabled || !remainingCredits || !currentBrain
|
||||||
|
? styles.disabled
|
||||||
|
: ""
|
||||||
|
}
|
||||||
`}
|
`}
|
||||||
onClick={() => void submit()}
|
onClick={() => void submit()}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user