feat(frontend): Add Brain On Search Page (#2067)

# Description

- Implement Icon Component
- Implement TextButton Component
- Change Add Brain Button And Set it in the Search Page
- Fix Errors When sending empty message
- Change EsLint rules

## 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:
Antoine Dewez 2024-01-22 17:37:45 -08:00 committed by GitHub
parent 921ab11067
commit b5e2d5ad9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 336 additions and 154 deletions

View File

@ -41,7 +41,9 @@ module.exports = {
["external", "builtin"],
"unknown",
"internal",
["parent", "sibling", "index"],
"sibling",
"parent",
"index",
],
alphabetize: {
order: "asc",

View File

@ -9,6 +9,7 @@ import { useAuthModes } from "@/lib/hooks/useAuthModes";
import { EmailInput } from "./components/EmailInput";
import { MagicLinkLogin } from "./components/MagicLinkLogin/MaginLinkLogin";
import { PasswordLogin } from "./components/PasswordLogin/PasswordLogin";
import { EmailAuthContextType } from "../../types";
export const EmailLogin = (): JSX.Element => {

View File

@ -1,7 +1,7 @@
import Link from "next/link";
import { Avatar } from "@/lib/components/ui/Avatar";
import { Testimonial } from "@/lib/types/testimonial";
import { Testimonial } from "@/lib/types/Testimonial";
import { socialMediaToIcon } from "../utils/socialMediaToIcon";

View File

@ -1,7 +1,7 @@
import { AiFillLinkedin } from "react-icons/ai";
import { RiTwitterXFill } from "react-icons/ri";
import { Testimonial } from "@/lib/types/testimonial";
import { Testimonial } from "@/lib/types/Testimonial";
export const socialMediaToIcon: Record<
Testimonial["socialMedia"],

View File

@ -5,6 +5,7 @@ import { LuChevronRight } from "react-icons/lu";
import Button from "@/lib/components/ui/Button";
import { UseCasesListing } from "./components/UseCasesListing/UseCasesListing";
import { useHomepageTracking } from "../../hooks/useHomepageTracking";
export const UseCases = (): JSX.Element => {

View File

@ -3,6 +3,7 @@ import { useEffect } from "react";
import { useUrlBrain } from "@/lib/hooks/useBrainIdFromUrl";
import { useFeedBrain } from "./useFeedBrain";
import { useKnowledge } from "../../../hooks/useKnowledge";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types

View File

@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import Spinner from "@/lib/components/ui/Spinner";
import { useAddedKnowledge } from "./hooks/useAddedKnowledge";
import { KnowledgeTable } from "../KnowledgeTable/KnowledgeTable";
type AddedKnowledgeProps = {

View File

@ -8,6 +8,7 @@ import { KnowledgeToFeedProvider } from "@/lib/context";
import { AddKnowledge } from "./components/AddKnowledge/AddKnowledge";
import { AddedKnowledge } from "./components/AddedKnowledge/AddedKnowledge";
import { useBrainFetcher } from "../../hooks/useBrainFetcher";
import { NoAccess } from "../NoAccess";

View File

@ -12,6 +12,7 @@ import { TextArea } from "@/lib/components/ui/TextArea";
import { BrainAccess } from "./components/BrainAccess/BrainAccess";
import { useGeneralInformation } from "./hooks/useGeneralInformation";
import { useBrainFormState } from "../../hooks/useBrainFormState";
type GeneralInformationProps = {

View File

@ -2,7 +2,7 @@ import { UseFormSetValue } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { LuLock, LuUnlock } from "react-icons/lu";
import { BrainConfig, BrainStatus } from "@/lib/types/brainConfig";
import { BrainConfig, BrainStatus } from "@/lib/types/BrainConfig";
import { BrainAccessRadio } from "./components/BrainAccessRadio";

View File

@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next";
import { BrainType } from "@/lib/types/brainConfig";
import { BrainType } from "@/lib/types/BrainConfig";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useGeneralInformation = () => {

View File

@ -6,7 +6,7 @@ import { useFormContext } from "react-hook-form";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { Brain } from "@/lib/context/BrainProvider/types";
import { useUrlBrain } from "@/lib/hooks/useBrainIdFromUrl";
import { BrainConfig } from "@/lib/types/brainConfig";
import { BrainConfig } from "@/lib/types/BrainConfig";
import { useBrainFetcher } from "../../../hooks/useBrainFetcher";

View File

@ -12,6 +12,7 @@ import { useToast } from "@/lib/hooks";
import { useUserData } from "@/lib/hooks/useUserData";
import { useBrainFormState } from "./useBrainFormState";
import { isBrainDescriptionValid } from "../utils/isBrainDescriptionValid";
import { isBrainNameValid } from "../utils/isBrainNameValid";

View File

@ -6,6 +6,7 @@ import { Tabs, TabsContent, TabsList } from "@/lib/components/ui/Tabs";
import { BrainSearchBar } from "./components/BrainSearchBar";
import { BrainsList } from "./components/BrainsList";
import { useBrainsTabs } from "./hooks/useBrainsTabs";
import { StyledTabsTrigger } from "../StyledTabsTrigger";
export const BrainsTabs = (): JSX.Element => {

View File

@ -11,8 +11,8 @@ const BrainsManagement = (): JSX.Element => {
const { t } = useTranslation("chat");
return (
<div className="flex flex-col flex-1">
<div className="w-full h-full p-6 bg-highlight flex flex-col flex-1 overflow-auto">
<div className="flex flex-col flex-1 bg-highlight">
<div className="w-full h-full p-6 flex flex-col flex-1 overflow-auto">
<div className="w-full mb-10">
<div className="flex flex-row justify-center items-center gap-2">
<LuBrain size={20} className="text-primary" />
@ -24,7 +24,7 @@ const BrainsManagement = (): JSX.Element => {
<BrainsTabs />
</div>
<div className="w-full flex justify-center py-4">
<AddBrainModal triggerClassName="bg-primary text-white font-normal" />
<AddBrainModal />
</div>
</div>
);

View File

@ -40,7 +40,7 @@ export const MentionList = forwardRef<MentionListRef, MentionListProps>(
/>
))}
</div>
<div className="relative">
<div className="relative flex justify-center items-center py-1">
{shouldShowScrollToBottomIcon && (
<FaAngleDoubleDown
size={20}

View File

@ -1,6 +1,7 @@
import { cn } from "@/lib/utils";
import { useMentionItemIcon } from "./hooks/useMentionItemIcon";
import { SuggestionDataType, SuggestionItem } from "../../../../types";
type MentionItemProps = {

View File

@ -1,6 +1,7 @@
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useMentionConfig } from "./useMentionConfig";
import { SuggestionItem } from "../types";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types

View File

@ -28,9 +28,7 @@ export const useChatStateUpdater = ({
const { text, brainId, promptId } =
getChatInputAttributesFromEditorState(editorNewState);
if (text !== "") {
setMessage(text);
}
setMessage(text);
if (brainId !== currentBrainId) {
if (brainId === "") {

View File

@ -1,6 +1,7 @@
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useMentionConfig } from "./useMentionConfig";
import { SuggestionItem } from "../types";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types

View File

@ -3,6 +3,7 @@ import { Fragment } from "react";
import { useOnboarding } from "@/lib/hooks/useOnboarding";
import { useOnboardingQuestion } from "./hooks/useOnboardingQuestion";
import { QuestionId } from "../../types";
type OnboardingQuestionsProps = {

View File

@ -7,6 +7,7 @@ import { useOnboardingTracker } from "@/lib/hooks/useOnboardingTracker";
import { useStreamText } from "@/lib/hooks/useStreamText";
import { stepsContainerStyle } from "./styles";
import { MessageRow } from "../QADisplay";
export const Onboarding = (): JSX.Element => {

View File

@ -10,6 +10,7 @@ import {
chatItemContainerClassName,
} from "./styles";
import { getKeyFromChatItem } from "./utils/getKeyFromChatItem";
import { ChatItemWithGroupedNotifications } from "../../types";
type MessagesDialogueProps = {

View File

@ -17,6 +17,7 @@ import { useEventTracking } from "@/services/analytics/june/useEventTracking";
import { useLocalStorageChatConfig } from "./useLocalStorageChatConfig";
import { useQuestion } from "./useQuestion";
import { ChatQuestion } from "../types";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types

View File

@ -5,6 +5,7 @@ import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainConte
import { useFetch, useToast } from "@/lib/hooks";
import { useHandleStream } from "./useHandleStream";
import { ChatQuestion } from "../types";
import { generatePlaceHolderMessage } from "../utils/generatePlaceHolderMessage";

View File

@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import Card from "@/lib/components/ui/Card";
import { ContactForm } from "./components";
import {
FooterSection,
HomeHeader,

View File

@ -1,43 +1,52 @@
@use '@/styles/Colors.module.scss';
@use '@/styles/IconSizes.module.scss';
@use '@/styles/ScreenSizes.module.scss';
@use '@/styles/Spacings.module.scss';
@use '@/styles/Typography.module.scss';
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@use "@/styles/Variables.module.scss";
.search_page_container {
background-color: Colors.$ivory;
width: 100%;
height: 100%;
background-color: Colors.$ivory;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.main_container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
row-gap: Spacings.$spacing05;
position: relative;
width: 50%;
margin-inline: auto;
transform: translateY(-#{Variables.$searchBarHeight});
.main_container {
display: flex;
flex-direction: column;
row-gap: Spacings.$spacing05;
position: relative;
width: 50vw;
margin-inline: auto;
@media (max-width: ScreenSizes.$small) {
width: 100%;
margin-inline: Spacings.$spacing07;
}
.quivr_logo_wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.quivr_text {
@include Typography.H1;
.quivr_text_primary {
color: Colors.$primary;
}
}
}
@media (max-width: ScreenSizes.$small) {
width: 100%;
margin-inline: Spacings.$spacing07;
}
}
.quivr_logo_wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.quivr_text {
@include Typography.H1;
.quivr_text_primary {
color: Colors.$primary;
}
}
}
}
.add_brain_wrapper {
position: absolute;
bottom: Spacings.$spacing05;
left: 50%;
transform: translateX(-50%);
}
}

View File

@ -3,44 +3,50 @@ import { usePathname } from "next/navigation";
import { useEffect } from "react";
import { QuivrLogo } from "@/lib/assets/QuivrLogo";
import { AddBrainModal } from "@/lib/components/AddBrainModal";
import { SearchBar } from "@/lib/components/ui/SearchBar/SearchBar";
import { useMenuContext } from "@/lib/context/MenuProvider/hooks/useMenuContext";
import { useSupabase } from "@/lib/context/SupabaseProvider";
import { redirectToLogin } from "@/lib/router/redirectToLogin";
import { useChatsList } from "../chat/[chatId]/hooks/useChatsList";
// eslint-disable-next-line import/order
import styles from "./page.module.scss";
import { useChatsList } from "../chat/[chatId]/hooks/useChatsList";
const Search = (): JSX.Element => {
const {setIsOpened} = useMenuContext();
const pathname = usePathname()
const { session } = useSupabase();
const { setIsOpened } = useMenuContext();
const pathname = usePathname();
const { session } = useSupabase();
useEffect(() => {
if (session === null) {
redirectToLogin();
}
setIsOpened(false)
}, [pathname, session, setIsOpened]);
useEffect(() => {
if (session === null) {
redirectToLogin();
}
setIsOpened(false);
}, [pathname, session, setIsOpened]);
useChatsList();
useChatsList();
return (
<div className={styles.search_page_container}>
<div className={styles.main_container}>
<div className={styles.quivr_logo_wrapper}>
<QuivrLogo size={80} color="black" />
<div className={styles.quivr_text}>
<span>Talk to </span>
<span className={styles.quivr_text_primary}>Quivr</span>
</div>
</div>
<SearchBar />
</div>
</div >
);
return (
<div className={styles.search_page_container}>
<div className={styles.main_container}>
<div className={styles.quivr_logo_wrapper}>
<QuivrLogo size={80} color="black" />
<div className={styles.quivr_text}>
<span>Talk to </span>
<span className={styles.quivr_text_primary}>Quivr</span>
</div>
</div>
<div className={styles.search_bar_wrapper}>
<SearchBar />
</div>
</div>
<div className={styles.add_brain_wrapper}>
<AddBrainModal />
</div>
</div>
);
};
export default Search;

View File

@ -4,6 +4,7 @@ import { testChat } from "./utils/testChat";
import { testDeleteChats } from "./utils/testDeleteChats";
import { testSelectBrain } from "./utils/testSelectBrain";
import { testUnplugChat } from "./utils/testUnplugChat";
import { login } from "../../utils/login";
export const chatTests = (): void => {

View File

@ -1,4 +1,5 @@
import { Page } from "@playwright/test";
import { getEditor } from "./getEditor";
export const testSelectBrain = async (page: Page): Promise<void> => {

View File

@ -1,4 +1,5 @@
import { Page } from "@playwright/test";
import { getEditor } from "./getEditor";
export const testUnplugChat = async (page: Page): Promise<void> => {

View File

@ -1,7 +1,7 @@
import { UUID } from "crypto";
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
import { BrainStatus, BrainType, Model } from "@/lib/types/brainConfig";
import { BrainStatus, BrainType, Model } from "@/lib/types/BrainConfig";
export type ApiBrainDefinitionSchemaPropertyType = "string" | "number";
@ -25,12 +25,12 @@ export type SubscriptionUpdatableProperties = {
export type ListFilesProps = {
files: {
file_name: string;
file_sha1: string;
file_size: number;
file_url: string;
file_id: string;
file_similarity: number;
file_name: string;
file_sha1: string;
file_size: number;
file_url: string;
file_id: string;
file_similarity: number;
}[];
};

View File

@ -1,6 +1,6 @@
import { AxiosInstance } from "axios";
import { Testimonial } from "@/lib/types/testimonial";
import { Testimonial } from "@/lib/types/Testimonial";
type CmsTestimonials = {
data: {

View File

@ -6,13 +6,7 @@ import { KnowledgeToFeedProvider } from "@/lib/context";
import { AddBrainSteps } from "./components/AddBrainSteps/AddBrainSteps";
import { CreateBrainProps } from "./types";
type AddBrainModalProps = {
triggerClassName?: string;
};
export const AddBrainModal = ({
triggerClassName,
}: AddBrainModalProps): JSX.Element => {
export const AddBrainModal = (): JSX.Element => {
const defaultValues: CreateBrainProps = {
...addBrainDefaultValues,
setDefault: true,
@ -26,7 +20,7 @@ export const AddBrainModal = ({
return (
<FormProvider {...methods}>
<KnowledgeToFeedProvider>
<AddBrainSteps triggerClassName={triggerClassName} />
<AddBrainSteps />
</KnowledgeToFeedProvider>
</FormProvider>
);

View File

@ -1,9 +1,7 @@
import { useTranslation } from "react-i18next";
import { LuPlusCircle } from "react-icons/lu";
import Button from "@/lib/components/ui/Button";
import { Modal } from "@/lib/components/ui/Modal";
import { cn } from "@/lib/utils";
import TextButton from "@/lib/components/ui/TextButton/TextButton";
import { BrainKnowledgeStep } from "./components/BrainKnowledgeStep/BrainKnowledgeStep";
import { BrainParamsStep } from "./components/BrainParamsStep/BrainParamsStep";
@ -11,13 +9,7 @@ import { BrainTypeSelectionStep } from "./components/BrainTypeSelectionStep/Brai
import { Stepper } from "./components/Stepper/Stepper";
import { useAddBrainConfig } from "./hooks/useAddBrainConfig";
type AddBrainConfigProps = {
triggerClassName?: string;
};
export const AddBrainSteps = ({
triggerClassName,
}: AddBrainConfigProps): JSX.Element => {
export const AddBrainSteps = (): JSX.Element => {
const { t } = useTranslation(["translation", "brain", "config"]);
const { isBrainCreationModalOpened, setIsBrainCreationModalOpened } =
@ -26,15 +18,13 @@ export const AddBrainSteps = ({
return (
<Modal
Trigger={
<Button
onClick={() => void 0}
variant={"tertiary"}
className={cn("border-0", triggerClassName)}
data-testid="add-brain-button"
>
<LuPlusCircle className="text-xl" />
{t("newBrain", { ns: "brain" })}
</Button>
<div onClick={() => void 0}>
<TextButton
iconName="add"
label={t("newBrain", { ns: "brain" })}
color="black"
></TextButton>
</div>
}
title={t("newBrainTitle", { ns: "brain" })}
desc={t("newBrainSubtitle", { ns: "brain" })}

View File

@ -4,12 +4,13 @@ import { FaArrowLeft } from "react-icons/fa";
import { ApiRequestDefinition } from "@/lib/components/ApiRequestDefinition";
import Button from "@/lib/components/ui/Button";
import { BrainType } from "@/lib/types/brainConfig";
import { BrainType } from "@/lib/types/BrainConfig";
import { CompositeBrainConnections } from "./components/CompositeBrainConnections/CompositeBrainConnections";
import { KnowledgeToFeedInput } from "./components/KnowledgeToFeedInput";
import { useBrainCreationHandler } from "./hooks/useBrainCreationHandler";
import { useBrainKnowledgeStep } from "./hooks/useBrainKnowledgeStep";
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
type BrainKnowledgeStepProps = {

View File

@ -11,6 +11,7 @@ import { PublicAccessConfirmationModal } from "./components/PublicAccessConfirma
import { useBrainParamsStep } from "./hooks/useBrainParamsStep";
import { useBrainStatusOptions } from "./hooks/useBrainStatusOptions";
import { usePublicAccessConfirmationModal } from "./hooks/usePublicAccessConfirmationModal";
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
import { useBrainTypeSelectionStep } from "../BrainTypeSelectionStep/hooks/useBrainTypeSelectionStep";

View File

@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next";
import { BrainStatus } from "@/lib/types/brainConfig";
import { BrainStatus } from "@/lib/types/BrainConfig";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useBrainStatusOptions = () => {
const { t } = useTranslation(["translation", "brain", "config"]);

View File

@ -7,6 +7,7 @@ import { Radio } from "@/lib/components/ui/Radio";
import { useBrainTypeSelectionStep } from "./hooks/useBrainTypeSelectionStep";
import { useKnowledgeSourceLabel } from "./hooks/useKnowledgeSourceLabel";
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
type BrainTypeSelectionStepProps = {

View File

@ -1,7 +1,7 @@
import { useFeatureIsOn } from "@growthbook/growthbook-react";
import { useTranslation } from "react-i18next";
import { BrainType } from "@/lib/types/brainConfig";
import { BrainType } from "@/lib/types/BrainConfig";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useKnowledgeSourceLabel = () => {

View File

@ -3,6 +3,7 @@ import { Fragment } from "react";
import { cn } from "@/lib/utils";
import { Step } from "./components/Step";
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
export const Stepper = (): JSX.Element => {

View File

@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { useBrainFetcher } from "@/app/brains-management/[brainId]/components/BrainManagementTabs/hooks/useBrainFetcher";
import { useApiBrainSecretsInputs } from "./hooks/useApiBrainSecretsInputs";
import Button from "../ui/Button";
type ApiBrainSecretsInputsProps = {

View File

@ -8,6 +8,7 @@ import Button from "@/lib/components/ui/Button";
import { ParamDefinitionRow } from "./components/ParamDefinitionRow";
import { defaultParamDefinitionRow } from "./config";
import { useParamsDefinition } from "./hooks/useParamsDefinition";
import { tabDescriptionStyle } from "../../styles";
type ParamsDefinitionProps = {

View File

@ -2,6 +2,7 @@ import { useEffect } from "react";
import { useForm, useFormContext, useWatch } from "react-hook-form";
import { useParamsDefinitionDefaultValues } from "./useParamsDefinitionDefaultValues";
import { ApiDefinitionContextType } from "../../../types";
import { ParameterDefinition } from "../types";
import { mapParameterDefinitionToApiBrainDefinitionSchema } from "../utils/mapParameterDefinitionToApiBrainDefinitionSchema";

View File

@ -11,6 +11,7 @@ import {
defaultSecretDefinitionRow,
} from "./config";
import { useSecretsDefinition } from "./hooks/useSecretsDefinition";
import { tabDescriptionStyle } from "../../styles";
const paramsNameStyle = "flex flex-1 justify-center";

View File

@ -2,6 +2,7 @@ import { useEffect } from "react";
import { useForm, useFormContext, useWatch } from "react-hook-form";
import { useSecretsDefinitionDefaultValues } from "./useSecretsDefinitionDefaultValues";
import { ApiDefinitionContextType } from "../../../types";
import {
brainSecretsSchemaDefinitionKeyInForm,

View File

@ -7,7 +7,9 @@ import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainConte
import { RemoveAccessIcon } from "./components/RemoveAccessIcon";
import { useBrainUser } from "./hooks/useBrainUser";
import { BrainRoleType } from "../../types";
type BrainUserProps = {
email: string;
role: BrainRoleType;

View File

@ -9,6 +9,7 @@ import {
isWithinLast7Days,
isYesterday,
} from "./utils";
import Spinner from "../ui/Spinner";
export const ChatHistory = (): JSX.Element => {

View File

@ -2,6 +2,7 @@ import { IoMdCloseCircle } from "react-icons/io";
import { MdLink } from "react-icons/md";
import { FeedTitleDisplayer } from "./FeedTitleDisplayer";
import { StyledFeedItemDiv } from "../styles/StyledFeedItemDiv";
type CrawlFeedItemProps = {

View File

@ -3,6 +3,7 @@ import { IoMdCloseCircle } from "react-icons/io";
import { getFileIcon } from "@/lib/helpers/getFileIcon";
import { FeedTitleDisplayer } from "./FeedTitleDisplayer";
import { StyledFeedItemDiv } from "../styles/StyledFeedItemDiv";
type FileFeedItemProps = {

View File

@ -1,4 +1,5 @@
import { StripePricingTable } from "./components/PricingTable/PricingTable";
import { Modal } from "../../ui/ModalPayment";
type StripePricingModalProps = {

View File

@ -0,0 +1,46 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/IconSizes.module.scss";
.small {
width: IconSizes.$small;
height: IconSizes.$small;
}
.normal {
width: IconSizes.$normal;
height: IconSizes.$normal;
}
.large {
width: IconSizes.$large;
height: IconSizes.$large;
}
.big {
width: IconSizes.$big;
height: IconSizes.$big;
}
.black {
color: Colors.$black;
&.hovered {
color: Colors.$accent;
}
}
.primary {
color: Colors.$primary;
}
.accent {
color: Colors.$accent;
}
.white {
color: Colors.$white;
}
.disabled {
opacity: 0.2;
}

View File

@ -0,0 +1,41 @@
import { IconType } from "react-icons/lib";
import { iconList } from "@/lib/helpers/iconList";
import { Color } from "@/lib/types/Colors";
import { IconSize } from "@/lib/types/Icons";
import styles from "./Icon.module.scss";
interface IconProps {
name: keyof typeof iconList;
size: IconSize;
color: Color;
disabled?: boolean;
classname?: string;
hovered?: boolean;
}
export const Icon = ({
name,
size,
color,
disabled,
classname,
hovered,
}: IconProps): JSX.Element => {
const IconComponent: IconType = iconList[name];
return (
<IconComponent
className={`
${classname ?? ""}
${styles[size] ?? ""}
${styles[color] ?? ""}
${disabled ? styles.disabled ?? "" : ""}
${hovered ? styles.hovered ?? "" : ""}
`}
/>
);
};
export default Icon;

View File

@ -3,9 +3,6 @@
.loader_icon {
animation: spin 1s linear infinite;
width: IconSizes.$big;
height: IconSizes.$big;
color: Colors.$accent;
@keyframes spin {
from {
@ -15,24 +12,4 @@
transform: rotate(360deg);
}
}
&.small {
width: IconSizes.$small;
height: IconSizes.$small;
}
&.normal {
width: IconSizes.$normal;
height: IconSizes.$normal;
}
&.large {
width: IconSizes.$large;
height: IconSizes.$large;
}
&.big {
width: IconSizes.$big;
height: IconSizes.$big;
}
}

View File

@ -1,17 +1,22 @@
import { AiOutlineLoading3Quarters } from "react-icons/ai";
import { Color } from "@/lib/types/Colors";
import { IconSize } from "@/lib/types/Icons";
import styles from "./LoaderIcon.module.scss";
import { Icon } from "../Icon/Icon";
interface LoaderIconProps {
size: IconSize;
color: Color;
}
export const LoaderIcon = (props: LoaderIconProps): JSX.Element => {
return (
<AiOutlineLoading3Quarters
className={`${styles.loader_icon ?? ""} ${styles[props.size] ?? ""}`}
<Icon
name="loader"
size={props.size}
color={props.color}
classname={styles.loader_icon ?? ""}
/>
);
};

View File

@ -7,12 +7,13 @@ import { useChat } from "@/app/chat/[chatId]/hooks/useChat";
import { useChatContext } from "@/lib/context";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { LoaderIcon } from "../LoaderIcon/LoaderIcon";
// eslint-disable-next-line import/order
import styles from "./SearchBar.module.scss";
import { LoaderIcon } from "../LoaderIcon/LoaderIcon";
export const SearchBar = (): JSX.Element => {
const [searching, setSearching] = useState(false);
const [isDisabled, setIsDisabled] = useState(true);
const { message, setMessage } = useChatInput();
const { setMessages } = useChatContext();
const { addQuestion } = useChat();
@ -20,7 +21,8 @@ export const SearchBar = (): JSX.Element => {
useEffect(() => {
setCurrentBrainId(null);
}, [setCurrentBrainId]);
setIsDisabled(message === "");
}, [setCurrentBrainId, message]);
const submit = async (): Promise<void> => {
setSearching(true);
@ -35,7 +37,6 @@ export const SearchBar = (): JSX.Element => {
};
/* eslint-disable @typescript-eslint/restrict-template-expressions */
return (
<div className={styles.search_bar_wrapper}>
<Editor
@ -45,10 +46,13 @@ export const SearchBar = (): JSX.Element => {
placeholder="Search"
></Editor>
{searching ? (
<LoaderIcon size="big" />
<LoaderIcon size="big" color="accent" />
) : (
<LuSearch
className={`${styles.search_icon} ${!message ? styles.disabled : ""}`}
className={`
${styles.search_icon}
${isDisabled ? styles.disabled : ""}
`}
onClick={() => void submit()}
/>
)}

View File

@ -0,0 +1,18 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
.text_button_wrapper {
display: flex;
align-items: center;
gap: Spacings.$spacing03;
cursor: pointer;
background-color: transparent;
}
.black {
color: Colors.$black;
&.hovered {
color: Colors.$accent;
}
}

View File

@ -0,0 +1,43 @@
import { useState } from "react";
import { iconList } from "@/lib/helpers/iconList";
import { Color } from "@/lib/types/Colors";
import styles from "./TextButton.module.scss";
import { Icon } from "../Icon/Icon";
interface TextButtonProps {
iconName: keyof typeof iconList;
label: string;
color: Color;
}
export const TextButton = (props: TextButtonProps): JSX.Element => {
const [hovered, setHovered] = useState(false);
return (
<div
className={styles.text_button_wrapper}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<Icon
name={props.iconName}
size="normal"
color={props.color}
hovered={hovered}
/>
<span
className={`
${styles[props.color] ?? ""}
${hovered ? styles.hovered ?? "" : ""}
`}
>
{props.label}
</span>
</div>
);
};
export default TextButton;

View File

@ -1,5 +1,5 @@
import { CreateBrainInput } from "../api/brain/types";
import { Model } from "../types/brainConfig";
import { Model } from "../types/BrainConfig";
export const addBrainDefaultValues: CreateBrainInput = {
model: "gpt-3.5-turbo",

View File

@ -5,7 +5,8 @@ import { BrainRoleType } from "@/lib/components/BrainUsers/types";
import { Document } from "@/lib/types/Document";
import { useBrainProvider } from "./hooks/useBrainProvider";
import { BrainType, Model } from "../../types/brainConfig";
import { BrainType, Model } from "../../types/BrainConfig";
export type BrainAccessStatus = "private" | "public";

View File

@ -1,6 +1,6 @@
import { ChatMessage, Notification } from "@/app/chat/[chatId]/types";
import { Model } from "../../types/brainConfig";
import { Model } from "../../types/BrainConfig";
export type ChatConfig = {
model: Model;

View File

@ -1,4 +1,4 @@
import { Model, PaidModels } from "../types/brainConfig";
import { Model, PaidModels } from "../types/BrainConfig";
export const defineMaxTokens = (
model: Model | PaidModels | undefined

View File

@ -1,5 +1,5 @@
import { freeModels, paidModels } from "@/lib/types/BrainConfig";
import { UserStats } from "@/lib/types/User";
import { freeModels, paidModels } from "@/lib/types/brainConfig";
type GetAccessibleModelsInput = {
openAiKey?: string | null;

View File

@ -3,7 +3,7 @@ import { LuBot, LuBrain } from "react-icons/lu";
import { PiPaperclipFill } from "react-icons/pi";
import { TbWorld } from "react-icons/tb";
import { BrainType } from "@/lib/types/brainConfig";
import { BrainType } from "@/lib/types/BrainConfig";
type GetBrainIconFromBrainTypeOptions = {
iconSize?: number;
ApiBrainIcon?: IconType;

View File

@ -17,6 +17,7 @@ import { LiaFileVideo } from "react-icons/lia";
import { IconType } from "react-icons/lib";
import { getFileType } from "./getFileType";
import { SupportedFileExtensions } from "../types/SupportedFileExtensions";
const fileTypeIcons: Record<SupportedFileExtensions, IconType> = {

View File

@ -0,0 +1,9 @@
import { AiOutlineLoading3Quarters } from "react-icons/ai";
import { IconType } from "react-icons/lib";
import { LuPlusCircle, LuSearch } from "react-icons/lu";
export const iconList: { [name: string]: IconType } = {
add: LuPlusCircle,
loader: AiOutlineLoading3Quarters,
search: LuSearch,
};

View File

@ -7,6 +7,7 @@ import { useEventTracking } from "@/services/analytics/june/useEventTracking";
import { useOnboarding } from "./useOnboarding";
import { useOnboardingTracker } from "./useOnboardingTracker";
import { useToast } from "./useToast";
import { useKnowledgeToFeedContext } from "../context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
import { acceptedFormats } from "../helpers/acceptedFormats";
import { cloneFileWithSanitizedName } from "../helpers/cloneFileWithSanitizedName";

View File

@ -0,0 +1 @@
export type Color = "black" | "primary" | "accent" | "white";

View File

@ -0,0 +1 @@
$searchBarHeight: 62px;