Skip to content

Commit

Permalink
feat: add new actions modal (#1870)
Browse files Browse the repository at this point in the history
Issue: #1861
- Update Quivr font
- Add Actions modal
- Update Popover component
- Move Chat config to Actions modal

Demo:


https://github.com/StanGirard/quivr/assets/63923024/df3ac138-6950-46fe-8e40-6276005c7ef1
  • Loading branch information
mamadoudicko authored Dec 13, 2023
1 parent f28e009 commit a30042f
Show file tree
Hide file tree
Showing 19 changed files with 170 additions and 177 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PopoverAnchor } from "@radix-ui/react-popover";
import { useState } from "react";
import { LuPlusCircle, LuXCircle } from "react-icons/lu";

import Button from "@/lib/components/ui/Button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/lib/components/ui/Popover";

import { ConfigModal } from "./components/ConfigModal";

export const ActionsModal = (): JSX.Element => {
const [isActionsModalOpened, setIsActionsModalOpened] = useState(false);

const Icon = isActionsModalOpened ? LuXCircle : LuPlusCircle;

return (
<div className="flex items-center">
<Popover
open={isActionsModalOpened}
onOpenChange={(isOpened) => setIsActionsModalOpened(isOpened)}
>
<PopoverTrigger>
<PopoverAnchor asChild>
<Button variant="tertiary" type="button" className="p-0">
<Icon className="text-accent font-bold" size={30} />
</Button>
</PopoverAnchor>
</PopoverTrigger>
<PopoverContent
align="end"
sideOffset={15}
className="min-h-[200px] w-[200px]"
>
<ConfigModal />
</PopoverContent>
</Popover>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { forwardRef } from "react";

import CoreButton, {
ButtonProps as CoreButtonProps,
} from "@/lib/components/ui/Button";
import { cn } from "@/lib/utils";

type ButtonProps = CoreButtonProps & {
onClick?: () => void;
className?: string;
label?: string;
startIcon?: JSX.Element;
endIcon?: JSX.Element;
};

export const Button = forwardRef(
(
{ onClick, className, label, startIcon, endIcon, ...props }: ButtonProps,
forwardedRef
): JSX.Element => {
return (
<CoreButton
className={cn("p-2 sm:px-3 text-primary focus:ring-0 ", className)}
variant={"tertiary"}
data-testid="config-button"
ref={forwardedRef}
onClick={onClick}
{...props}
>
<div className="flex flex-row justify-between w-full items-center">
<div className="flex flex-row gap-2 items-center">
{startIcon}
<span className="hidden sm:block">{label}</span>
</div>
{endIcon}
</div>
</CoreButton>
);
}
);

Button.displayName = CoreButton.displayName;
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable max-lines */
import { useTranslation } from "react-i18next";
import { MdCheck, MdSettings } from "react-icons/md";
import { LuChevronRight, LuSettings } from "react-icons/lu";
import { MdCheck } from "react-icons/md";

import Button from "@/lib/components/ui/Button";
import { Modal } from "@/lib/components/ui/Modal";
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";

import { useConfigModal } from "./hooks/useConfigModal";
import { Button } from "../Button";

export const ConfigModal = (): JSX.Element => {
const {
Expand All @@ -24,12 +24,11 @@ export const ConfigModal = (): JSX.Element => {
<Modal
Trigger={
<Button
className="p-2 sm:px-3"
variant={"tertiary"}
data-testid="config-button"
>
<MdSettings className="text-lg sm:text-xl lg:text-2xl" />
</Button>
label={"Parametres"}
startIcon={<LuSettings size={18} />}
endIcon={<LuChevronRight size={18} />}
className="w-full"
/>
}
title="Chat configuration"
desc="Adjust your chat settings"
Expand Down Expand Up @@ -68,16 +67,16 @@ export const ConfigModal = (): JSX.Element => {
</fieldset>

<Button
className="mt-12 self-end"
className="mt-12 self-end text-white"
type="button"
onClick={() => {
handleSubmit();
setIsConfigModalOpen(false);
}}
>
Save
<MdCheck className="text-xl" />
</Button>
variant={"primary"}
label="Save"
endIcon={<MdCheck className="text-xl" />}
/>
</form>
</Modal>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./ConfigModal";
export * from "./OnboardingQuestions";
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { useKnowledgeToFeedContext } from "@/lib/context/KnowledgeToFeedProvider
import { getBrainIconFromBrainType } from "@/lib/helpers/getBrainIconFromBrainType";

import { OnboardingQuestions } from "./components";
import { ActionsModal } from "./components/ActionsModal/ActionsModal";
import { ChatEditor } from "./components/ChatEditor/ChatEditor";
import { ConfigModal } from "./components/ConfigModal";
import { useChatInput } from "./hooks/useChatInput";

type ChatInputProps = {
Expand Down Expand Up @@ -58,9 +58,9 @@ export const ChatInput = ({
/>
</div>

<div className="flex flex-row items-end">
<div className="flex flex-row items-center gap-4">
<Button
className="px-3 py-2 sm:px-4 sm:py-2"
className="px-3 py-2 sm:px-4 sm:py-2 bg-primary border-0"
type="submit"
isLoading={generatingAnswer}
data-testid="submit-button"
Expand All @@ -69,9 +69,7 @@ export const ChatInput = ({
? t("thinking", { ns: "chat" })
: t("chat", { ns: "chat" })}
</Button>
<div className="hidden md:flex items-center">
<ConfigModal />
</div>
<ActionsModal />
</div>
</form>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { Analytics as VercelAnalytics } from "@vercel/analytics/react";
import { Inter } from "next/font/google";
import { Outfit } from "next/font/google";
import { cookies, headers } from "next/headers";

import { ToastProvider } from "@/lib/components/ui/Toast";
Expand All @@ -10,7 +10,7 @@ import { SupabaseProvider } from "@/lib/context/SupabaseProvider";
import { App } from "./App";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });
const inter = Outfit({ subsets: ["latin"], weight: "400" });

export const metadata = {
title: "Quivr - Get a Second Brain with Generative AI",
Expand Down
59 changes: 0 additions & 59 deletions frontend/app/user/components/LanguageDropDown/LanguageDropDown.tsx

This file was deleted.

1 change: 0 additions & 1 deletion frontend/app/user/components/LanguageDropDown/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/app/user/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { redirectToLogin } from "@/lib/router/redirectToLogin";

import { StripePricingOrManageButton, UserStatistics } from "./components";
import { ApiKeyConfig } from "./components/ApiKeyConfig";
import LanguageSelect from "./components/LanguageDropDown/LanguageSelect";
import LanguageSelect from "./components/LanguageSelect/LanguageSelect";
import { LogoutModal } from "./components/LogoutCard/LogoutModal";
import ThemeSelect from "./components/ThemeSelect/ThemeSelect";

Expand Down
2 changes: 2 additions & 0 deletions frontend/lib/components/UserToInvite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export const UserToInvite = ({
onChange={setSelectedRole}
value={selectedRole}
options={translatedOptions}
popoverSide="bottom"
popoverClassName="w-36"
/>
</div>
);
Expand Down
1 change: 0 additions & 1 deletion frontend/lib/components/ui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ const Button = forwardRef(
{children} {isLoading && <FaSpinner className="animate-spin" />}
</>
);
const buttonElement = <button {...buttonProps}>buttonChildren</button>;

if (tooltip !== undefined) {
return (
Expand Down
88 changes: 23 additions & 65 deletions frontend/lib/components/ui/Popover.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,29 @@
"use client";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { AnimatePresence, motion } from "framer-motion";
import { ReactNode, useState } from "react";
import * as React from "react";

import Button from "./Button";
import { cn } from "@/lib/utils";

interface PopoverProps {
children?: ReactNode;
Trigger: ReactNode;
ActionTrigger?: ReactNode;
CloseTrigger?: ReactNode;
}
const Popover = PopoverPrimitive.Root;

const Popover = ({
children,
Trigger,
ActionTrigger,
CloseTrigger,
}: PopoverProps): JSX.Element => {
const [open, setOpen] = useState(false);
const PopoverTrigger = PopoverPrimitive.Trigger;

return (
<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
<PopoverPrimitive.Trigger asChild>{Trigger}</PopoverPrimitive.Trigger>
<AnimatePresence>
{open && (
<PopoverPrimitive.Portal forceMount>
<PopoverPrimitive.Content forceMount asChild sideOffset={5}>
<motion.div
initial={{ opacity: 0, y: -32 }}
animate={{
opacity: 1,
y: 0,
}}
exit={{ opacity: 0, y: -32 }}
transition={{ duration: 0.2, ease: "easeInOut" }}
className="relative flex flex-col p-4 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg shadow-lg z-50 md:z-40"
>
<div className="flex-1">{children}</div>
<div className="mt-4 self-end flex gap-4">
{ActionTrigger !== undefined && (
<PopoverPrimitive.Close asChild>
{ActionTrigger}
</PopoverPrimitive.Close>
)}
<PopoverPrimitive.Close asChild>
{CloseTrigger === undefined ? (
<Button
variant={"secondary"}
className="px-3 py-2"
aria-label="Close"
>
Close
</Button>
) : (
CloseTrigger
)}
</PopoverPrimitive.Close>
</div>
<PopoverPrimitive.Arrow className="fill-white stroke-gray-300 stroke-2" />
</motion.div>
</PopoverPrimitive.Content>
</PopoverPrimitive.Portal>
)}
</AnimatePresence>
</PopoverPrimitive.Root>
);
};
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-white p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;

export default Popover;
export { Popover, PopoverContent, PopoverTrigger };
Loading

0 comments on commit a30042f

Please sign in to comment.