Devx/add linter rules (#331)

* remove duplicate import

* 🚧 add new linter configuration

* 🧑‍💻  add and run prettier

* 🐛 add babel parser for linter

* 🧑‍💻 add lint-fix command

* 🚨 use lint-fix

* 🚨 remove 'FC' as a type. Use const and JSX.Element

* 🚨 enforce arrow function rule from linter

* 🔥 delete unused file

* 🚨 adding /* eslint-disable */ in failing files

* 💩 add ts-expect-error to Victory components
This commit is contained in:
Zineb El Bachiri 2023-06-15 11:52:46 +02:00 committed by GitHub
parent e6e5099d6b
commit 1d7bc8a5bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 1791 additions and 1335 deletions

188
frontend/.eslintrc.js Normal file
View File

@ -0,0 +1,188 @@
/* eslint-disable max-lines */
module.exports = {
plugins: ["prefer-arrow", "import"],
extends: [
"next",
"next/core-web-vitals",
"eslint:recommended",
"plugin:import/recommended",
],
ignorePatterns: ["**/node_modules/", "**/.next/"],
rules: {
"import/extensions": 0,
"import/no-unresolved": 0,
"import/prefer-default-export": 0,
"import/no-duplicates": "error",
complexity: ["error", 8],
"max-lines": ["error", 100],
"max-depth": ["error", 3],
"max-params": ["error", 4],
eqeqeq: ["error", "smart"],
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: true,
optionalDependencies: false,
peerDependencies: false,
},
],
"no-shadow": [
"error",
{
hoist: "all",
},
],
"prefer-const": "error",
"import/order": [
"error",
{
pathGroups: [{ pattern: "@lib/**", group: "unknown" }],
groups: [
["external", "builtin"],
"unknown",
"internal",
["parent", "sibling", "index"],
],
alphabetize: {
order: "asc",
caseInsensitive: false,
},
"newlines-between": "always",
pathGroupsExcludedImportTypes: ["builtin"],
},
],
"import/namespace": "off",
"sort-imports": [
"error",
{
ignoreCase: true,
ignoreDeclarationSort: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
},
],
"padding-line-between-statements": [
"error",
{
blankLine: "always",
prev: "*",
next: "return",
},
],
"prefer-arrow/prefer-arrow-functions": [
"error",
{
disallowPrototype: true,
singleReturnOnly: false,
classPropertiesAllowed: false,
},
],
"no-restricted-imports": [
"error",
{
paths: [
{
name: "lodash",
message: "Please use lodash/{module} import instead",
},
{
name: "aws-sdk",
message: "Please use aws-sdk/{module} import instead",
},
{
name: ".",
message: "Please use explicit import file",
},
],
},
],
curly: ["error", "all"],
},
root: true,
env: {
es6: true,
node: true,
browser: true,
},
parserOptions: {
ecmaVersion: 9,
sourceType: "module",
babelOptions: {
presets: [require.resolve("next/babel")],
},
},
overrides: [
{
files: ["**/*.ts?(x)"],
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:import/typescript",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.eslint.json",
tsconfigRootDir: __dirname,
sourceType: "module",
},
rules: {
"@typescript-eslint/prefer-optional-chain": "error",
"no-shadow": "off",
"@typescript-eslint/no-shadow": "error",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/strict-boolean-expressions": [
"error",
{
allowString: false,
allowNumber: false,
allowNullableObject: true,
},
],
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-ignore": "allow-with-description",
minimumDescriptionLength: 10,
},
],
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-member-accessibility": 0,
"@typescript-eslint/camelcase": 0,
"@typescript-eslint/interface-name-prefix": 0,
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/ban-types": [
"error",
{
types: {
FC: "Use `const MyComponent = (props: Props): JSX.Element` instead",
SFC: "Use `const MyComponent = (props: Props): JSX.Element` instead",
FunctionComponent:
"Use `const MyComponent = (props: Props): JSX.Element` instead",
"React.FC":
"Use `const MyComponent = (props: Props): JSX.Element` instead",
"React.SFC":
"Use `const MyComponent = (props: Props): JSX.Element` instead",
"React.FunctionComponent":
"Use `const MyComponent = (props: Props): JSX.Element` instead",
},
extendDefaults: true,
},
],
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
"@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-unnecessary-type-arguments": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/switch-exhaustiveness-check": "error",
"@typescript-eslint/restrict-template-expressions": [
"error",
{
allowNumber: true,
allowBoolean: true,
},
],
},
},
],
};

View File

@ -1,14 +0,0 @@
{
"extends": [
"next/core-web-vitals",
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"plugins": ["@typescript-eslint"],
"parser": "@typescript-eslint/parser",
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error"
}
}

View File

@ -0,0 +1,4 @@
module.exports = {
"*": "prettier --ignore-unknown --write",
"*.{js,ts, tsx}": "pnpm lint-fix",
};

View File

@ -1,6 +1,8 @@
/* eslint-disable */
import { useState } from "react";
import { useSupabase } from "@/app/supabase-provider";
import { useToast } from "@/lib/hooks/useToast";
import { useState } from "react";
export const useGoogleLogin = () => {
const { supabase } = useSupabase();

View File

@ -1,3 +1,4 @@
/* eslint-disable */
import Button from "@/lib/components/ui/Button";
import { useGoogleLogin } from "./hooks/useGoogleLogin";

View File

@ -1,8 +1,10 @@
/* eslint-disable */
"use client";
import { useState } from "react";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import { useToast } from "@/lib/hooks/useToast";
import { useState } from "react";
type MaginLinkLoginProps = {
email: string;
@ -21,6 +23,7 @@ export const MagicLinkLogin = ({ email, setEmail }: MaginLinkLoginProps) => {
variant: "danger",
text: "Please enter your email address",
});
return;
}

View File

@ -1,4 +1,9 @@
/* eslint-disable */
"use client";
import Link from "next/link";
import { redirect } from "next/navigation";
import { useState } from "react";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import Card from "@/lib/components/ui/Card";
@ -6,9 +11,7 @@ import { Divider } from "@/lib/components/ui/Divider";
import Field from "@/lib/components/ui/Field";
import PageHeading from "@/lib/components/ui/PageHeading";
import { useToast } from "@/lib/hooks/useToast";
import Link from "next/link";
import { redirect } from "next/navigation";
import { useState } from "react";
import { GoogleLoginButton } from "./components/GoogleLogin";
import { MagicLinkLogin } from "./components/MagicLinkLogin";

View File

@ -1,12 +1,14 @@
/* eslint-disable */
"use client";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import Card from "@/lib/components/ui/Card";
import PageHeading from "@/lib/components/ui/PageHeading";
import { useToast } from "@/lib/hooks/useToast";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
export default function Logout() {
const { supabase } = useSupabase();

View File

@ -1,12 +1,14 @@
/* eslint-disable */
"use client";
import Link from "next/link";
import { useState } from "react";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import Card from "@/lib/components/ui/Card";
import Field from "@/lib/components/ui/Field";
import PageHeading from "@/lib/components/ui/PageHeading";
import { useToast } from "@/lib/hooks/useToast";
import Link from "next/link";
import { useState } from "react";
export default function SignUp() {
const { supabase } = useSupabase();

View File

@ -1,5 +1,4 @@
import Card from "@/lib/components/ui/Card";
import { FC, ReactNode } from "react";
import { ReactNode } from "react";
import {
GiArtificialIntelligence,
GiBrain,
@ -9,7 +8,9 @@ import {
GiOpenBook,
} from "react-icons/gi";
const Features: FC = () => {
import Card from "@/lib/components/ui/Card";
const Features = (): JSX.Element => {
return (
<section className="my-20 text-center flex flex-col items-center justify-center gap-10">
<div>
@ -58,7 +59,7 @@ interface FeatureProps {
desc: string;
}
const Feature: FC<FeatureProps> = ({ title, desc, icon }) => {
const Feature = ({ title, desc, icon }: FeatureProps): JSX.Element => {
return (
<Card className="p-10 max-w-xs flex flex-col gap-5 w-full">
{icon}

View File

@ -1,11 +1,12 @@
"use client";
import Button from "@/lib/components/ui/Button";
import { motion, useScroll, useSpring, useTransform } from "framer-motion";
import Link from "next/link";
import { FC, useRef } from "react";
import { useRef } from "react";
import { MdNorthEast } from "react-icons/md";
const Hero: FC = () => {
import Button from "@/lib/components/ui/Button";
const Hero = (): JSX.Element => {
const targetRef = useRef<HTMLDivElement | null>(null);
const { scrollYProgress } = useScroll({
target: targetRef,
@ -19,6 +20,7 @@ const Hero: FC = () => {
if (pos === 1) {
return "relative";
}
return "sticky";
});

View File

@ -1,8 +1,9 @@
import Features from "./Features";
import Hero from "./Hero";
import { redirect } from "next/navigation";
export default function HomePage() {
import Features from "./Features";
import Hero from "./Hero";
const HomePage = (): JSX.Element => {
if (process.env.NEXT_PUBLIC_ENV === "local") {
redirect("/upload");
}
@ -13,4 +14,6 @@ export default function HomePage() {
<Features />
</main>
);
}
};
export default HomePage;

View File

@ -1,8 +1,11 @@
/* eslint-disable */
"use client";
import PageHeading from "@/lib/components/ui/PageHeading";
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
import { UUID } from "crypto";
import { useEffect } from "react";
import PageHeading from "@/lib/components/ui/PageHeading";
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
import { ChatInput, ChatMessages } from "../components";
interface ChatPageProps {
@ -18,7 +21,9 @@ export default function ChatPage({ params }: ChatPageProps) {
useEffect(() => {
// if (chatId)
if (!chatId) resetChat();
if (!chatId) {
resetChat();
}
fetchChat(chatId);
}, [fetchChat, chatId]);

View File

@ -1,9 +1,10 @@
"use client";
import Button from "@/lib/components/ui/Button";
import Link from "next/link";
import { MdSettings } from "react-icons/md";
export function ConfigButton() {
import Button from "@/lib/components/ui/Button";
export const ConfigButton = (): JSX.Element => {
return (
<Link href={"/config"}>
<Button className="p-2 sm:px-3" variant={"tertiary"}>
@ -11,4 +12,4 @@ export function ConfigButton() {
</Button>
</Link>
);
}
};

View File

@ -1,9 +1,11 @@
/* eslint-disable */
"use client";
import Button from "@/lib/components/ui/Button";
import { useSpeech } from "@/lib/context/ChatsProvider/hooks/useSpeech";
import { MdMic, MdMicOff } from "react-icons/md";
export function MicButton() {
import Button from "@/lib/components/ui/Button";
import { useSpeech } from "@/lib/context/ChatsProvider/hooks/useSpeech";
export const MicButton = (): JSX.Element => {
const { isListening, speechSupported, startListening } = useSpeech();
return (
@ -21,4 +23,4 @@ export function MicButton() {
)}
</Button>
);
}
};

View File

@ -1,17 +1,22 @@
/* eslint-disable */
"use client";
import Button from "@/lib/components/ui/Button";
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
import { ConfigButton } from "./ConfigButton";
import { MicButton } from "./MicButton";
export function ChatInput() {
export const ChatInput = (): JSX.Element => {
const { isSendingMessage, sendMessage, setMessage, message, chat } =
useChatsContext();
return (
<form
onSubmit={(e) => {
e.preventDefault();
if (!isSendingMessage) sendMessage(chat?.chatId);
if (!isSendingMessage) {
sendMessage(chat?.chatId);
}
}}
className="sticky bottom-0 p-5 bg-white dark:bg-black rounded-t-md border border-black/10 dark:border-white/25 border-b-0 w-full max-w-3xl flex items-center justify-center gap-2 z-20"
>
@ -22,7 +27,9 @@ export function ChatInput() {
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault(); // Prevents the newline from being entered in the textarea
if (!isSendingMessage) sendMessage(chat?.chatId); // Call the submit function here
if (!isSendingMessage) {
sendMessage(chat?.chatId);
} // Call the submit function here
}
}}
className="w-full p-2 border border-gray-300 dark:border-gray-500 outline-none rounded dark:bg-gray-800"
@ -41,4 +48,4 @@ export function ChatInput() {
</div>
</form>
);
}
};

View File

@ -1,9 +1,11 @@
/* eslint-disable */
"use client";
import { cn } from "@/lib/utils";
import { forwardRef, Ref } from "react";
import ReactMarkdown from "react-markdown";
const ChatMessage = forwardRef(
import { cn } from "@/lib/utils";
export const ChatMessage = forwardRef(
(
{
speaker,
@ -46,5 +48,3 @@ const ChatMessage = forwardRef(
);
ChatMessage.displayName = "ChatMessage";
export default ChatMessage;

View File

@ -1,26 +1,32 @@
/* eslint-disable */
"use client";
import { useEffect, useRef } from "react";
import Card from "@/lib/components/ui/Card";
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
import { FC, useEffect, useRef } from "react";
import ChatMessage from "./ChatMessage";
import { ChatMessage } from "./ChatMessage";
export const ChatMessages: FC = () => {
export const ChatMessages = (): JSX.Element => {
const lastChatRef = useRef<HTMLDivElement | null>(null);
const { chat } = useChatsContext();
useEffect(() => {
if (!chat || !lastChatRef.current) return;
if (!chat || !lastChatRef.current) {
return;
}
// if (chat.history.length > 2) {
lastChatRef.current?.scrollIntoView({
lastChatRef.current.scrollIntoView({
behavior: "smooth",
block: "end",
});
// }
}, [chat, lastChatRef]);
if (!chat) return null;
if (!chat) {
return <></>;
}
return (
<Card className="p-5 max-w-3xl w-full flex flex-col h-full mb-8">

View File

@ -1,10 +1,12 @@
import { cn } from "@/lib/utils";
/* eslint-disable */
import { UUID } from "crypto";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { FC } from "react";
import { FiTrash2 } from "react-icons/fi";
import { MdChatBubbleOutline } from "react-icons/md";
import { cn } from "@/lib/utils";
import { Chat } from "../../../../lib/types/Chat";
interface ChatsListItemProps {
@ -12,7 +14,10 @@ interface ChatsListItemProps {
deleteChat: (id: UUID) => void;
}
const ChatsListItem: FC<ChatsListItemProps> = ({ chat, deleteChat }) => {
const ChatsListItem = ({
chat,
deleteChat,
}: ChatsListItemProps): JSX.Element => {
const pathname = usePathname()?.split("/").at(-1);
const selected = chat.chatId === pathname;

View File

@ -1,7 +1,7 @@
import Link from "next/link";
import { BsPlusSquare } from "react-icons/bs";
export const NewChatButton = () => (
export const NewChatButton = (): JSX.Element => (
<Link
href="/chat"
className="px-4 py-2 mx-4 my-2 border border-primary bg-white dark:bg-black hover:text-white hover:bg-primary shadow-lg rounded-lg flex items-center justify-center sticky top-2 z-20"

View File

@ -1,12 +1,15 @@
/* eslint-disable */
"use client";
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
import { cn } from "@/lib/utils";
import { MotionConfig, motion } from "framer-motion";
import { useState } from "react";
import { MdChevronRight } from "react-icons/md";
import useChatsContext from "@/lib/context/ChatsProvider/hooks/useChatsContext";
import ChatsListItem from "./ChatsListItem";
import { NewChatButton } from "./NewChatButton";
export function ChatsList() {
export const ChatsList = (): JSX.Element => {
const { allChats, deleteChat } = useChatsContext();
const [open, setOpen] = useState(false);
@ -69,4 +72,4 @@ export function ChatsList() {
</motion.div>
</MotionConfig>
);
}
};

View File

@ -1,4 +1,4 @@
export * from "./ChatMessages";
export * from "./ChatMessages/ChatInput";
export * from "./ChatMessages/ChatMessage";
export * from "./ChatMessages";
export * from "./ChatsList";

View File

@ -1,17 +1,21 @@
"use client";
import { ChatsProvider } from "@/lib/context/ChatsProvider/chats-provider";
import { redirect } from "next/navigation";
import { FC, ReactNode } from "react";
import { useSupabase } from "../supabase-provider";
import { ReactNode } from "react";
import { ChatsProvider } from "@/lib/context/ChatsProvider/chats-provider";
import { ChatsList } from "./components";
import { useSupabase } from "../supabase-provider";
interface LayoutProps {
children?: ReactNode;
}
const Layout: FC<LayoutProps> = ({ children }) => {
const Layout = ({ children }: LayoutProps): JSX.Element => {
const { session } = useSupabase();
if (!session) redirect("/login");
if (!session) {
redirect("/login");
}
return (
<ChatsProvider>

View File

@ -1,8 +1,10 @@
/* eslint-disable */
"use client";
import { useState } from "react";
import Button from "@/lib/components/ui/Button";
import { useAxios } from "@/lib/useAxios";
import { useState } from "react";
export const ApiKeyConfig = (): JSX.Element => {
const [apiKey, setApiKey] = useState("");
@ -40,13 +42,13 @@ export const ApiKeyConfig = (): JSX.Element => {
</div>
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
{!apiKey && (
{apiKey === "" && (
<Button variant="secondary" onClick={handleCreateClick}>
Create New Key
</Button>
)}
</div>
{apiKey && (
{apiKey !== "" && (
<div className="flex items-center space-x-4">
<span className="text-gray-600">{apiKey}</span>
<Button variant="secondary" onClick={handleCopyClick}>

View File

@ -1,8 +1,10 @@
/* eslint-disable */
"use client";
import { UseFormRegister } from "react-hook-form";
import Field from "@/lib/components/ui/Field";
import { BrainConfig } from "@/lib/context/BrainConfigProvider/types";
import { UseFormRegister } from "react-hook-form";
interface BackendConfigProps {
register: UseFormRegister<BrainConfig>;

View File

@ -1,7 +1,10 @@
/* eslint-disable */
"use client";
import Button from "@/lib/components/ui/Button";
import { useRouter } from "next/navigation";
import Button from "@/lib/components/ui/Button";
import { useConfig } from "../hooks/useConfig";
import { BackendConfig } from "./BackendConfig";
import { ModelConfig } from "./ModelConfig";

View File

@ -1,5 +1,8 @@
/* eslint-disable */
"use client";
import { UseFormRegister } from "react-hook-form";
import Field from "@/lib/components/ui/Field";
import {
BrainConfig,
@ -9,7 +12,6 @@ import {
models,
paidModels,
} from "@/lib/context/BrainConfigProvider/types";
import { UseFormRegister } from "react-hook-form";
interface ModelConfigProps {
register: UseFormRegister<BrainConfig>;

View File

@ -1,8 +1,10 @@
/* eslint-disable */
"use client";
import Link from "next/link";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import Link from "next/link";
export const UserAccountSection = (): JSX.Element => {
const { session } = useSupabase();

View File

@ -1,8 +1,9 @@
import { useToast } from "@/lib/hooks/useToast";
/* eslint-disable */
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useBrainConfig } from "@/lib/context/BrainConfigProvider/hooks/useBrainConfig";
import { useEffect } from "react";
import { useToast } from "@/lib/hooks/useToast";
export const useConfig = () => {
const { config, updateConfig, resetConfig } = useBrainConfig();

View File

@ -1,3 +1,4 @@
/* eslint-disable */
"use client";
import { redirect } from "next/navigation";
@ -6,7 +7,7 @@ import { ConfigForm, ConfigTitle } from "./components";
import { ApiKeyConfig } from "./components/ApiKeyConfig";
// TODO: Use states instead of NEXTJS router to open and close modal
export default function ConfigPage() {
const ConfigPage = (): JSX.Element => {
const { session } = useSupabase();
if (session === null) {
@ -22,4 +23,6 @@ export default function ConfigPage() {
</section>
</main>
);
}
};
export default ConfigPage;

View File

@ -1,5 +1,8 @@
import { useAxios } from "@/lib/useAxios";
/* eslint-disable */
import { useEffect, useState } from "react";
import { useAxios } from "@/lib/useAxios";
import { useSupabase } from "../../supabase-provider";
interface DocumentDataProps {

View File

@ -1,19 +1,22 @@
/* eslint-disable */
"use client";
import {
Dispatch,
forwardRef,
RefObject,
SetStateAction,
useState,
} from "react";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import { AnimatedCard } from "@/lib/components/ui/Card";
import Ellipsis from "@/lib/components/ui/Ellipsis";
import Modal from "@/lib/components/ui/Modal";
import { useToast } from "@/lib/hooks/useToast";
import { Document } from "@/lib/types/Document";
import { useAxios } from "@/lib/useAxios";
import {
Dispatch,
RefObject,
SetStateAction,
forwardRef,
useState,
} from "react";
import { Document } from "../../../lib/types/Document";
import DocumentData from "./DocumentData";
interface DocumentProps {

View File

@ -1,17 +1,19 @@
/* eslint-disable */
"use client";
import Button from "@/lib/components/ui/Button";
import Spinner from "@/lib/components/ui/Spinner";
import { Document } from "@/lib/types/Document";
import { useAxios } from "@/lib/useAxios";
import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { redirect } from "next/navigation";
import { useEffect, useState } from "react";
import Button from "@/lib/components/ui/Button";
import Spinner from "@/lib/components/ui/Spinner";
import { Document } from "@/lib/types/Document";
import { useAxios } from "@/lib/useAxios";
import { useSupabase } from "../supabase-provider";
import DocumentItem from "./DocumentItem";
export default function ExplorePage() {
const ExplorePage = (): JSX.Element => {
const [documents, setDocuments] = useState<Document[]>([]);
const [isPending, setIsPending] = useState(true);
const { session } = useSupabase();
@ -79,4 +81,6 @@ export default function ExplorePage() {
</section>
</main>
);
}
};
export default ExplorePage;

View File

@ -1,13 +1,13 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
main {
@apply max-w-screen-xl mx-auto flex flex-col;
}
header, section {
header,
section {
@apply px-5 md:px-10;
}
@ -43,4 +43,4 @@ a {
max-height: 60vh; /* Adjust this value based on the desired maximum height */
overflow-y: auto; /* Enable vertical scroll if the content exceeds the maximum height */
padding-right: 1rem; /* Optional: Add some padding if the scrollbar appears, so the text is not squished */
}
}

View File

@ -1,11 +1,13 @@
import Footer from "@/lib/components/Footer";
import { NavBar } from "@/lib/components/NavBar";
import { ToastProvider } from "@/lib/components/ui/Toast";
import { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { Analytics } from "@vercel/analytics/react";
import { Inter } from "next/font/google";
import { cookies, headers } from "next/headers";
import { BrainConfigProvider } from "../lib/context/BrainConfigProvider/brain-config-provider";
import Footer from "@/lib/components/Footer";
import { NavBar } from "@/lib/components/NavBar";
import { ToastProvider } from "@/lib/components/ui/Toast";
import { BrainConfigProvider } from "@/lib/context/BrainConfigProvider/brain-config-provider";
import "./globals.css";
import SupabaseProvider from "./supabase-provider";
@ -17,11 +19,11 @@ export const metadata = {
"Quivr is your second brain in the cloud, designed to easily store and retrieve unstructured information.",
};
export default async function RootLayout({
const RootLayout = async ({
children,
}: {
children: React.ReactNode;
}) {
}): Promise<JSX.Element> => {
const supabase = createServerComponentSupabaseClient({
headers,
cookies,
@ -49,4 +51,6 @@ export default async function RootLayout({
</body>
</html>
);
}
};
export default RootLayout;

View File

@ -1,56 +1,59 @@
'use client'
"use client";
import { createContext, useContext, useEffect, useState } from 'react'
import { Session, createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { useRouter } from 'next/navigation'
import type { SupabaseClient } from "@supabase/auth-helpers-nextjs";
import {
createBrowserSupabaseClient,
Session,
} from "@supabase/auth-helpers-nextjs";
import { useRouter } from "next/navigation";
import { createContext, useContext, useEffect, useState } from "react";
import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
type MaybeSession = Session | null
type MaybeSession = Session | null;
type SupabaseContext = {
supabase: SupabaseClient
session: MaybeSession
}
supabase: SupabaseClient;
session: MaybeSession;
};
const Context = createContext<SupabaseContext | undefined>(undefined)
const Context = createContext<SupabaseContext | undefined>(undefined);
export default function SupabaseProvider({
const SupabaseProvider = ({
children,
session,
}: {
children: React.ReactNode
session: MaybeSession
}) {
const [supabase] = useState(() => createBrowserSupabaseClient())
const router = useRouter()
children: React.ReactNode;
session: MaybeSession;
}): JSX.Element => {
const [supabase] = useState(() => createBrowserSupabaseClient());
const router = useRouter();
useEffect(() => {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange(() => {
router.refresh()
})
router.refresh();
});
return () => {
subscription.unsubscribe()
}
}, [router, supabase])
subscription.unsubscribe();
};
}, [router, supabase]);
return (
<Context.Provider value={{ supabase, session }}>
<>{children}</>
</Context.Provider>
)
}
);
};
export const useSupabase = () => {
const context = useContext(Context)
export const useSupabase = (): SupabaseContext => {
const context = useContext(Context);
if (context === undefined) {
throw new Error('useSupabase must be used inside SupabaseProvider')
throw new Error("useSupabase must be used inside SupabaseProvider");
}
return context
}
return context;
};
export default SupabaseProvider;

View File

@ -1,6 +1,7 @@
export const isValidUrl = (string: string) => {
export const isValidUrl = (string: string): boolean => {
try {
new URL(string);
return true;
} catch (_) {
return false;

View File

@ -1,8 +1,11 @@
/* eslint-disable */
import { redirect } from "next/navigation";
import { useCallback, useRef, useState } from "react";
import { useSupabase } from "@/app/supabase-provider";
import { useToast } from "@/lib/hooks/useToast";
import { useAxios } from "@/lib/useAxios";
import { redirect } from "next/navigation";
import { useCallback, useRef, useState } from "react";
import { isValidUrl } from "../helpers/isValidUrl";
export const useCrawler = () => {
@ -26,6 +29,7 @@ export const useCrawler = () => {
variant: "danger",
text: "Invalid URL",
});
return;
}

View File

@ -2,10 +2,12 @@
import Button from "@/lib/components/ui/Button";
import Card from "@/lib/components/ui/Card";
import Field from "@/lib/components/ui/Field";
import { useCrawler } from "./hooks/useCrawler";
export const Crawler = (): JSX.Element => {
const { urlInputRef, isCrawling, crawlWebsite } = useCrawler();
return (
<div className="w-full">
<div className="flex justify-center gap-5 px-6">

View File

@ -1,5 +1,6 @@
/* eslint-disable */
import { motion } from "framer-motion";
import { Dispatch, RefObject, SetStateAction, forwardRef } from "react";
import { Dispatch, forwardRef, RefObject, SetStateAction } from "react";
import { MdClose } from "react-icons/md";
interface FileComponentProps {

View File

@ -1,10 +1,12 @@
import { useSupabase } from "@/app/supabase-provider";
import { useToast } from "@/lib/hooks/useToast";
import { useAxios } from "@/lib/useAxios";
/* eslint-disable */
import { redirect } from "next/navigation";
import { useCallback, useState } from "react";
import { FileRejection, useDropzone } from "react-dropzone";
import { useSupabase } from "@/app/supabase-provider";
import { useToast } from "@/lib/hooks/useToast";
import { useAxios } from "@/lib/useAxios";
export const useFileUploader = () => {
const [isPending, setIsPending] = useState(false);
const { publish } = useToast();
@ -44,6 +46,7 @@ export const useFileUploader = () => {
const onDrop = (acceptedFiles: File[], fileRejections: FileRejection[]) => {
if (fileRejections.length > 0) {
publish({ variant: "danger", text: "File too big." });
return;
}
@ -69,6 +72,7 @@ export const useFileUploader = () => {
text: "Please, add files to upload",
variant: "warning",
});
return;
}
setIsPending(true);

View File

@ -1,7 +1,10 @@
/* eslint-disable */
"use client";
import { AnimatePresence } from "framer-motion";
import Button from "@/lib/components/ui/Button";
import Card from "@/lib/components/ui/Card";
import { AnimatePresence } from "framer-motion";
import FileComponent from "./components/FileComponent";
import { useFileUploader } from "./hooks/useFileUploader";

View File

@ -1,12 +1,14 @@
"use client";
import Link from "next/link";
import Button from "@/lib/components/ui/Button";
import { Divider } from "@/lib/components/ui/Divider";
import PageHeading from "@/lib/components/ui/PageHeading";
import Link from "next/link";
import { Crawler } from "./components/Crawler";
import { FileUploader } from "./components/FileUploader";
export default function UploadPage() {
const UploadPage = (): JSX.Element => {
return (
<main className="pt-10">
<PageHeading
@ -25,4 +27,6 @@ export default function UploadPage() {
</div>
</main>
);
}
};
export default UploadPage;

View File

@ -1,4 +1,5 @@
import { GiBrain } from "react-icons/gi";
import { UserStats } from "../../../lib/types/User";
export const BrainConsumption = (userStats: UserStats): JSX.Element => {
@ -27,6 +28,7 @@ export const BrainConsumption = (userStats: UserStats): JSX.Element => {
className="fill-pink-300 stroke-black stoke-1"
/>
);
return (
<div className="flex flex-col items-center justify-center w-fit">
<div className="w-24 h-24 relative">

View File

@ -1,4 +1,5 @@
import { HTMLAttributes } from "react";
import { UserStats } from "../../../lib/types/User";
interface DateComponentProps extends HTMLAttributes<HTMLSpanElement> {

View File

@ -1,5 +1,4 @@
"use client";
import { FC } from "react";
import {
VictoryContainer,
VictoryPie,
@ -12,26 +11,29 @@ interface BrainSpaceChartProps extends VictoryPieProps {
max_brain_size: number;
}
const BrainSpaceChart: FC<BrainSpaceChartProps> = ({
const BrainSpaceChart = ({
current_brain_size,
max_brain_size,
...props
}) => {
}: BrainSpaceChartProps): JSX.Element => {
return (
<VictoryPie
data={[
{ x: "Used", y: current_brain_size },
{ x: "Unused", y: max_brain_size - current_brain_size },
]}
containerComponent={
<VictoryContainer
className="bg-white rounded-md w-full h-full"
responsive={true}
/>
}
{...props}
theme={VictoryTheme.material}
/>
<>
{/* @ts-expect-error Server Component */}
<VictoryPie
data={[
{ x: "Used", y: current_brain_size },
{ x: "Unused", y: max_brain_size - current_brain_size },
]}
containerComponent={
<VictoryContainer
className="bg-white rounded-md w-full h-full"
responsive={true}
/>
}
{...props}
theme={VictoryTheme.material}
/>
</>
);
};

View File

@ -1,6 +1,6 @@
/* eslint-disable */
"use client";
import { format, subDays } from "date-fns";
import React from "react";
import {
VictoryAxis,
VictoryChart,
@ -20,14 +20,15 @@ interface RequestsPerDayChartProps extends VictoryChartProps {
requests_stats: RequestStat[];
}
export const RequestsPerDayChart: React.FC<RequestsPerDayChartProps> = ({
export const RequestsPerDayChart = ({
requests_stats,
...props
}) => {
}: RequestsPerDayChartProps): JSX.Element => {
const data = Array.from({ length: 7 }, (_, i) => subDays(new Date(), i))
.map((date) => {
const dateString = format(date, "yyyyMMdd");
const stat = requests_stats.find((s) => s.date === dateString);
return {
date: format(date, "MM/dd/yyyy"),
requests_count: stat ? stat.requests_count : 0,
@ -50,12 +51,15 @@ export const RequestsPerDayChart: React.FC<RequestsPerDayChartProps> = ({
}}
{...props}
>
{/* @ts-expect-error Server Component */}
<VictoryAxis
tickFormat={(tick) => {
return `${tick.split("/")[0]}/${tick.split("/")[1]}`;
}}
/>
{/* @ts-expect-error Server Component */}
<VictoryAxis dependentAxis />
{/* @ts-expect-error Server Component */}
<VictoryLine data={data} x="date" y="requests_count" />
</VictoryChart>
);

View File

@ -1,10 +1,13 @@
/* eslint-disable */
"use client";
import Button from "@/lib/components/ui/Button";
import { cn } from "@/lib/utils";
import Link from "next/link";
import prettyBytes from "pretty-bytes";
import { HTMLAttributes } from "react";
import { UserStats } from "../../../lib/types/User";
import Button from "@/lib/components/ui/Button";
import { UserStats } from "@/lib/types/User";
import { cn } from "@/lib/utils";
import { BrainConsumption } from "./BrainConsumption";
import { DateComponent } from "./Date";
import BrainSpaceChart from "./Graphs/BrainSpaceChart";
@ -13,6 +16,7 @@ import { RequestsPerDayChart } from "./Graphs/RequestsPerDayChart";
export const UserStatistics = (userStats: UserStats): JSX.Element => {
const { email, current_brain_size, max_brain_size, date, requests_stats } =
userStats;
return (
<>
<div className="flex flex-col sm:flex-row sm:items-center py-10 gap-5">

View File

@ -1 +1 @@
export { UserStatistics } from "./UserStatistics";
export { UserStatistics } from "./UserStatistics";

View File

@ -1,13 +1,16 @@
/* eslint-disable */
"use client";
import Spinner from "@/lib/components/ui/Spinner";
import { useAxios } from "@/lib/useAxios";
import { redirect } from "next/navigation";
import { useEffect, useState } from "react";
import { UserStats } from "../../lib/types/User";
import Spinner from "@/lib/components/ui/Spinner";
import { UserStats } from "@/lib/types/User";
import { useAxios } from "@/lib/useAxios";
import { useSupabase } from "../supabase-provider";
import { UserStatistics } from "./components/UserStatistics";
export default function UserPage() {
const UserPage = (): JSX.Element => {
const [userStats, setUserStats] = useState<UserStats>();
const { session } = useSupabase();
const { axiosInstance } = useAxios();
@ -57,4 +60,5 @@ export default function UserPage() {
</section>
</main>
);
}
};
export default UserPage;

View File

@ -1,4 +1,4 @@
const Footer = () => {
const Footer = (): JSX.Element => {
return (
<footer className="bg-white dark:bg-black border-t dark:border-white/10 mt-auto py-10">
<div className="max-w-screen-xl mx-auto flex justify-center items-center gap-4">

View File

@ -1,3 +1,4 @@
/* eslint-disable */
import { useEffect, useRef, useState } from "react";
export const useHeader = () => {
@ -16,6 +17,7 @@ export const useHeader = () => {
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);

View File

@ -1,7 +1,12 @@
import { motion } from "framer-motion";
import { useHeader } from "./hooks/useHeader";
export const Header = ({ children }: { children: React.ReactNode }) => {
export const Header = ({
children,
}: {
children: React.ReactNode;
}): JSX.Element => {
const { hidden } = useHeader();
return (

View File

@ -1,7 +1,7 @@
import Image from "next/image";
import Link from "next/link";
export const Logo = () => {
export const Logo = (): JSX.Element => {
return (
<Link href={"/"} className="flex items-center gap-4">
<Image

View File

@ -1,11 +1,13 @@
import * as Dialog from "@radix-ui/react-dialog";
import { AnimatePresence, motion } from "framer-motion";
import { FC, useState } from "react";
import { useState } from "react";
import { MdClose, MdMenu } from "react-icons/md";
import { NavItems } from "./NavItems";
export const MobileMenu: FC = () => {
export const MobileMenu = (): JSX.Element => {
const [open, setOpen] = useState(false);
return (
<Dialog.Root onOpenChange={setOpen} open={open}>
<Dialog.Trigger asChild>

View File

@ -1,16 +1,19 @@
import Button from "@/lib/components/ui/Button";
import Link from "next/link";
import { usePathname } from "next/navigation";
import Button from "@/lib/components/ui/Button";
export const AuthButtons = (): JSX.Element => {
const pathname = usePathname();
if (pathname === "/signup")
if (pathname === "/signup") {
return (
<Link href={"/login"}>
<Button variant={"secondary"}>Login</Button>
</Link>
);
}
return (
<Link href={"/signup"}>
<Button variant={"secondary"}>Register</Button>

View File

@ -1,9 +1,11 @@
/* eslint-disable */
"use client";
import Button from "@/lib/components/ui/Button";
import { FC, useEffect, useLayoutEffect, useState } from "react";
import { useEffect, useLayoutEffect, useState } from "react";
import { MdDarkMode, MdLightMode } from "react-icons/md";
export const DarkModeToggle: FC = () => {
import Button from "@/lib/components/ui/Button";
export const DarkModeToggle = (): JSX.Element => {
const [dark, setDark] = useState(false);
useLayoutEffect(() => {

View File

@ -1,5 +1,6 @@
/* eslint-disable */
import Link from "next/link";
import { Dispatch, FC, ReactNode, SetStateAction } from "react";
import { Dispatch, ReactNode, SetStateAction } from "react";
interface NavLinkProps {
children: ReactNode;
@ -7,7 +8,11 @@ interface NavLinkProps {
setOpen?: Dispatch<SetStateAction<boolean>>;
}
export const NavLink: FC<NavLinkProps> = ({ children, to, setOpen }) => {
export const NavLink = ({
children,
to,
setOpen,
}: NavLinkProps): JSX.Element => {
return (
<li className="group relative">
<Link onClick={() => setOpen && setOpen(false)} href={to}>

View File

@ -1,10 +1,13 @@
/* eslint-disable */
"use client";
import Link from "next/link";
import { Dispatch, HTMLAttributes, SetStateAction } from "react";
import { MdPerson, MdSettings } from "react-icons/md";
import { useSupabase } from "@/app/supabase-provider";
import Button from "@/lib/components/ui/Button";
import { cn } from "@/lib/utils";
import Link from "next/link";
import { Dispatch, FC, HTMLAttributes, SetStateAction } from "react";
import { MdPerson, MdSettings } from "react-icons/md";
import { AuthButtons } from "./components/AuthButtons";
import { DarkModeToggle } from "./components/DarkModeToggle";
import { NavLink } from "./components/NavLink";
@ -13,11 +16,11 @@ interface NavItemsProps extends HTMLAttributes<HTMLUListElement> {
setOpen?: Dispatch<SetStateAction<boolean>>;
}
export const NavItems: FC<NavItemsProps> = ({
export const NavItems = ({
className,
setOpen,
...props
}) => {
}: NavItemsProps): JSX.Element => {
const { session } = useSupabase();
const isUserLoggedIn = session?.user !== undefined;

View File

@ -1,12 +1,11 @@
"use client";
import { FC } from "react";
import { Header } from "./components/Header";
import { Logo } from "./components/Logo";
import { MobileMenu } from "./components/MobileMenu";
import { NavItems } from "./components/NavItems";
export const NavBar: FC = () => {
export const NavBar = (): JSX.Element => {
return (
<Header>
<Logo />

View File

@ -1,8 +1,10 @@
import { cn } from "@/lib/utils";
/* eslint-disable */
import { cva, type VariantProps } from "class-variance-authority";
import { ButtonHTMLAttributes, FC, LegacyRef, forwardRef } from "react";
import { ButtonHTMLAttributes, forwardRef, LegacyRef } from "react";
import { FaSpinner } from "react-icons/fa";
import { cn } from "@/lib/utils";
const ButtonVariants = cva(
"px-8 py-3 text-sm disabled:opacity-80 text-center font-medium rounded-md focus:ring ring-primary/10 outline-none flex items-center justify-center gap-2 transition-opacity",
{
@ -34,11 +36,18 @@ export interface ButtonProps
isLoading?: boolean;
}
const Button: FC<ButtonProps> = forwardRef(
const Button = forwardRef(
(
{ className, children, variant, brightness, isLoading, ...props },
{
className,
children,
variant,
brightness,
isLoading,
...props
}: ButtonProps,
forwardedRef
) => {
): JSX.Element => {
return (
<button
className={cn(ButtonVariants({ variant, brightness, className }))}

View File

@ -1,12 +1,14 @@
/* eslint-disable */
"use client";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { FC, HTMLAttributes, LegacyRef, forwardRef } from "react";
import { forwardRef, HTMLAttributes, LegacyRef } from "react";
import { cn } from "@/lib/utils";
type CardProps = HTMLAttributes<HTMLDivElement>;
const Card: FC<CardProps> = forwardRef(
({ children, className, ...props }, ref) => {
const Card = forwardRef(
({ children, className, ...props }: CardProps, ref): JSX.Element => {
return (
<div
ref={ref as LegacyRef<HTMLDivElement>}

View File

@ -1,12 +1,14 @@
/* eslint-disable */
import { forwardRef, HTMLAttributes, LegacyRef } from "react";
import { cn } from "@/lib/utils";
import { FC, HTMLAttributes, LegacyRef, forwardRef } from "react";
type DividerProps = HTMLAttributes<HTMLDivElement> & {
text?: string;
};
const Divider: FC<DividerProps> = forwardRef(
({ className, text, ...props }, ref) => {
const Divider = forwardRef(
({ className, text, ...props }: DividerProps, ref): JSX.Element => {
return (
<div
ref={ref as LegacyRef<HTMLDivElement>}

View File

@ -1,6 +1,9 @@
/* eslint-disable */
"use client";
import { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";
import { FC, HTMLAttributes } from "react";
import Tooltip from "./Tooltip";
interface EllipsisProps extends HTMLAttributes<HTMLDivElement> {
@ -9,12 +12,12 @@ interface EllipsisProps extends HTMLAttributes<HTMLDivElement> {
tooltip?: boolean;
}
const Ellipsis: FC<EllipsisProps> = ({
const Ellipsis = ({
children: originalContent,
className,
maxCharacters,
tooltip = false,
}) => {
}: EllipsisProps): JSX.Element => {
const renderedContent =
originalContent.length > maxCharacters
? `${originalContent.slice(0, maxCharacters)}...`

View File

@ -1,11 +1,13 @@
import { cn } from "@/lib/utils";
/* eslint-disable */
import {
DetailedHTMLProps,
forwardRef,
InputHTMLAttributes,
RefObject,
forwardRef,
} from "react";
import { cn } from "@/lib/utils";
interface FieldProps
extends DetailedHTMLProps<
InputHTMLAttributes<HTMLInputElement>,

View File

@ -1,8 +1,9 @@
"use client";
import * as Dialog from "@radix-ui/react-dialog";
import { AnimatePresence, motion } from "framer-motion";
import { FC, ReactNode, useState } from "react";
import { ReactNode, useState } from "react";
import { MdClose } from "react-icons/md";
import Button from "./Button";
interface ModalProps {
@ -13,14 +14,15 @@ interface ModalProps {
CloseTrigger?: ReactNode;
}
const Modal: FC<ModalProps> = ({
const Modal = ({
title,
desc,
children,
Trigger,
CloseTrigger,
}) => {
}: ModalProps): JSX.Element => {
const [open, setOpen] = useState(false);
return (
<Dialog.Root onOpenChange={setOpen}>
<Dialog.Trigger asChild>
@ -58,7 +60,7 @@ const Modal: FC<ModalProps> = ({
{children}
<Dialog.Close asChild>
{CloseTrigger ? (
{CloseTrigger !== undefined ? (
CloseTrigger
) : (
<Button variant={"secondary"} className="self-end">

View File

@ -1,15 +1,15 @@
import { FC } from "react";
interface PageHeadingProps {
title: string;
subtitle?: string;
}
const PageHeading: FC<PageHeadingProps> = ({ title, subtitle }) => {
const PageHeading = ({ title, subtitle }: PageHeadingProps): JSX.Element => {
return (
<div className="flex flex-col items-center justify-center px-5">
<h1 className="text-3xl font-bold text-center">{title}</h1>
{subtitle && <h2 className="opacity-50 text-center">{subtitle}</h2>}
{subtitle !== undefined && (
<h2 className="opacity-50 text-center">{subtitle}</h2>
)}
</div>
);
};

View File

@ -1,7 +1,6 @@
import { FC } from "react";
import { FaSpinner } from "react-icons/fa";
const Spinner: FC = () => {
const Spinner = (): JSX.Element => {
return <FaSpinner className="animate-spin m-5" />;
};

View File

@ -1,8 +1,11 @@
/* eslint-disable */
"use client";
import { cn } from "@/lib/utils";
import * as ToastPrimitive from "@radix-ui/react-toast";
import { AnimatePresence, motion } from "framer-motion";
import { ReactNode } from "react";
import { cn } from "@/lib/utils";
import Button from "../../Button";
import { ToastContext } from "../domain/ToastContext";
import { ToastVariants } from "../domain/types";
@ -11,15 +14,21 @@ import { useToastBuilder } from "../hooks/useToastBuilder";
export const Toast = ({
children,
...toastProviderProps
}: { children?: ReactNode } & ToastPrimitive.ToastProviderProps) => {
}: {
children?: ReactNode;
} & ToastPrimitive.ToastProviderProps): JSX.Element => {
const { publish, toasts, toggleToast } = useToastBuilder();
return (
<ToastPrimitive.Provider {...toastProviderProps}>
<ToastContext.Provider value={{ publish }}>
{children}
<AnimatePresence mode="popLayout">
{toasts.map((toast) => {
if (!toast.open) return;
if (toast.open !== true) {
return;
}
return (
<ToastPrimitive.Root
open={toast.open}

View File

@ -1,11 +1,14 @@
"use client";
import * as ToastPrimitive from "@radix-ui/react-toast";
import { ReactNode } from "react";
import { Toast } from "./Toast";
export const ToastProvider = ({
children,
...toastProviderProps
}: { children?: ReactNode } & ToastPrimitive.ToastProviderProps) => {
}: {
children?: ReactNode;
} & ToastPrimitive.ToastProviderProps): JSX.Element => {
return <Toast {...toastProviderProps}>{children}</Toast>;
};

View File

@ -1,4 +1,5 @@
import { createContext } from "react";
import { ToastPublisher } from "./types";
const publish: ToastPublisher = () => void 0;

View File

@ -1,4 +1,4 @@
import { VariantProps, cva } from "class-variance-authority";
import { cva, VariantProps } from "class-variance-authority";
export const ToastVariants = cva(
"bg-white dark:bg-black px-8 max-w-sm w-full py-5 border border-black/10 dark:border-white/25 rounded-xl shadow-xl flex items-center pointer-events-auto data-[swipe=end]:opacity-0 data-[state=closed]:opacity-0 transition-opacity",

View File

@ -1,5 +1,6 @@
export const generateToastUniqueId = () => {
export const generateToastUniqueId = (): string => {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 10000);
return `${timestamp}-${random}`;
};

View File

@ -1,4 +1,6 @@
/* eslint-disable */
import { useState } from "react";
import { ToastContent, ToastData, ToastPublisher } from "../domain/types";
import { generateToastUniqueId } from "../helpers/generateToastUniqueId";
@ -12,6 +14,7 @@ export const useToastBuilder = () => {
if (toast.id === toastId) {
toast.open = value;
}
return toast;
})
);

View File

@ -1,15 +1,16 @@
"use client";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { AnimatePresence, motion } from "framer-motion";
import { FC, ReactNode, useState } from "react";
import { ReactNode, useState } from "react";
interface TooltipProps {
children?: ReactNode;
tooltip?: ReactNode;
}
const Tooltip: FC<TooltipProps> = ({ children, tooltip }) => {
const Tooltip = ({ children, tooltip }: TooltipProps): JSX.Element => {
const [open, setOpen] = useState(false);
return (
<TooltipPrimitive.Provider>
<TooltipPrimitive.Root onOpenChange={setOpen} open={open}>

View File

@ -1,7 +1,10 @@
/* eslint-disable */
"use client";
import { setEmptyStringsUndefined } from "@/lib/helpers/setEmptyStringsUndefined";
import { createContext, useEffect, useState } from "react";
import { setEmptyStringsUndefined } from "@/lib/helpers/setEmptyStringsUndefined";
import {
getBrainConfigFromLocalStorage,
saveBrainConfigInLocalStorage,

View File

@ -1,3 +1,4 @@
/* eslint-disable */
import { BrainConfig } from "../types";
const BRAIN_CONFIG_LOCAL_STORAGE_KEY = "userBrainConfig";
@ -12,6 +13,9 @@ export const getBrainConfigFromLocalStorage = (): BrainConfig | undefined => {
const persistedBrainConfig = localStorage.getItem(
BRAIN_CONFIG_LOCAL_STORAGE_KEY
);
if (persistedBrainConfig === null) return;
if (persistedBrainConfig === null) {
return;
}
return JSON.parse(persistedBrainConfig);
};

View File

@ -1,4 +1,6 @@
/* eslint-disable */
import { useContext } from "react";
import { BrainConfigContext } from "../brain-config-provider";
export const useBrainConfig = () => {

View File

@ -20,8 +20,18 @@ export type ConfigContext = {
// export const openAiModels = ["gpt-3.5-turbo", "gpt-4"] as const; ## TODO activate GPT4 when not in demo mode
export const openAiModels = ["gpt-3.5-turbo","gpt-3.5-turbo-0613","gpt-3.5-turbo-16k"] as const;
export const openAiPaidModels = ["gpt-3.5-turbo","gpt-3.5-turbo-0613","gpt-3.5-turbo-16k","gpt-4","gpt-4-0613"] as const;
export const openAiModels = [
"gpt-3.5-turbo",
"gpt-3.5-turbo-0613",
"gpt-3.5-turbo-16k",
] as const;
export const openAiPaidModels = [
"gpt-3.5-turbo",
"gpt-3.5-turbo-0613",
"gpt-3.5-turbo-16k",
"gpt-4",
"gpt-4-0613",
] as const;
export const anthropicModels = [
// "claude-v1",
@ -39,7 +49,7 @@ export const models = [
...googleModels,
] as const;
export const paidModels = [...openAiPaidModels, ...googleModels] as const;
export const paidModels = [...openAiPaidModels] as const;
export type PaidModels = (typeof paidModels)[number];

View File

@ -1,6 +1,8 @@
/* eslint-disable */
"use client";
import { createContext } from "react";
import useChats from "./hooks/useChats";
import { ChatsState } from "./types";

View File

@ -1,9 +1,12 @@
import { useBrainConfig } from "@/lib/context/BrainConfigProvider/hooks/useBrainConfig";
import { useToast } from "@/lib/hooks/useToast";
import { useAxios } from "@/lib/useAxios";
/* eslint-disable */
import { UUID } from "crypto";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { useBrainConfig } from "@/lib/context/BrainConfigProvider/hooks/useBrainConfig";
import { useToast } from "@/lib/hooks/useToast";
import { useAxios } from "@/lib/useAxios";
import { Chat, ChatMessage } from "../../../types/Chat";
export default function useChats() {
@ -38,7 +41,9 @@ export default function useChats() {
const fetchChat = useCallback(
async (chatId?: UUID) => {
if (!chatId) return;
if (!chatId) {
return;
}
try {
console.log(`Fetching chat ${chatId}`);
const response = await axiosInstance.get<Chat>(`/chat/${chatId}`);
@ -64,6 +69,7 @@ export default function useChats() {
options: Record<string, string | unknown>;
}) => {
fetchAllChats();
return axiosInstance.post<ChatResponse>(`/chat`, options);
};
@ -78,7 +84,9 @@ export default function useChats() {
const sendMessage = async (chatId?: UUID, msg?: ChatMessage) => {
setIsSendingMessage(true);
if (msg) setMessage(msg);
if (msg) {
setMessage(msg);
}
const options: Record<string, unknown> = {
chat_id: chatId,
model,
@ -101,6 +109,7 @@ export default function useChats() {
});
setMessage(["", ""]);
setIsSendingMessage(false);
return;
}
@ -114,6 +123,7 @@ export default function useChats() {
console.log("---- Creating a new chat ----");
setAllChats((chats) => {
console.log({ chats });
return [...chats, newChat];
});
setChat(newChat);

View File

@ -1,4 +1,6 @@
/* eslint-disable */
import { useContext } from "react";
import { ChatsContext } from "../chats-provider";
const useChatsContext = () => {

View File

@ -1,5 +1,8 @@
import { isSpeechRecognitionSupported } from "@/lib/helpers/isSpeechRecognitionSupported";
/* eslint-disable */
import { useEffect, useState } from "react";
import { isSpeechRecognitionSupported } from "@/lib/helpers/isSpeechRecognitionSupported";
import useChatsContext from "./useChatsContext";
export const useSpeech = () => {

View File

@ -1,9 +1,10 @@
export function isSpeechRecognitionSupported() {
export const isSpeechRecognitionSupported = (): boolean => {
if (
typeof window !== "undefined" &&
("SpeechRecognition" in window || "webkitSpeechRecognition" in window)
) {
return true;
}
return false;
}
};

View File

@ -1,6 +1,8 @@
import { ToastContext } from "@/lib/components/ui/Toast/domain/ToastContext";
/* eslint-disable */
import { useContext } from "react";
import { ToastContext } from "@/lib/components/ui/Toast/domain/ToastContext";
export const useToast = () => {
const { publish } = useContext(ToastContext);

View File

@ -1,4 +1,4 @@
export type Message = {
type: "success" | "error" | "warning";
text: string;
type: "success" | "error" | "warning";
text: string;
};

View File

@ -1,20 +1,23 @@
/* eslint-disable */
import axios, { AxiosInstance } from "axios";
import { useSupabase } from "@/app/supabase-provider";
import axios from "axios";
import { useBrainConfig } from "./context/BrainConfigProvider/hooks/useBrainConfig";
const axiosInstance = axios.create({
baseURL: `${process.env.NEXT_PUBLIC_BACKEND_URL}`,
baseURL: `${process.env.NEXT_PUBLIC_BACKEND_URL ?? ""}`,
});
export const useAxios = () => {
export const useAxios = (): { axiosInstance: AxiosInstance } => {
const { session } = useSupabase();
const {
config: { backendUrl, openAiKey },
} = useBrainConfig();
axiosInstance.interceptors.request.clear();
axiosInstance.interceptors.request.use(
async (config) => {
config.headers["Authorization"] = "Bearer " + session?.access_token;
(config) => {
config.headers["Authorization"] = `Bearer ${session?.access_token}`;
config.headers["Openai-Api-Key"] = openAiKey;
config.baseURL = backendUrl ?? config.baseURL;

View File

@ -1,6 +1,6 @@
import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
export const cn = (...inputs: ClassValue[]): string => {
return twMerge(clsx(inputs));
}
};

View File

@ -1,12 +1,12 @@
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
// import type { Database } from '@/lib/database.types'
export async function middleware(req: NextRequest) {
const res = NextResponse.next()
const supabase = createMiddlewareSupabaseClient({ req, res })
await supabase.auth.getSession()
return res
}
export const middleware = async (req: NextRequest): Promise<NextResponse> => {
const res = NextResponse.next();
const supabase = createMiddlewareSupabaseClient({ req, res });
await supabase.auth.getSession();
return res;
};

View File

@ -7,9 +7,12 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint-fix": "eslint --fix .",
"test-type": "tsc --noEmit --emitDeclarationOnly false",
"test": "yarn test-type",
"precommit": "yarn lint && yarn test"
"precommit": "yarn lint && yarn test",
"format-check": "prettier --check .",
"format-fix": "prettier --write ."
},
"dependencies": {
"@emotion/react": "^11.11.0",
@ -35,9 +38,11 @@
"encoding": "^0.1.13",
"eslint": "^8.41.0",
"eslint-config-next": "13.4.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"framer-motion": "^10.12.12",
"next": "13.4.2",
"postcss": "8.4.23",
"prettier": "^2.8.8",
"pretty-bytes": "^6.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",

View File

@ -1,3 +1,5 @@
export default function About() {
return <div>About</div>;
}
const About = (): JSX.Element => {
return <div>About</div>;
};
export default About;

View File

@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {},
autoprefixer: {},
},
}
};

View File

@ -2,25 +2,23 @@
module.exports = {
darkMode: "class",
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./lib/**/*.{js,ts,jsx,tsx,mdx}',
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./lib/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
colors: {
black: "#00121F",
primary: "#4F46E5",
}
},
},
},
plugins: [
require('@tailwindcss/typography'),
],
}
plugins: [require("@tailwindcss/typography")],
};

View File

@ -0,0 +1,42 @@
{
"compilerOptions": {
"typeRoots": ["node_modules/@types"],
"composite": true,
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": true,
"incremental": true,
"noEmitOnError": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"strictNullChecks": true,
"target": "esnext",
"types": ["node", "vitest/globals"],
"lib": ["dom", "dom.iterable","es2020"],
"noUnusedLocals": true,
"noUnusedParameters": true,
"sourceMap": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"plugins": [
{
"name": "next"
}
],
},
"ts-node": {
"compilerOptions": {
"module": "NodeNext"
},
"require": ["tsconfig-paths/register"]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@ -1,9 +1,10 @@
{
"compilerOptions": {
"typeRoots": ["node_modules/@types"],
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@ -21,10 +22,18 @@
"name": "next"
}
],
"strictNullChecks": true,
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

File diff suppressed because it is too large Load Diff